How to write spec files

The generator is mostly* compliant with the AsyncAPI specification

*Limitations

  • Only json payloads are currently supported for automatic deserialization
  • Only one server is currently supported and only nats protocol is supported
  • Generated microservice doesn't support authentication with NATS-broker out of the box
  • Only one message is currently supported per channel, payloads can be choosen freely including anyOf/oneOf/allOf
  • The generated rust types are required by default, if you want to use optional types, please modify the generated types after generation or use oneOf/anyOf/allOf to represent optional types
  • references in the specification are only suppported inside the same file, external references are not supported

Crustagen specific features

  • A channel supposed to be a stream can be declared in the specification file with the x-streamname field.
channels:
  {channel-name}:
    {channel-operation}:
      bindings:
        nats:
          x-streamname: testStream

Writing your own templates

  • Any templates that in the templates folder at compilation time will be embedded in the compiled binary.
  • If you only have the binary you can put templates in the folder user-templates. If a file from the user-templates folder has the same path as an embedded template, only the template from user-template will be rendered.
    • set the command line argument --user-templates or -u to set a custom folder
  • the last file extension will be removed e.g: file.rs.go will be rendered to file.rs.
  • For examples refer to the allready included templates

What fields are available inside templates?

Any of these fields will be accessible:

    pub struct TemplateContext<'a> {
        pub title: &'a String,
        pub description: &'a Option<String>,
        pub server: &'a Server,
        pub subscribe_channels: Vec<(&'a String, SimplifiedOperation)>,
        pub publish_channels: Vec<(&'a String, SimplifiedOperation)>,
        pub model: Model,
    }
    
    pub struct Model {
        pub message_models: Vec<RustSchemaRepresentation>,
        // pub enums: Vec<MultiStructEnum>,
    }
    
    pub struct SimplifiedOperation {
        pub unique_id: String,
        pub original_operation: Operation,
        // array, da es eine oder mehrere messages geben kann
        pub messages: Vec<SimplifiedMessage>,
        // pub multiple_messages_enum: Option<MultiStructEnum>,
    }
    
    pub struct MultiStructEnum {
        pub unique_id: String,
        pub messages: Vec<SimplifiedMessage>,
        pub struct_definition: String,
    }
    
    pub struct SimplifiedMessage {
        pub unique_id: String,
        pub original_message: Message,
        pub payload: Option<RustSchemaRepresentation>,
    }
  • for more information about the fields available from these structs please refer to: all rust structs

Render to separate files

It is possible to generate files for each specific object in your AsyncAPI documentation. For example, you can specify a filename like $$handler$$.rs.go to generate a file for each publish_channel defined in your AsyncAPI spec.

This works with file templates that include the following in their name:

  • $$handler$$
  • $$producer$$
  • $$model$$
  • $$schemas$$

Functions available inside the templates

  • to_lower(input: String) -> String converts String to lowercase
  • key_exists(input: String) -> String checks if key exists
  • camel_to_snake_case(input :String) -> String converts a String in camelCase to snake_case
  • replace(input: String, from: String, to: String) -> String replaces from with to for input
    • Side Note: these functions are defined in src/generator/template_functions.rs feel free to extend then, if you have access to the source code.

Environment Variables

An .env-file is automatically generated from the .env template If you want to extend the .env file feel free to do so in the generated code

  • or if you want to customize the generated .enf file before it is generated take a look at writing your own templates The generated microservice uses the following environment variables (with their respective default values):
SERVICE_PORT = "8080"
SERVER_URL = "{{ .server.url }}"
LOG_LEVEL = "DEBUG"     # available levels are ERROR, WARN, INFO, DEBUG and TRACE
OPA_RULES= "path/to/admin/policy"
TRACING_ENABLED = false
SCHEMA_VALIDATION_ENABLED = true

Also per channel the subject will be set via an environment variable:

{channel_name}_SUBJECT = "{subject}"    # for normal pub_sub channels
{channel_name}_QUEUE = "{subject}"      # for nats queue channels
{channel_name}_STREAM = "{subject}"     # for nats jetstream channels

And for OPA

OPA_ENABLED = false                 # choose if OPA should be enabled
#OPA_REMOTE_URL = "localhost:4042"  # pick the url for an opa server
#OPA_LOCAL_WASM_PATH = "some/path"  # pick the path of a to wasm compiled rego file 

Working with Open Policy Agent

The microservice can utilize the function:

pub async fn opa_eval<I>(input: Serialize) -> Result<serde_json::Value>

from src/policy/policy.rs which sends the input to an opa_server or uses it as input to evaluate a to .wasm compiled .rego file dependant on the set enviornment variables.