ServiceStack's .NET Core Utility Belt

Our x and app dotnet tools are a versatile invaluable companion for all ServiceStack developers where it's jam packed with functionality to power a number of exciting scenarios where it serves as a Sharp App delivery platform where they can be run as a .NET Core Windows Desktop App with app or as a cross-platform Web App launcher using web and we've already how it's now a #Script runner with x run and into a Live #Script playground with x watch.

These tools contains all the functionality ServiceStack Developers or API consumers need that can be used Create ServiceStack projects, run Gist Desktop Apps or generate typed endpoints for consuming ServiceStack Services by either Add/Update ServiceStack References or by generating gRPC client proxies.

Install

To access available features, install with:

dotnet tool install --global x

Update

Or if you had a previous version installed, update with:

dotnet tool update -g x

INFO

Both x and app have equivalent base functionality, whilst app has superset Windows-only Desktop features

INFO

To update and download Add ServiceStack Reference dtos without .NET see npx get-dtos

Usage

Then run x without any arguments to view Usage:

x

Usage:

  x new                     List available Project Templates
  x new <template> <name>   Create New Project From Template
  x download <user>/<repo>  Download latest GitHub Repo Release
  x get <url>               Download URL to file                     (-out <file|dir>)
  x stream <url>            Stream URL contents to console stdout

  x mix                     Show available gists to mixin            (Alias '+')
  x mix <name>              Write gist files locally, e.g:           (Alias +init)
  x mix init                Create empty .NET Core ServiceStack App
  x mix [tag]               Search available gists
  x mix <gist-url>          Write all Gist text files to current directory
  x gist <gist-id>          Write all Gist text files to current directory

  x publish                 Publish Current Directory to Gist        (requires token)
  x gist-new <dir>          Create new Gist with Directory Files     (requires token)
  x gist-update <id> <dir>  Update Gist ID with Directory Files      (requires token)
  x gist-open <gist>        Download and open Gist folder            (-out <dir>)

  x <lang>                  Update all ServiceStack References in directory (recursive)
  x <file>                  Update existing ServiceStack Reference (e.g. dtos.cs)
  x <lang>     <url> <file> Add ServiceStack Reference and save to file name
  x csharp     <url>        Add C# ServiceStack Reference            (Alias 'cs')
  x typescript <url>        Add TypeScript ServiceStack Reference    (Alias 'ts')
  x swift      <url>        Add Swift ServiceStack Reference         (Alias 'sw')
  x java       <url>        Add Java ServiceStack Reference          (Alias 'ja')
  x kotlin     <url>        Add Kotlin ServiceStack Reference        (Alias 'kt')
  x dart       <url>        Add Dart ServiceStack Reference          (Alias 'da')
  x fsharp     <url>        Add F# ServiceStack Reference            (Alias 'fs')
  x vbnet      <url>        Add VB.NET ServiceStack Reference        (Alias 'vb')
  x tsd        <url>        Add TypeScript Definition ServiceStack Reference

  x proto <url>             Add gRPC .proto ServiceStack Reference
  x proto <url> <name>      Add gRPC .proto and save to <name>.services.proto
  x proto                   Update all gRPC *.services.proto ServiceStack References
  x proto-langs             Display list of gRPC supported languages
  x proto-<lang> <url>      Add gRPC .proto and generate language    (-out <dir>)
  x proto-<lang> <file|dir> Update gRPC .proto and re-gen language   (-out <dir>)
  x proto-<lang>            Update all gRPC .proto's and re-gen lang (-out <dir>)

  x open                    List of available Sharp Apps
  x open <app>              Install and run Sharp App

  x run                     Run Sharp App in current directory
  x run <name>              Run Installed Sharp App
  x run path/app.settings   Run Sharp App at directory containing specified app.settings

  x install                 List available Sharp Apps to install     (Alias 'l')
  x install <app>           Install Sharp App                        (Alias 'i')

  x uninstall               List Installed Sharp Apps
  x uninstall <app>         Uninstall Sharp App

  x alias                   Show all local gist aliases (for usage in mix or app's)
  x alias <alias>           Print local alias value
  x alias <alias> <gist-id> Set local alias with Gist Id or Gist URL
  x unalias <alias>         Remove local alias

  x shortcut                Create Shortcut for Sharp App
  x shortcut <name>.dll     Create Shortcut for .NET Core App

  x scripts                 List all available package.json scripts
  x scripts <name>          Run package.json script

  x run <name>.ss           Run #Script within context of AppHost   (or <name>.html)
  x watch <name>.ss         Watch #Script within context of AppHost (or <name>.html)
                            Language File Extensions:
                              .ss - #Script source file
                              .sc - #Script `code` source file
                              .l  - #Script `lisp` source file
  x lisp                    Start Lisp REPL

  dotnet tool update -g x   Update to latest version

Options:
    -h, --help, ?             Print this message
    -v, --version             Print this version
    -d, --debug               Run in Debug mode for Development
    -r, --release             Run in Release mode for Production
    -s, --source              Change GitHub Source for App Directory
    -f, --force               Quiet mode, always approve, never prompt   (Alias 'y')
    -e, --eval                Evaluate #Script Code
        --token               Use GitHub Auth Token
        --clean               Delete downloaded caches
        --verbose             Display verbose logging
        --ignore-ssl-errors   Ignore SSL Errors

Add/Update ServiceStack References

This shows us we can Add a ServiceStack Reference with x <lang> <baseurl> which will let us create a TypeScript Reference to the new World Validation App using its ts file extension alias:

Output:

Saved to: dtos.ts

Or create a C# ServiceStack Reference with:

Output:

Saved to: dtos.cs

To update run x <lang> which will recursively update all existing ServiceStack References:

x ts

Output:

Updated: dtos.ts

Integrate with Visual Studio

You can also easily integrate this within your VS.NET dev workflows by adding it as an External Tool in the External Tools dialog box by choosing Tools > External Tools:

Title Update TypeScript &Reference
Command web.exe
Arguments ts
Initial directory $(ProjectDir)

Which will then let you update all your *dtos.ts TypeScript References in your project by clicking on Tools > Update TypeScript Reference or using the ALT+T R keyboard shortcut.

If you wanted to Update your *dtos.cs C# ServiceStack References instead, just change Arguments to cs:

Title Update C# &Reference
Command web.exe
Arguments cs
Initial directory $(ProjectDir)

Refer to the x usage output above for the arguments or aliases for all other supported languages.

Integrate with Rider

Just like with VS.NET above you can add an External Tool in JetBrains Rider by opening the Settings dialog with CTRL+ALT+S then searching for external tools under the Tools category:

Name Update TypeScript Reference
Command web.exe
Arguments ts
Working directory \(FileParentDir\)

Now you can update your *dtos.ts TypeScript References in your project by clicking on External Tools > Update TypeScript Reference in the right-click context menu:

If you're updating references frequently you can save time by assigning it a keyboard shortcut.

Create new Project Templates

See x new for available Project Templates you can create with:

x new

Mix Features into existing ASP.NET Core Apps

The x dotnet tool is a versatile utility belt packed with a number of features to simplify discovering, installing, running and deploying .NET Core Apps. You can view the full list of supported commands by running x ?, e.g. another useful command is using x mix for generating pre-set templates:

x mix                     Show available gists to mixin         (Alias '+')
x mix <name>              Write gist files locally, e.g:        (Alias +init)
x mix init                Create empty .NET Core ServiceStack App
x mix [tag]               Search available gists
x gist <gist-id>          Write all Gist text files to current directory

View available gists with:

x mix

Where you can use x mix nginx to generate a common nginx template configuration for reverse proxying .NET Core Apps, making configuring Linux deployment servers for your .NET Core Apps less tedious.

In addition to the pre-set templates, you can create your own public GitHub gist with any number of different files customized for your Environment that anyone can write to their current directory with the gist id or gist URL:

x gist <gist-id>

Patch JSON files

The x dotnet tool also includes a number of utilities for patching JSON files, e.g. you can use x patch to patch a JSON file with a JSON Patch.

The json.patch file supports the following JSON Patch operations:

Operation Notes
add Add a property or array element. For existing property: set value.
remove Remove a property or array element.
replace Same as remove followed by add at same location.
move Same as remove from source followed by add to destination using value from source.
copy Same as add to destination using value from source.
test Return success status code if value at path = provided value.

For example, we could have the following original.json file:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "SmtpConfig": {}
}

