Important
This repository is deprecated. It was moved to the NDC REST monorepo.
This module includes libraries and tools to convert other API schemas to Native Data Connector (NDC) schema, as well as extend the NDC spec with REST request information.
- Convert API documentation to NDC schema
- Convert JSON to YAML. It's helpful to convert JSON schema
Prerequisites
- Go 1.21+
Install
go install github.com/hasura/ndc-rest-schema
Usage: ndc-rest-schema <command>
Flags:
-h, --help Show context-sensitive help.
--log-level="info" Log level.
Commands:
convert --file=STRING
Convert API spec to NDC schema. For example:
ndc-rest-schema convert -f petstore.yaml --spec oas2 -o petstore.json
json2yaml --file=STRING
Convert JSON file to YAML. For example:
ndc-rest-schema json2yaml -f petstore.json -o petstore.yaml
version
Print the CLI version.
Convert an OpenAPI v3 file to NDC schema with the convert
command. The tool can accept either file path or URL. The output format can be in JSON or YAML, depending on the file extension:
ndc-rest-schema convert -f https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.yaml -o petstore.json --spec oas3
The --spec
flag represents the input specification:
oas3
(openapi3
): OpenAPI 3.0 and 3.1 (default)oas2
(openapi2
): OpenAPI 2.0
The output schema can extend from the NDC schema with REST information that will be used for the NDC REST connector. You can convert the pure NDC schema with --pure
flag.
You also can use a config file to convert (example). Arguments will override the config file if you use both the config file and other arguments.
ndc-rest-schema convert -c ./config.yaml
Note
The tool will consider the path of the config file as the root directory. For example, if the config path is ./foo/bar/config.yaml
, the tool will look for relative patch files from ./foo/bar
folder. Extra arguments will take the execution location as the root directory.
The NDC REST configuration adds request
information into functions
and procedures
so the connector can have more context to initiate HTTP requests to the remote REST service. The request schema is inspired by OpenAPI 3 paths and operations.
- request:
url: /pets/{petId}
method: get
type: rest
headers:
Foo: bar
timeout: 30 # seconds, default 30s
parameters:
- name: petId
in: path
schema:
type: string
nullable: false
security:
- api_key: []
The URL can be a relative path or an absolute URL. If the URL is relative, there must be a base URL in settings
:
settings:
servers:
- url: http://petstore.swagger.io/v1
parameters
include the list of URL and query parameters so the connector can replace values from request arguments.
For procedures, the body
argument is always treated as the request body. If there is a parameter that has the same name, the tool will rename it to paramBody
.
The settings
object contains global configuration about servers, authentication, and other information.
servers
: list of servers that serve the API service.url
: the base URL of the API server.id
: the unique identity for the server. The array index will be used if empty. If the server ID is present, the variable name of the server URL will be[prefix]_[server-id]_SERVER_URL
. This value can be parsed fromx-server-id
extension field (OAS 3.0).headers
,timeout
,securitySchemes
,security
: same as below but take effect to the current server only.
headers
: default headers will be injected into all requests.timeout
: default timeout for all requestssecuritySchemes
: global configurations for authentication, follow the security scheme of OpenAPI 3.security
: default authentication requirements will be applied to all requests.
Environment variable template which is in {{CONSTANT_CASE}}
or {{CONSTANT_CASE:-some_default_value}}
format can be replaced with value in the runtime. The wrapper should be double-brackets to avoid mistaking an OpenAPI variable template.
settings:
servers:
- url: "{{PET_STORE_SERVER_URL:-https://petstore3.swagger.io/api/v3}}"
timeout: 30
headers:
foo: bar
securitySchemes:
api_key:
type: apiKey
value: "{{PET_STORE_API_KEY}}"
in: header
name: api_key
petstore_auth:
type: oauth2
flows:
implicit:
authorizationUrl: https://petstore3.swagger.io/oauth/authorize
scopes:
read:pets: read your pets
write:pets: modify pets in your account
security:
- {}
- petstore_auth:
- write:pets
- read:pets
version: 1.0.18
collections: []
functions:
- request:
url: "/pet/findByStatus"
method: get
parameters:
- name: status
in: query
schema:
type: String
nullable: true
enum:
- available
- pending
- sold
security:
- petstore_auth:
- write:pets
- read:pets
arguments:
status:
description: Status values that need to be considered for filter
type:
type: nullable
underlying_type:
name: String
type: named
description: Finds Pets by status
name: findPetsByStatus
result_type:
element_type:
name: Pet
type: named
type: array
procedures:
- request:
url: "/pet"
method: post
headers:
Content-Type: application/json
security:
- petstore_auth:
- write:pets
- read:pets
arguments:
body:
description: Request body of /pet
type:
name: Pet
type: named
description: Add a new pet to the store
name: addPet
result_type:
name: Pet
type: named
The tool can parse and convert OpenAPI documentation to NDC functions and procedures via HTTP methods:
GET
->Function
POST
,PUT
,PATCH
,DELETE
->Procedure
boolean
->Boolean
string
->String
integer
->Int
number
->Float
object
-> Object typesanyOf
,additionalProperties
and others ->JSON
Because NDC schema doesn't support union types it's impossible to convert dynamic schema to a static type. The
JSON
scalar represents as a dynamic JSON field and doesn't support nested selection.
Schema type names are usually matched with the referenced name. Anonymous type names will be generated from the URL path in PascalCase.
If the operationId
field exists in API operation, it will be used for functions or procedure names. Otherwise, the operation name will be generated from the URL path with camelCase format:
{http_method}{url_path_without_slash}
# GET /users/{id} => getUsersId
# POST /users => postUsers
# DELETE /users => deleteUsers
You can also change the method alias with --method-alias=KEY=VALUE;...
flag, for example: --method-alias=post=create;put=update
.
If the URL path has a prefix such as /api/v1/users
, you can trim that prefix with --trim-prefix
flag.
If the OpenAPI definition has authentication (or security), the tool converts them to settings
object. The schema is similar to OpenAPI 3.0 authentication with extra configuration fields.
API Keys
There is an extra value
field with the environment variable template to be replaced in runtime. The name of the variable is generated from the security scheme key. For example:
{
"securitySchemes": {
"api_key": {
"type": "apiKey",
"value": "{{API_KEY}}", // the constant case of api_key
"in": "header",
"name": "api_key"
}
}
}
You can set the prefix for environment variables with
--env-prefix
flag.
Auth Token
This is the general authentication for Basic, Bearer or any token with the scheme. The output credential will be the combination of scheme
and value
.
{
"securitySchemes": {
"bearer_auth": {
"type": "http",
"scheme": "bearer",
"value": "{{BEARER_AUTH_TOKEN}}", // the constant case of bearer_auth + _TOKEN suffix
"header": "Authentication"
}
}
}
Authentication: Bearer {{BEARER_AUTH_TOKEN}}
The environment variable name of value
field is the constant case of the security scheme key with _TOKEN
suffix.
You can set the prefix for environment variables with
--env-prefix
flag.
OAuth 2.0
See OAuth 2.0 section of OpenAPI 3.
OpenID Connect Discovery
See OpenID Connect Discovery section of OpenAPI 3.
You may want to modify the API document but don't want to edit the original file. That's possible with patch files. This tool supports the following specifications:
Patches can be applied before (--patch-before
) and after (--patch-after
) the conversion. The value accepts a list of paths, separated by commas. Each path can be a file, folder, or URL.
The pre-hook is useful for applying against raw documents such as OpenAPI, and the post-hook patches are applied against the output schema.
Note
You must convert slashes in paths to ~1
or the RFC6902 JSON patch will confuse them with JSON Pointer reference tokens.
[
{
"op": "remove",
"path": "/paths/~1notifications~1{notification_id}~1history"
}
]