From a14bfddd5264c528b8c98d64fed860e3874e01a5 Mon Sep 17 00:00:00 2001 From: Daniel Chambers Date: Mon, 5 Feb 2024 13:16:21 +1100 Subject: [PATCH 1/2] Support v0.1.0-rc.14 of the NDC spec --- Cargo.lock | 2 +- changelog.md | 17 + package-lock.json | 4 +- package.json | 2 +- ...tion_server.ts => configuration-server.ts} | 26 +- src/connector.ts | 35 ++- src/index.ts | 20 +- src/schema/index.ts | 20 +- src/schema/schema.generated.json | 292 +++++++++++------- src/schema/schema.generated.ts | 89 ++++-- src/server.ts | 50 ++- typegen/Cargo.toml | 2 +- 12 files changed, 365 insertions(+), 194 deletions(-) rename src/{configuration_server.ts => configuration-server.ts} (83%) diff --git a/Cargo.lock b/Cargo.lock index 2ca2e9d..72d4dc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -654,7 +654,7 @@ dependencies = [ [[package]] name = "ndc-client" version = "0.1.0" -source = "git+http://github.com/hasura/ndc-spec.git?tag=v0.1.0-rc.13#1f9b2a996ad74ac4bc97a783c4d014a3fd46b08e" +source = "git+http://github.com/hasura/ndc-spec.git?tag=v0.1.0-rc.14#cd24992ea77010e1ef2dff18f7d5656fb0546f3b" dependencies = [ "async-trait", "indexmap 2.1.0", diff --git a/changelog.md b/changelog.md index fd78f81..beb67dd 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,22 @@ # Changelog +## 2.0.0 +Breaking change: support for the [v0.1.0-rc.14 of NDC Spec](https://github.com/hasura/ndc-spec/compare/v0.1.0-rc.13...v0.1.0-rc.14). + +- Function name formatting is now standard JavaScript `camelCase`. NDC types used for wire-transmission match the spec (snake_cased). +- Added [nested field selections](https://github.com/hasura/ndc-spec/pull/70) (`Field.fields`) +- Capabilities now only specifies [a single supported version](https://github.com/hasura/ndc-spec/pull/82) (`CapabilitiesResponse.version`) +- `Expression.where` [renamed](https://github.com/hasura/ndc-spec/pull/87) to `Expression.predicate` +- `PathElement.predicate` is now [optional](https://github.com/hasura/ndc-spec/pull/87) +- Added [Predicate types](https://github.com/hasura/ndc-spec/blob/main/rfcs/0002-boolean-expression-types.md) (new `predicate` Type.type) +- Added [mutation capability](https://github.com/hasura/ndc-spec/pull/80) +- Comparison operators +- [Changes to explain](https://github.com/hasura/ndc-spec/pull/85): + - `Connector.explain` renamed to `Connector.queryExplain` and endpoint moved from `/explain` to `/query/explain`. + - `Connector.mutationExplain` added with endpoint `/mutation/explain`. + - `explain` capability moved to `query.explain`. `mutation.explain` capability added. +- `ComparisonOperatorDefinition` now has `equal` and `in` as [two standard definitions](https://github.com/hasura/ndc-spec/pull/79/files) and custom operators can be defined. The equality operator is no longer required to be defined and must be explicitly defined. + ## 1.2.8 - Add new `ConnectorError` types: - `UnprocessableContent`: The request could not be handled because, while the request was well-formed, it was not semantically correct. For example, a value for a custom scalar type was provided, but with an incorrect type diff --git a/package-lock.json b/package-lock.json index 3d0af6a..a2b9e92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@hasura/ndc-sdk-typescript", - "version": "1.2.8", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@hasura/ndc-sdk-typescript", - "version": "1.2.8", + "version": "2.0.0", "license": "ISC", "dependencies": { "@json-schema-tools/meta-schema": "^1.7.0", diff --git a/package.json b/package.json index e7a0c8e..015f97c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hasura/ndc-sdk-typescript", - "version": "1.2.8", + "version": "2.0.0", "description": "", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/src/configuration_server.ts b/src/configuration-server.ts similarity index 83% rename from src/configuration_server.ts rename to src/configuration-server.ts index 8a050bb..7bbb225 100644 --- a/src/configuration_server.ts +++ b/src/configuration-server.ts @@ -17,11 +17,13 @@ const errorResponses = { 400: ErrorResponseSchema, 403: ErrorResponseSchema, 409: ErrorResponseSchema, + 422: ErrorResponseSchema, 500: ErrorResponseSchema, 501: ErrorResponseSchema, + 502: ErrorResponseSchema, }; -export async function start_configuration_server< +export async function startConfigurationServer< RawConfiguration, Configuration, State @@ -41,14 +43,14 @@ export async function start_configuration_server< } ); - const raw_configuration_schema = connector.get_raw_configuration_schema(); + const rawConfigurationSchema = connector.getRawConfigurationSchema(); server.get( "/", { schema: { response: { - 200: raw_configuration_schema, + 200: rawConfigurationSchema, ...errorResponses, }, }, @@ -56,7 +58,7 @@ export async function start_configuration_server< async function get_schema( _request: FastifyRequest ): Promise { - return connector.make_empty_configuration(); + return connector.makeEmptyConfiguration(); } ); @@ -64,9 +66,9 @@ export async function start_configuration_server< "/", { schema: { - body: raw_configuration_schema, + body: rawConfigurationSchema, response: { - 200: raw_configuration_schema, + 200: rawConfigurationSchema, ...errorResponses, }, }, @@ -76,7 +78,7 @@ export async function start_configuration_server< Body: RawConfiguration; }> ): Promise => { - return connector.update_configuration( + return connector.updateConfiguration( // type assertion required because Configuration is a generic parameter request.body as RawConfiguration ); @@ -93,14 +95,14 @@ export async function start_configuration_server< }, }, }, - async (): Promise => raw_configuration_schema + async (): Promise => rawConfigurationSchema ); server.post( "/validate", { schema: { - body: raw_configuration_schema, + body: rawConfigurationSchema, response: { 200: ValidateResponseSchema, ...errorResponses, @@ -110,12 +112,12 @@ export async function start_configuration_server< async ( request: FastifyRequest<{ Body: RawConfiguration }> ): Promise => { - const resolvedConfiguration = await connector.validate_raw_configuration( + const resolvedConfiguration = await connector.validateRawConfiguration( // type assertion required because Configuration is a generic parameter request.body as RawConfiguration ); - const schema = await connector.get_schema(resolvedConfiguration); - const capabilities = connector.get_capabilities(resolvedConfiguration); + const schema = await connector.getSchema(resolvedConfiguration); + const capabilities = connector.getCapabilities(resolvedConfiguration); return { schema, diff --git a/src/connector.ts b/src/connector.ts index 26e361b..bb2764c 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -14,7 +14,7 @@ export interface Connector { /** * Return jsonschema for the raw configuration for this connector */ - get_raw_configuration_schema(): JSONSchemaObject; + getRawConfigurationSchema(): JSONSchemaObject; /** * Return an empty raw configuration, to be manually filled in by the user to allow connection to the data source. @@ -28,14 +28,14 @@ export interface Connector { * } * ``` */ - make_empty_configuration(): RawConfiguration; + makeEmptyConfiguration(): RawConfiguration; /** * Take a raw configuration, update it where appropriate by connecting to the underlying data source, and otherwise return it as-is * For example, if our configuration includes a list of tables, we may want to fetch an updated list from the data source. * This is also used to "hidrate" an "empty" configuration where a user has provided connection details and little else. * @param rawConfiguration a base raw configuration */ - update_configuration( + updateConfiguration( rawConfiguration: RawConfiguration ): Promise; /** @@ -43,7 +43,7 @@ export interface Connector { * returning a configuration error or a validated [`Connector::Configuration`]. * @param configuration */ - validate_raw_configuration( + validateRawConfiguration( rawConfiguration: RawConfiguration ): Promise; @@ -58,7 +58,7 @@ export interface Connector { * @param configuration * @param metrics */ - try_init_state( + tryInitState( configuration: Configuration, metrics: unknown ): Promise; @@ -74,7 +74,7 @@ export interface Connector { * @param configuration * @param state */ - fetch_metrics(configuration: Configuration, state: State): Promise; + fetchMetrics(configuration: Configuration, state: State): Promise; /** * Check the health of the connector. * @@ -85,7 +85,7 @@ export interface Connector { * @param configuration * @param state */ - health_check(configuration: Configuration, state: State): Promise; + healthCheck(configuration: Configuration, state: State): Promise; /** * Get the connector's capabilities. @@ -96,7 +96,7 @@ export interface Connector { * This function should be syncronous * @param configuration */ - get_capabilities(configuration: Configuration): CapabilitiesResponse; + getCapabilities(configuration: Configuration): CapabilitiesResponse; /** * Get the connector's schema. @@ -105,7 +105,7 @@ export interface Connector { * from the NDC specification. * @param configuration */ - get_schema(configuration: Configuration): Promise; + getSchema(configuration: Configuration): Promise; /** * Explain a query by creating an execution plan @@ -116,12 +116,27 @@ export interface Connector { * @param state * @param request */ - explain( + queryExplain( configuration: Configuration, state: State, request: QueryRequest ): Promise; + /** + * Explain a mutation by creating an execution plan + * + * This function implements the [explain endpoint](https://hasura.github.io/ndc-spec/specification/explain.html) + * from the NDC specification. + * @param configuration + * @param state + * @param request + */ + mutationExplain( + configuration: Configuration, + state: State, + request: MutationRequest + ): Promise; + /** * Execute a mutation * diff --git a/src/index.ts b/src/index.ts index a834385..3f306b8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,14 @@ import { Connector } from "./connector"; import { Command, Option, InvalidOptionArgumentError } from "commander"; -import { ServerOptions, start_server } from "./server"; +import { ServerOptions, startServer } from "./server"; import { ConfigurationServerOptions, - start_configuration_server, -} from "./configuration_server"; + startConfigurationServer, +} from "./configuration-server"; export * from "./error"; export * from "./schema"; -export { Connector, ServerOptions, ConfigurationServerOptions, start_configuration_server, start_server }; +export { Connector, ServerOptions, ConfigurationServerOptions, startConfigurationServer, startServer }; /** * Starts the connector. @@ -22,13 +22,13 @@ export function start( ) { const program = new Command(); - program.addCommand(get_serve_command(connector)); - program.addCommand(get_serve_configuration_command(connector)); + program.addCommand(getServeCommand(connector)); + program.addCommand(getServeConfigurationCommand(connector)); program.parseAsync(process.argv).catch(console.error); } -export function get_serve_command( +export function getServeCommand( connector?: Connector ) { const command = new Command("serve") @@ -53,13 +53,13 @@ export function get_serve_command( if (connector) { command.action(async (options: ServerOptions) => { - await start_server(connector, options); + await startServer(connector, options); }) } return command; } -export function get_serve_configuration_command< +export function getServeConfigurationCommand< RawConfiguration, Configuration, State @@ -76,7 +76,7 @@ export function get_serve_configuration_command< if (connector) { serveCommand.action(async (options: ConfigurationServerOptions) => { - await start_configuration_server(connector, options); + await startConfigurationServer(connector, options); }); } diff --git a/src/schema/index.ts b/src/schema/index.ts index 021d922..84983a1 100644 --- a/src/schema/index.ts +++ b/src/schema/index.ts @@ -1,7 +1,7 @@ import { JSONSchemaObject } from "@json-schema-tools/meta-schema"; import schema from "./schema.generated.json"; -function schema_for_type(type_name: string): JSONSchemaObject { +function schemaForType(type_name: string): JSONSchemaObject { return { $schema: schema.$schema, $ref: `#/definitions/${type_name}`, @@ -9,15 +9,15 @@ function schema_for_type(type_name: string): JSONSchemaObject { } } -const CapabilitiesResponseSchema = schema_for_type("CapabilitiesResponse"); -const SchemaResponseSchema = schema_for_type("SchemaResponse"); -const QueryRequestSchema = schema_for_type("QueryRequest"); -const QueryResponseSchema = schema_for_type("QueryResponse"); -const ExplainResponseSchema = schema_for_type("ExplainResponse"); -const MutationRequestSchema = schema_for_type("MutationRequest"); -const MutationResponseSchema = schema_for_type("MutationResponse"); -const ErrorResponseSchema = schema_for_type("ErrorResponse"); -const ValidateResponseSchema = schema_for_type("ValidateResponse"); +const CapabilitiesResponseSchema = schemaForType("CapabilitiesResponse"); +const SchemaResponseSchema = schemaForType("SchemaResponse"); +const QueryRequestSchema = schemaForType("QueryRequest"); +const QueryResponseSchema = schemaForType("QueryResponse"); +const ExplainResponseSchema = schemaForType("ExplainResponse"); +const MutationRequestSchema = schemaForType("MutationRequest"); +const MutationResponseSchema = schemaForType("MutationResponse"); +const ErrorResponseSchema = schemaForType("ErrorResponse"); +const ValidateResponseSchema = schemaForType("ValidateResponse"); export * from "./schema.generated"; export { diff --git a/src/schema/schema.generated.json b/src/schema/schema.generated.json index dbf101f..ac23992 100644 --- a/src/schema/schema.generated.json +++ b/src/schema/schema.generated.json @@ -48,10 +48,10 @@ "type": "object", "required": [ "capabilities", - "versions" + "version" ], "properties": { - "versions": { + "version": { "type": "string" }, "capabilities": { @@ -64,21 +64,15 @@ "description": "Describes the features of the specification which a data connector implements.", "type": "object", "required": [ + "mutation", "query" ], "properties": { "query": { "$ref": "#/definitions/QueryCapabilities" }, - "explain": { - "anyOf": [ - { - "$ref": "#/definitions/LeafCapability" - }, - { - "type": "null" - } - ] + "mutation": { + "$ref": "#/definitions/MutationCapabilities" }, "relationships": { "anyOf": [ @@ -117,6 +111,17 @@ "type": "null" } ] + }, + "explain": { + "description": "Does the connector support explaining queries", + "anyOf": [ + { + "$ref": "#/definitions/LeafCapability" + }, + { + "type": "null" + } + ] } } }, @@ -124,6 +129,34 @@ "description": "A unit value to indicate a particular leaf capability is supported. This is an empty struct to allow for future sub-capabilities.", "type": "object" }, + "MutationCapabilities": { + "title": "Mutation Capabilities", + "type": "object", + "properties": { + "transactional": { + "description": "Does the connector support executing multiple mutations in a transaction.", + "anyOf": [ + { + "$ref": "#/definitions/LeafCapability" + }, + { + "type": "null" + } + ] + }, + "explain": { + "description": "Does the connector support explaining mutations", + "anyOf": [ + { + "$ref": "#/definitions/LeafCapability" + }, + { + "type": "null" + } + ] + } + } + }, "RelationshipCapabilities": { "title": "Relationship Capabilities", "type": "object", @@ -314,26 +347,85 @@ ] } } + }, + { + "description": "A predicate type for a given object type", + "type": "object", + "required": [ + "object_type_name", + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "predicate" + ] + }, + "object_type_name": { + "description": "The object type name", + "type": "string" + } + } } ] }, "ComparisonOperatorDefinition": { "title": "Comparison Operator Definition", "description": "The definition of a comparison operator on a scalar type", - "type": "object", - "required": [ - "argument_type" - ], - "properties": { - "argument_type": { - "description": "The type of the argument to this operator", - "allOf": [ - { - "$ref": "#/definitions/Type" + "oneOf": [ + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "equal" + ] } - ] + } + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "in" + ] + } + } + }, + { + "type": "object", + "required": [ + "argument_type", + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "custom" + ] + }, + "argument_type": { + "description": "The type of the argument to this operator", + "allOf": [ + { + "$ref": "#/definitions/Type" + } + ] + } + } } - } + ] }, "ObjectType": { "title": "Object Type", @@ -670,7 +762,7 @@ } ] }, - "where": { + "predicate": { "anyOf": [ { "$ref": "#/definitions/Expression" @@ -767,6 +859,17 @@ }, "column": { "type": "string" + }, + "fields": { + "description": "When the type of the column is a (possibly-nullable) array or object, the caller can request a subset of the complete column data, by specifying fields to fetch here. If omitted, the column data will be fetched in full.", + "anyOf": [ + { + "$ref": "#/definitions/NestedField" + }, + { + "type": "null" + } + ] } } }, @@ -803,6 +906,52 @@ } ] }, + "NestedField": { + "title": "NestedField", + "oneOf": [ + { + "title": "NestedObject", + "type": "object", + "required": [ + "fields", + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "object" + ] + }, + "fields": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Field" + } + } + } + }, + { + "title": "NestedArray", + "type": "object", + "required": [ + "fields", + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "array" + ] + }, + "fields": { + "$ref": "#/definitions/NestedField" + } + } + } + ] + }, "RelationshipArgument": { "title": "Relationship Argument", "oneOf": [ @@ -993,7 +1142,6 @@ "type": "object", "required": [ "arguments", - "predicate", "relationship" ], "properties": { @@ -1010,9 +1158,12 @@ }, "predicate": { "description": "A predicate expression to apply to the target collection", - "allOf": [ + "anyOf": [ { "$ref": "#/definitions/Expression" + }, + { + "type": "null" } ] } @@ -1122,48 +1273,18 @@ "$ref": "#/definitions/ComparisonTarget" }, "operator": { - "$ref": "#/definitions/BinaryComparisonOperator" + "type": "string" }, "value": { "$ref": "#/definitions/ComparisonValue" } } }, - { - "type": "object", - "required": [ - "column", - "operator", - "type", - "values" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "binary_array_comparison_operator" - ] - }, - "column": { - "$ref": "#/definitions/ComparisonTarget" - }, - "operator": { - "$ref": "#/definitions/BinaryArrayComparisonOperator" - }, - "values": { - "type": "array", - "items": { - "$ref": "#/definitions/ComparisonValue" - } - } - } - }, { "type": "object", "required": [ "in_collection", - "type", - "where" + "type" ], "properties": { "type": { @@ -1175,8 +1296,15 @@ "in_collection": { "$ref": "#/definitions/ExistsInCollection" }, - "where": { - "$ref": "#/definitions/Expression" + "predicate": { + "anyOf": [ + { + "$ref": "#/definitions/Expression" + }, + { + "type": "null" + } + ] } } } @@ -1240,43 +1368,6 @@ "is_null" ] }, - "BinaryComparisonOperator": { - "title": "Binary Comparison Operator", - "oneOf": [ - { - "type": "object", - "required": [ - "type" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "equal" - ] - } - } - }, - { - "type": "object", - "required": [ - "name", - "type" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "other" - ] - }, - "name": { - "type": "string" - } - } - } - ] - }, "ComparisonValue": { "title": "Comparison Value", "oneOf": [ @@ -1334,13 +1425,6 @@ } ] }, - "BinaryArrayComparisonOperator": { - "title": "Binary Array Comparison Operator", - "type": "string", - "enum": [ - "in" - ] - }, "ExistsInCollection": { "title": "Exists In Collection", "oneOf": [ diff --git a/src/schema/schema.generated.ts b/src/schema/schema.generated.ts index 927e2c6..0207892 100644 --- a/src/schema/schema.generated.ts +++ b/src/schema/schema.generated.ts @@ -29,6 +29,30 @@ export type Type = * The type of the elements of the array */ element_type: Type; + } + | { + type: "predicate"; + /** + * The object type name + */ + object_type_name: string; + }; +/** + * The definition of a comparison operator on a scalar type + */ +export type ComparisonOperatorDefinition = + | { + type: "equal"; + } + | { + type: "in"; + } + | { + type: "custom"; + /** + * The type of the argument to this operator + */ + argument_type: Type; }; export type Aggregate = | { @@ -60,6 +84,10 @@ export type Field = | { type: "column"; column: string; + /** + * When the type of the column is a (possibly-nullable) array or object, the caller can request a subset of the complete column data, by specifying fields to fetch here. If omitted, the column data will be fetched in full. + */ + fields?: NestedField | null; } | { type: "relationship"; @@ -75,6 +103,7 @@ export type Field = [k: string]: RelationshipArgument; }; }; +export type NestedField = NestedObject | NestedArray; export type RelationshipArgument = | { type: "variable"; @@ -144,19 +173,13 @@ export type Expression = | { type: "binary_comparison_operator"; column: ComparisonTarget; - operator: BinaryComparisonOperator; + operator: string; value: ComparisonValue; } - | { - type: "binary_array_comparison_operator"; - column: ComparisonTarget; - operator: BinaryArrayComparisonOperator; - values: ComparisonValue[]; - } | { type: "exists"; in_collection: ExistsInCollection; - where: Expression; + predicate?: Expression | null; }; export type ComparisonTarget = | { @@ -178,14 +201,6 @@ export type ComparisonTarget = name: string; }; export type UnaryComparisonOperator = "is_null"; -export type BinaryComparisonOperator = - | { - type: "equal"; - } - | { - type: "other"; - name: string; - }; export type ComparisonValue = | { type: "column"; @@ -199,7 +214,6 @@ export type ComparisonValue = type: "variable"; name: string; }; -export type BinaryArrayComparisonOperator = "in"; export type ExistsInCollection = | { type: "related"; @@ -270,7 +284,7 @@ export interface SchemaRoot { validate_response: ValidateResponse; } export interface CapabilitiesResponse { - versions: string; + version: string; capabilities: Capabilities; } /** @@ -278,7 +292,7 @@ export interface CapabilitiesResponse { */ export interface Capabilities { query: QueryCapabilities; - explain?: LeafCapability | null; + mutation: MutationCapabilities; relationships?: RelationshipCapabilities | null; } export interface QueryCapabilities { @@ -290,11 +304,25 @@ export interface QueryCapabilities { * Does the connector support queries which use variables */ variables?: LeafCapability | null; + /** + * Does the connector support explaining queries + */ + explain?: LeafCapability | null; } /** * A unit value to indicate a particular leaf capability is supported. This is an empty struct to allow for future sub-capabilities. */ export interface LeafCapability {} +export interface MutationCapabilities { + /** + * Does the connector support executing multiple mutations in a transaction. + */ + transactional?: LeafCapability | null; + /** + * Does the connector support explaining mutations + */ + explain?: LeafCapability | null; +} export interface RelationshipCapabilities { /** * Does the connector support comparisons that involve related collections (ie. joins)? @@ -357,15 +385,6 @@ export interface AggregateFunctionDefinition { */ result_type: Type; } -/** - * The definition of a comparison operator on a scalar type - */ -export interface ComparisonOperatorDefinition { - /** - * The type of the argument to this operator - */ - argument_type: Type; -} /** * The definition of an object type */ @@ -551,7 +570,17 @@ export interface Query { */ offset?: number | null; order_by?: OrderBy | null; - where?: Expression | null; + predicate?: Expression | null; +} +export interface NestedObject { + type: "object"; + fields: { + [k: string]: Field; + }; +} +export interface NestedArray { + type: "array"; + fields: NestedField; } export interface OrderBy { /** @@ -577,7 +606,7 @@ export interface PathElement { /** * A predicate expression to apply to the target collection */ - predicate: Expression; + predicate?: Expression | null; } export interface Relationship { /** diff --git a/src/server.ts b/src/server.ts index 6782f88..86af227 100644 --- a/src/server.ts +++ b/src/server.ts @@ -22,7 +22,6 @@ import { } from "./schema"; import Ajv, { Options as AjvOptions, ErrorObject as AjvErrorObject } from "ajv"; -import fastify from "fastify"; // Create custom Ajv options to handle Rust's uint32 which is a format used in the JSON schemas, so this converts that to a number const customAjvOptions: AjvOptions = { @@ -47,8 +46,10 @@ const errorResponses = { 400: ErrorResponseSchema, 403: ErrorResponseSchema, 409: ErrorResponseSchema, + 422: ErrorResponseSchema, 500: ErrorResponseSchema, 501: ErrorResponseSchema, + 502: ErrorResponseSchema, }; export interface ServerOptions { @@ -62,21 +63,21 @@ export interface ServerOptions { } class ConfigurationError extends Error { - validation_errors: AjvErrorObject[]; + validationErrors: AjvErrorObject[]; constructor(message: string, errors: AjvErrorObject[]) { super(message); - this.validation_errors = errors; + this.validationErrors = errors; } } -export async function start_server( +export async function startServer( connector: Connector, options: ServerOptions ) { const ajv = new Ajv(customAjvOptions); const validateRawConfigurationAgainstSchema = ajv.compile( - connector.get_raw_configuration_schema() + connector.getRawConfigurationSchema() ); const data = fs.readFileSync(options.configuration); @@ -88,13 +89,13 @@ export async function start_server( ); } - const configuration = await connector.validate_raw_configuration( + const configuration = await connector.validateRawConfiguration( rawConfiguration ); const metrics = {}; // todo - const state = await connector.try_init_state(configuration, metrics); + const state = await connector.tryInitState(configuration, metrics); const server = Fastify({ logger: configureFastifyLogging(options), @@ -142,16 +143,16 @@ export async function start_server( }, }, (_request: FastifyRequest): CapabilitiesResponse => { - return connector.get_capabilities(configuration); + return connector.getCapabilities(configuration); } ); server.get("/health", (_request): Promise => { - return connector.health_check(configuration, state); + return connector.healthCheck(configuration, state); }); server.get("/metrics", (_request) => { - return connector.fetch_metrics(configuration, state); + return connector.fetchMetrics(configuration, state); }); server.get( @@ -165,7 +166,7 @@ export async function start_server( }, }, (_request): Promise => { - return connector.get_schema(configuration); + return connector.getSchema(configuration); } ); @@ -197,7 +198,7 @@ export async function start_server( ); server.post( - "/explain", + "/query/explain", { schema: { body: QueryRequestSchema, @@ -209,7 +210,7 @@ export async function start_server( }, async (request: FastifyRequest<{ Body: QueryRequest }>) => { request.log.debug({ requestBody: request.body }, "Explain Request"); - const explainResponse = await connector.explain( + const explainResponse = await connector.queryExplain( configuration, state, request.body @@ -249,6 +250,29 @@ export async function start_server( } ); + server.post( + "/mutation/explain", + { + schema: { + body: MutationRequestSchema, + response: { + 200: ExplainResponseSchema, + ...errorResponses, + }, + }, + }, + async (request: FastifyRequest<{ Body: MutationRequest }>) => { + request.log.debug({ requestBody: request.body }, "Mutation Explain Request"); + const explainResponse = await connector.mutationExplain( + configuration, + state, + request.body + ); + request.log.debug({ responseBody: explainResponse }, "Mutation Explain Response"); + return explainResponse; + } + ); + server.setErrorHandler(function (error, _request, reply) { this.log.error(error); diff --git a/typegen/Cargo.toml b/typegen/Cargo.toml index 94923e2..b25d837 100644 --- a/typegen/Cargo.toml +++ b/typegen/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ndc-client = { git = "http://github.com/hasura/ndc-spec.git", tag = "v0.1.0-rc.13" } +ndc-client = { git = "http://github.com/hasura/ndc-spec.git", tag = "v0.1.0-rc.14" } schemars = "0.8.15" serde = "^1.0" serde_json = "1.0.107" From 03191e612f0bd49f8d6f96ff6cc43672ad8e50ee Mon Sep 17 00:00:00 2001 From: Daniel Chambers Date: Mon, 5 Feb 2024 14:48:30 +1100 Subject: [PATCH 2/2] Update query explain debug logging message --- src/server.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server.ts b/src/server.ts index 86af227..58e369a 100644 --- a/src/server.ts +++ b/src/server.ts @@ -209,13 +209,13 @@ export async function startServer( }, }, async (request: FastifyRequest<{ Body: QueryRequest }>) => { - request.log.debug({ requestBody: request.body }, "Explain Request"); + request.log.debug({ requestBody: request.body }, "Query Explain Request"); const explainResponse = await connector.queryExplain( configuration, state, request.body ); - request.log.debug({ responseBody: explainResponse }, "Explain Response"); + request.log.debug({ responseBody: explainResponse }, "Query Explain Response"); return explainResponse; } );