We need to fill this smtp object with settings such as username, password, host, port, and more. To automate filling these values, we can use the ServiceStack x tool to apply a json.patch. The json.patch file to accomplish this would look something like:

[
  {
    "op": "add",
    "path": "/SmtpConfig",
    "value": {
      "UserName": "AWS_ACCESS_KEY_ID",
      "Password": "AWS_SECRET_ACCESS_KEY",
      "Host": "email-smtp.us-east-1.amazonaws.com",
      "Port": 587,
      "FromName": "From Name",
      "FromEmail": "email@address.com",
      "Bcc": "bcc email address"
    }
  }
]

Once this patch is applied, our appsettings.json transforms into:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "SmtpConfig": {
    "UserName": "AWS_ACCESS_KEY_ID",
    "Password": "AWS_SECRET_ACCESS_KEY",
    "Host": "email-smtp.us-east-1.amazonaws.com",
    "Port": 587,
    "From": "email address",
    "FromName": "From Name",
    "Bcc": "bcc email address"
  }
}

You can apply this patch using the x tool's patch command:

x patch appsettings.json.patch

This expects both the appsettings.json.patch and appsettings.json files to be local. Optionally, you can specify both files if their names differ.

x patch changes.json.patch appsettings.json

Lisp REPL

Lisp's dynamism and extensibility makes it particularly well suited for explanatory programming whose access via a REPL is available x, web and app dotnet tools.

