Skip to content

A file format specification for defining HTTP requests, response assertions, and associated data/configuration in "request files".

License

Notifications You must be signed in to change notification settings

testingrequired/reqlang

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Request Language

A file format specification for defining HTTP requests, response assertions, and configuration in "request files".

Goals

  • HTTP request and response messages
  • Easy to read, write, and diff
  • Lives in source control
  • Environments
  • Templating with variables, prompted, and secret values
  • Client/implementation agnostic

Future

  • Chaining requests
  • Response body mapping/transformation/extraction
  • Authenticated requests (e.g. OAuth2) configuration
  • Project workspaces

Request Files

Request files (*.reqlang) are multi-document files containing a request along with an optional config and expected response. They are designed to define what the request is, not how to execute it (e.g. defining what secrets are needed instead of how to fetch them). This is left to implementing clients.

Living Syntax

This is a living syntax subject to change wildly at anytime. The core concepts and goals will remain the same however.

Example

post.reqlang:

```%config
vars = ["test_value"]
secrets = ["super_secret_value"]

[prompts]
prompt_value = ""

[envs.test]
test_value = "test_value"

[envs.prod]
test_value = "prod_value"

[envs.local]
test_value = "local_value"
```

```%request
POST https://httpbin.org/post HTTP/1.1

{
  "env": "{{@env}}",
  "value": "{{:test_value}}",
  "prompted_value": "{{?prompt_value}}",
  "secret_value": "{{!super_secret_value}}"
}
```

Request

The request is the request is what's executed when the request file is ran. They are written as HTTP request messages.

```%request
GET https://example.com HTTP/1.1
```

Response

The response is optional but treated as an assertion if it is defined. When the request is executed, this response is compared to the actual response received.

```%request
GET https://example.com HTTP/1.1
```

```%response
HTTP/1.1 200 OK
```

Matching Rules

Client implementations can choose how to match the response against the expected response. Here are a list of recommended ways to match.

  • Exact match status code
  • Exact match status text
  • Exact match header value of headers present in the expected response
  • Exact match body
  • Wildcard match header value and body using the {{*}} template references.

Configuration

The configuration is optional but is used to define environment names and their variables as well as what prompts & secrets are needed. It currently uses the toml syntax.

Variables & Environments

Variables contain environmental variables that can be used in the request or response. A list of variable names is first declared.

Variables can be templated using the {{:var_name}} syntax. The environment of the request execution can be referenced using the {{@env}} syntax.

vars = ["user_id", "item_id"]

Then enviroments are declared with the appropriate values.

vars = ["user_id", "item_id"]

[envs.dev]
user_id = 12345
item_id = "abcd"

[envs.prod]
user_id = 67890
item_id = "efgh"

There is an implicitly defined default environment present but it still must be declared in the config.

vars = ["user_id"]

[envs.default]
user_id = 12345
Usage
```%config
vars = ["user_id", "item_id"]

[envs.dev]
user_id = 12345
item_id = "abcd"

[envs.prod]
user_id = 67890
item_id = "efgh"
```

```%request
GET https://{{@env}}.example.com/users/{{:user_id}}/items/{{:item_id}} HTTP/1.1
```
Goals
  • Clearly define everything the request and response will need
  • Declare environments once
  • Require variable declaration before definition
Future
  • Default value (implicitly set in the default environment)
  • Value type

Prompts

Prompts are values provided by the user at request execution time. These are "inputs" to the request file. They can be templated in the request and responses using the {{?prompt_name}} syntax.

[prompts]
tags = ""
Usage
```%config
[prompts]
tags = ""
```

```%request
GET https://example.com/posts?tags={{?tags}} HTTP/1.1
```
Future
  • Default value
  • Value type

Secrets

Secrets are protected values referenced by a name and declares what secrets will be required. How secret values are fetched is up to client implementations. They can be referenced using the {{!secret_name}} syntax.

secrets = ["api_key"]
Usage
```%config
secrets = ["api_key"]
```

```%request
GET https://example.com HTTP/1.1
x-api-key: {{!api_key}}
```
Goals
  • Secret fetching is outside the scope of the request file
Future
  • Configuring secret fetching in the workspace

Examples

See all examples for more request files.

Libraries

Rust

The reqlang crate is a library working with request files.

Tooling

build-artifacts

These act as both tooling for request file and reference implementations for clients.

CLI

The reqlang CLI validates and exports requests in to a variety of formats (http, curl, json).

reqlang
Command to work with request files

Usage: reqlang [COMMAND]

Commands:
  export  Export request to specified format
  parse   Parse a request file
  run     Run a request file
  help    Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version

Run

Execute the request from a request file.

Usage: reqlang run [OPTIONS] <path>

Arguments:
  <path>  Path to request file

Options:
  -e, --env <env>         Resolve with an environment
  -P, --prompt <prompts>  Input a prompt value
  -S, --secret <secrets>  Input a secret value
  -f, --format <format>   Format the response [default: http] [possible values: http, json]
  -t, --test              Test if the response matches the expected response, if defined
  -h, --help              Print help
Examples
reqlang run ./examples/valid/status_code.reqlang --prompt status_code=200
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
connection: keep-alive
content-length: 0
server: gunicorn/19.9.0
access-control-allow-credentials: true
access-control-allow-origin: *
Testing Responses
reqlang run examples/valid/mismatch_response.reqlang --test

See: mismatch_response.reqlang

Response did not match expected response: [
    StatusCode {
        expected: HttpStatusCode(
            200,
        ),
        actual: HttpStatusCode(
            201,
        ),
    },
    StatusText {
        expected: "OK",
        actual: "Created",
    },
]

Parse