The quick demo below shows the kind of exploratory programming available where you can query the scriptMethods available, query an objects props, query the Lisp interpreter's global symbols table containing all its global state including all defined lisp functions, macros and variables:

Annotated REPL Walk through

Here's an annotated version of the demo below which explains what each of the different expressions is doing.

Just like Sharp Scripts and Sharp Apps the Lisp REPL runs within the #Script Pages ScriptContext sandbox that when run from a Sharp App folder, starts a .NET Core App Server that simulates a fully configured .NET Core App. In this case it's running in the redis Sharp App directory where it was able to access its static web assets as well as its redis-server connection configured in its app.settings.

; quick lisp test!
(+ 1 2 3)

; List of ScriptMethodInfo that the ScriptContext running this Lisp Interpreter has access to
scriptMethods

; first script method
(:0 scriptMethods)

; show public properties of ScriptMethodInfo 
(props (:0 scriptMethods))

; show 1 property per line
(joinln (props (:0 scriptMethods)))

; show both Property Type and Name
(joinln (propTypes (:0 scriptMethods)))

; view the Names of all avaialble script methods
(joinln (map .Name scriptMethods))

; view all script methods starting with 'a'
(globln "a*" (map .Name scriptMethods))

; view all script methods starting with 'env'
(globln "env*" (map .Name scriptMethods))

; print environment info about this machine seperated by spaces
(printlns envOSVersion envMachineName envFrameworkDescription envLogicalDrives)

; expand logical drives
(printlns envOSVersion envMachineName envFrameworkDescription "- drives:" (join envLogicalDrives " "))

; view all current global symbols defined in this Lisp interpreter
symbols

; view all symbols starting with 'c'
(globln "c*" symbols)

; see how many symbols are defined in this interpreter
(count symbols)

; see how many script methods there are available
(count scriptMethods)

; view the method signature for all script methods starting with 'all'
(globln "all*" (map .Signature scriptMethods))

; count all files accessible from the configured ScriptContext
(count allFiles)

; view the public properties of the first IVirtualFile
(props (:0 allFiles))

; display the VirtualPath of all available files
(joinln (map .VirtualPath allFiles))

; display the method signature for all script methods starting with 'findFiles'
(globln "findFiles*" (map .Signature scriptMethods))

; see how many .html files are available to this App
(count (findFiles "*.html"))

; see how many .js files are available to this App
(count (findFiles "*.js"))

; show the VirtualPath of all .html files
(joinln (map .VirtualPath (findFiles "*.html")))

; view the VirtualPath's of the 1st and 2nd .html files
(:0 (map .VirtualPath (findFiles "*.html")))
(:1 (map .VirtualPath (findFiles "*.html")))

; view the text file contents of the 1st and 2nd .html files
(fileTextContents (:0 (map .VirtualPath (findFiles "*.html"))))
(fileTextContents (:1 (map .VirtualPath (findFiles "*.html"))))

; display the method signatures of all script methods starting with 'redis'
(globln "redis*" (map .Signature scriptMethods))

; search for all Redis Keys starting with 'urn:' in the redis-server instances this App is configured with
(redisSearchKeys "urn:*")

; display the first redis search entry
(:0 (redisSearchKeys "urn:*"))

; display the key names of all redis keys starting with 'urn:'
(joinln (map :id (redisSearchKeys "urn:*")))

; find out the redis-server data type of the 'urn:tags' key
(redisCall "TYPE urn:tags")

; view all tags in the 'urn:tags' sorted set
(redisCall "ZRANGE urn:tags 0 -1")

; view the string contents of the 'urn:question:1' key
(redisCall "GET urn:question:1")

; parse the json contents of question 1 and display its tag names
(:Tags (parseJson (redisCall "GET urn:question:1")))

; extract the 2nd tag of question 1
(:1 (:Tags (parseJson (redisCall "GET urn:question:1"))))

; clear the Console screen
clear

; exit the Lisp REPL
quit

Enable features and access resources with app.settings

You can configure the Lisp REPL with any of the resources and features that Sharp Apps and Gist Desktop Apps have access to, by creating a plain text app.settings file with all the features and resources you want the Lisp REPL to have access to, e.g. this Pure Cloud App app.settings allows the Lisp REPL to use Database Scripts against a AWS PostgreSQL RDS server and query remote S3 Virtual Files using Virtual File System APIs:

# Note: values prefixed with '$' are resolved from Environment Variables
name AWS S3 PostgreSQL Web App
db postgres
db.connection $AWS_RDS_POSTGRES
files s3
files.config {AccessKey:$AWS_S3_ACCESS_KEY,SecretKey:$AWS_S3_SECRET_KEY,Region:us-east-1,Bucket:rockwind}

See the plugins app.settings for examples of how to load and configure ServiceStack Plugins.


Troubleshooting

x: command not found

If after installing any of the dotnet tools it fails with bash: x: command not found you'll need to add dotnet tools to your PATH which you can do in Linux Bash with:

$ echo "export PATH=\$HOME/.dotnet/tools:\$PATH" >> ~/.bashrc
$ . ~/.bashrc

SSL Connection Errors

To resolve SSL Connection errors you can try commenting out ssl_conf = ssl_sect, e.g:

$ sudo vi /etc/ssl/openssl.cnf

Comment out line in vi using a # prefix, write changes and quit:

:%s/^ssl_conf/#&/
:wq

If that doesn't resolve the issue you can try updating the local ca-certificates:

$ sudo update-ca-certificates --fresh

Or try updating the SSL_CERT Environment variables before running the tool again:

export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
export SSL_CERT_DIR=/dev/null

Finally you can try running the x tool with the --ignore-ssl-errors switch, e.g:

$ x new vue-lite VueLite --ignore-ssl-errors