Validate and parse request files. It returns a JSON object with info about the request file: environment names, variables, prompts, secrets, the (untemplated) request itself.

Usage: reqlang parse <path>

Arguments:
  <path>  Path to request file

Options:
  -h, --help  Print help
Examples
reqlang parse ./examples/valid/status_code.reqlang
{
  "vars": ["test_value"],
  "envs": ["prod", "test", "local"],
  "prompts": ["prompt_value"],
  "secrets": ["super_secret_value"],
  "request": {
    "verb": "POST",
    "target": "https://httpbin.org/post",
    "http_version": "1.1",
    "headers": [],
    "body": "{\n  \"env\": \"{{@env}}\",\n  \"value\": \"{{:test_value}}\",\n  \"prompted_value\": \"{{?prompt_value}}\",\n  \"secret_value\": \"{{!super_secret_value}}\"\n}\n\n"
  }
}
Filtering

Use tools like jq to extract specific information from the parsed request.

Environment Names

Let a list of environment names defined in the request file.

reqlang parse ./examples/valid/post.reqlang | jq '.envs'
["local", "test", "prod"]
Variables

Let a list of variables provided by the request file.

reqlang parse ./examples/valid/post.reqlang | jq '.vars'
["test_value"]
Prompts

Let a list of prompts required by the request file.

reqlang parse ./examples/valid/post.reqlang | jq '.prompts'
["prompt_value"]
Secrets

Let a list of secrets required by the request file.

reqlang parse ./examples/valid/post.reqlang | jq '.secrets'
["super_secret_value"]
Config Location In Request File

Get the span of the config, if defined, in the request file. Otherwise it's null.

reqlang parse ./examples/valid/post.reqlang | jq '.full.config[1]'
{
  "start": 0,
  "end": 204
}
Request Location In Request File

Get the span of the request in the request file.

reqlang parse ./examples/valid/post.reqlang | jq '.full.request[1]'
{
  "start": 208,
  "end": 388
}
Response Location In Request File

Get the span of the response, if defined, in the request file. Otherwise it's null.

reqlang parse ./examples/valid/post.reqlang | jq '.full.response[1]'
null
Ref Locations In Request File

Get the span of all the template references (variables, prompts, secrets, providers), if defined, in the request file.

reqlang parse ./examples/valid/post.reqlang | jq '.full.refs'
[
  [
    {
      "Provider": "env"
    },
    {
      "start": 208,
      "end": 388
    }
  ],
  [
    {
      "Variable": "test_value"
    },
    {
      "start": 208,
      "end": 388
    }
  ],
  [
    {
      "Prompt": "prompt_value"
    },
    {
      "start": 208,
      "end": 388
    }
  ],
  [
    {
      "Secret": "super_secret_value"
    },
    {
      "start": 208,
      "end": 388
    }
  ]
]
Validation Errors

If the request file is invalid, a list of errors will be returned instead.

reqlang parse examples/invalid/empty.reqlang
[
  {
    "range": {
      "start": {
        "line": 0,
        "character": 0
      },
      "end": {
        "line": 0,
        "character": 0
      }
    },
    "severity": 1,
    "message": "ParseError: Request file is an empty file"
  }
]

Export

Parse and template the request file then export it in different formats.

Usage: reqlang export [OPTIONS] <path>

Arguments:
  <path>  Path to request file

Options:
  -e, --env <env>         Resolve with an environment
  -P, --prompt <prompts>  Pass prompt values to resolve with
  -S, --secret <secrets>  Pass secret values to resolve with
  -f, --format <format>   Format to export [default: json] [possible values: http, curl, json]
  -h, --help              Print help
Examples
JSON
reqlang export examples/valid/status_code.reqlang --prompt status_code=200 --format json

# This is the same thing
reqlang export examples/valid/status_code.reqlang --prompt status_code=200
{
  "verb": "GET",
  "target": "https://httpbin.org/status/200",
  "http_version": "1.1",
  "headers": [],
  "body": ""
}
HTTP Request Message
reqlang export examples/valid/status_code.reqlang --prompt status_code=201 --format http
GET https://httpbin.org/status/201 HTTP/1.1
Curl command
reqlang export examples/valid/status_code.reqlang --prompt status_code=400 --format curl
curl https://httpbin.org/status/400 --http1.1 -v
Validation Errors

If the request file is invalid or there were errors templating, a list of errors will be returned instead.

reqlang export examples/invalid/empty.reqlang
[
  {
    "range": {
      "start": {
        "line": 0,
        "character": 0
      },
      "end": {
        "line": 0,
        "character": 0
      }
    },
    "severity": 1,
    "message": "ParseError: Request file is an empty file"
  }
]

CLI in Docker

The reqlang CLI can be run from a docker image.

Building

docker build -t reqlang:0.1.0 .

Running

A directory of request files can be mounted inside the container's /usr/local/src directory to make them accessible.

docker run --rm --read-only \
    -v "/$PWD/examples":/usr/local/src/examples:ro \
    reqlang:0.1.0 \
    export \
    ./examples/valid/delay.reqlang \
    -f curl \
    -P seconds=5 | bash
# HTTP/1.1 201 CREATED
# Date: Sat, 14 Dec 2024 19:20:26 GMT
# Content-Type: text/html; charset=utf-8
# Content-Length: 0
# Connection: keep-alive
# Server: gunicorn/19.9.0
# Access-Control-Allow-Origin: *
# Access-Control-Allow-Credentials: true

VS Code

The VS Code extension acts as an in-editor REST client.

VS Code Extension Screenshot

Desktop Client

The desktop client is a very simple GUI written in egui. It's mostly used for testing.

GUI Client Screenshot

Contributing

Please see CONTRIBUTING.md for details on how to contribute.