@mittwald/api-code-generator 4.461.0 → 4.462.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/generation/compileJsonSchema.test.js +66 -0
- package/dist/esm/generation/model/CodeGenerationModel.test.js +66 -0
- package/dist/esm/openapi/OpenApiSpec.js +11 -2
- package/dist/esm/openapi/OpenApiSpec.test.js +49 -0
- package/dist/esm/openapi/openApiSchemaValidatorExtensions.js +30 -0
- package/dist/types/generation/compileJsonSchema.test.d.ts +1 -0
- package/dist/types/generation/model/CodeGenerationModel.test.d.ts +1 -0
- package/dist/types/openapi/OpenApiSpec.test.d.ts +1 -0
- package/dist/types/openapi/openApiSchemaValidatorExtensions.d.ts +16 -0
- package/package.json +2 -2
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { compileJsonSchema } from "./compileJsonSchema.js";
|
|
2
|
+
const normalize = (typeScript) => typeScript.replace(/\s+/g, " ").trim();
|
|
3
|
+
test.each([
|
|
4
|
+
["boolean", { type: "boolean", const: false }, "value: false;"],
|
|
5
|
+
["string", { type: "string", const: "foo" }, 'value: "foo";'],
|
|
6
|
+
["number", { type: "number", const: 42 }, "value: 42;"],
|
|
7
|
+
["integer", { type: "integer", const: 0 }, "value: 0;"],
|
|
8
|
+
["null", { type: "null", const: null }, "value: null;"],
|
|
9
|
+
])("const keyword on a %s property compiles to a literal type", async (_name, propertySchema, expected) => {
|
|
10
|
+
const compiled = await compileJsonSchema({
|
|
11
|
+
type: "object",
|
|
12
|
+
required: ["value"],
|
|
13
|
+
properties: { value: propertySchema },
|
|
14
|
+
}, "WithConst");
|
|
15
|
+
expect(normalize(compiled)).toContain(expected);
|
|
16
|
+
});
|
|
17
|
+
test("const keyword is equivalent to a single-valued enum", async () => {
|
|
18
|
+
const schemaWithConst = {
|
|
19
|
+
type: "object",
|
|
20
|
+
required: ["value"],
|
|
21
|
+
properties: { value: { type: "string", const: "foo" } },
|
|
22
|
+
};
|
|
23
|
+
const schemaWithEnum = {
|
|
24
|
+
type: "object",
|
|
25
|
+
required: ["value"],
|
|
26
|
+
properties: { value: { type: "string", enum: ["foo"] } },
|
|
27
|
+
};
|
|
28
|
+
expect(await compileJsonSchema(schemaWithConst, "Tagged")).toEqual(await compileJsonSchema(schemaWithEnum, "Tagged"));
|
|
29
|
+
});
|
|
30
|
+
test("const-tagged members compile to a discriminated union", async () => {
|
|
31
|
+
const compiled = await compileJsonSchema({
|
|
32
|
+
oneOf: [
|
|
33
|
+
{
|
|
34
|
+
type: "object",
|
|
35
|
+
required: ["event", "done", "progress"],
|
|
36
|
+
properties: {
|
|
37
|
+
event: { type: "string", const: "progress" },
|
|
38
|
+
done: { type: "boolean", const: false },
|
|
39
|
+
progress: { type: "number" },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: "object",
|
|
44
|
+
required: ["event", "done"],
|
|
45
|
+
properties: {
|
|
46
|
+
event: { type: "string", const: "done" },
|
|
47
|
+
done: { type: "boolean", const: true },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
}, "StreamEvent");
|
|
52
|
+
// Both discriminants must be literal types, otherwise TypeScript cannot
|
|
53
|
+
// narrow the union by checking `event` or `done`.
|
|
54
|
+
expect(normalize(compiled)).toEqual(normalize(`
|
|
55
|
+
export type StreamEvent =
|
|
56
|
+
| {
|
|
57
|
+
event: "progress";
|
|
58
|
+
done: false;
|
|
59
|
+
progress: number;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
event: "done";
|
|
63
|
+
done: true;
|
|
64
|
+
};
|
|
65
|
+
`));
|
|
66
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { OpenApiSpec } from "../../openapi/OpenApiSpec.js";
|
|
2
|
+
import { CodeGenerationModel } from "./CodeGenerationModel.js";
|
|
3
|
+
const normalize = (typeScript) => typeScript.replace(/\s+/g, " ").trim();
|
|
4
|
+
/**
|
|
5
|
+
* End-to-end check for the `const` keyword: an OpenAPI document describing a
|
|
6
|
+
* server-sent-event stream as a discriminated union has to pass validation and
|
|
7
|
+
* has to compile into literal types, so that consumers can narrow the union.
|
|
8
|
+
*
|
|
9
|
+
* @see https://spec.openapis.org/oas/v3.2.0.html#server-sent-event-streams
|
|
10
|
+
*/
|
|
11
|
+
test("const keywords survive from the spec into the generated types", async () => {
|
|
12
|
+
const document = {
|
|
13
|
+
openapi: "3.0.3",
|
|
14
|
+
info: { title: "Test", version: "1.0.0" },
|
|
15
|
+
paths: {},
|
|
16
|
+
components: {
|
|
17
|
+
schemas: {
|
|
18
|
+
ProgressEvent: {
|
|
19
|
+
type: "object",
|
|
20
|
+
required: ["event", "done", "progress"],
|
|
21
|
+
properties: {
|
|
22
|
+
event: { type: "string", const: "progress" },
|
|
23
|
+
done: { type: "boolean", const: false },
|
|
24
|
+
progress: { type: "number" },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
DoneEvent: {
|
|
28
|
+
type: "object",
|
|
29
|
+
required: ["event", "done", "exitCode"],
|
|
30
|
+
properties: {
|
|
31
|
+
event: { type: "string", const: "done" },
|
|
32
|
+
done: { type: "boolean", const: true },
|
|
33
|
+
exitCode: { type: "integer", const: 0 },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
StreamEvent: {
|
|
37
|
+
oneOf: [
|
|
38
|
+
{ $ref: "#/components/schemas/ProgressEvent" },
|
|
39
|
+
{ $ref: "#/components/schemas/DoneEvent" },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const spec = await OpenApiSpec.parse(document);
|
|
46
|
+
const model = CodeGenerationModel.fromDoc("Api", spec.document);
|
|
47
|
+
const types = normalize(await model.compileTypes({ rootNamespace: "Api" }));
|
|
48
|
+
expect(types).toContain(normalize(`
|
|
49
|
+
export interface ProgressEvent {
|
|
50
|
+
event: "progress";
|
|
51
|
+
done: false;
|
|
52
|
+
progress: number;
|
|
53
|
+
}
|
|
54
|
+
`));
|
|
55
|
+
expect(types).toContain(normalize(`
|
|
56
|
+
export interface DoneEvent {
|
|
57
|
+
event: "done";
|
|
58
|
+
done: true;
|
|
59
|
+
exitCode: 0;
|
|
60
|
+
}
|
|
61
|
+
`));
|
|
62
|
+
expect(types).toContain(normalize(`
|
|
63
|
+
export type StreamEvent = Api.Components.Schemas.ProgressEvent
|
|
64
|
+
| Api.Components.Schemas.DoneEvent;
|
|
65
|
+
`));
|
|
66
|
+
});
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { OpenAPISchemaValidationError } from "./OpenAPISchemaValidationError.js";
|
|
2
2
|
import VError from "verror";
|
|
3
3
|
import { makeError } from "../lib/makeError.js";
|
|
4
|
-
import
|
|
4
|
+
import openApiSchemaValidatorModule from "openapi-schema-validator";
|
|
5
5
|
import { convert } from "swagger2openapi";
|
|
6
6
|
import { ux } from "@oclif/core";
|
|
7
|
-
|
|
7
|
+
import { openApiSchemaValidatorExtensions } from "./openApiSchemaValidatorExtensions.js";
|
|
8
|
+
/**
|
|
9
|
+
* `openapi-schema-validator` is a CommonJS module. Depending on the module
|
|
10
|
+
* interop of the environment, the default import either _is_ the class or is a
|
|
11
|
+
* namespace object carrying it in a nested `default` property.
|
|
12
|
+
*/
|
|
13
|
+
const interopDefault = (imported) => typeof imported === "function" ? imported : imported.default;
|
|
14
|
+
const OpenAPISchemaValidator = interopDefault(openApiSchemaValidatorModule);
|
|
15
|
+
const validator = new OpenAPISchemaValidator({
|
|
8
16
|
version: 3,
|
|
17
|
+
extensions: openApiSchemaValidatorExtensions,
|
|
9
18
|
});
|
|
10
19
|
export class OpenApiSpec {
|
|
11
20
|
document;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { OpenApiSpec } from "./OpenApiSpec.js";
|
|
2
|
+
const documentWithSchemas = (schemas) => ({
|
|
3
|
+
openapi: "3.0.3",
|
|
4
|
+
info: { title: "Test", version: "1.0.0" },
|
|
5
|
+
paths: {},
|
|
6
|
+
components: { schemas },
|
|
7
|
+
});
|
|
8
|
+
test.each([
|
|
9
|
+
["boolean", { type: "boolean", const: false }],
|
|
10
|
+
["string", { type: "string", const: "foo" }],
|
|
11
|
+
["number", { type: "number", const: 42 }],
|
|
12
|
+
["integer", { type: "integer", const: 0 }],
|
|
13
|
+
])("a %s schema using the const keyword validates", async (_name, schema) => {
|
|
14
|
+
const spec = await OpenApiSpec.parse(documentWithSchemas({ Tagged: schema }));
|
|
15
|
+
expect(spec.document.components?.schemas?.Tagged).toEqual(schema);
|
|
16
|
+
});
|
|
17
|
+
test("a discriminated union tagged with const validates", async () => {
|
|
18
|
+
const schemas = {
|
|
19
|
+
ProgressEvent: {
|
|
20
|
+
type: "object",
|
|
21
|
+
required: ["event", "done"],
|
|
22
|
+
properties: {
|
|
23
|
+
event: { type: "string", const: "progress" },
|
|
24
|
+
done: { type: "boolean", const: false },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
DoneEvent: {
|
|
28
|
+
type: "object",
|
|
29
|
+
required: ["event", "done"],
|
|
30
|
+
properties: {
|
|
31
|
+
event: { type: "string", const: "done" },
|
|
32
|
+
done: { type: "boolean", const: true },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
StreamEvent: {
|
|
36
|
+
oneOf: [
|
|
37
|
+
{ $ref: "#/components/schemas/ProgressEvent" },
|
|
38
|
+
{ $ref: "#/components/schemas/DoneEvent" },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const spec = await OpenApiSpec.parse(documentWithSchemas(schemas));
|
|
43
|
+
expect(spec.document.components?.schemas).toEqual(schemas);
|
|
44
|
+
});
|
|
45
|
+
test("unknown schema keywords are still rejected", async () => {
|
|
46
|
+
await expect(OpenApiSpec.parse(documentWithSchemas({
|
|
47
|
+
Broken: { type: "string", definitelyNotAKeyword: true },
|
|
48
|
+
}))).rejects.toThrow(/Failed parsing OpenAPISpec/);
|
|
49
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The meta schema shipped with `openapi-schema-validator` describes OpenAPI
|
|
3
|
+
* 3.0, whose Schema Object is a subset of JSON Schema Draft Wright 00 and is
|
|
4
|
+
* declared with `additionalProperties: false`. Keywords that were only added to
|
|
5
|
+
* the Schema Object with OpenAPI 3.1 (which aligns with JSON Schema 2020-12)
|
|
6
|
+
* are therefore rejected as "additional properties" — even though they are
|
|
7
|
+
* perfectly understood by the code generation further down the pipeline.
|
|
8
|
+
*
|
|
9
|
+
* These extensions are deep-merged into the meta schema by
|
|
10
|
+
* `openapi-schema-validator` and re-allow such keywords.
|
|
11
|
+
*
|
|
12
|
+
* @see https://spec.openapis.org/oas/v3.1.0.html#schema-object
|
|
13
|
+
* @see https://json-schema.org/draft/2020-12/json-schema-validation#name-const
|
|
14
|
+
*/
|
|
15
|
+
export const openApiSchemaValidatorExtensions = {
|
|
16
|
+
definitions: {
|
|
17
|
+
schema: {
|
|
18
|
+
properties: {
|
|
19
|
+
/**
|
|
20
|
+
* `const` restricts a schema to exactly one value; it is the JSON
|
|
21
|
+
* Schema 2020-12 equivalent of a single-valued `enum` and is compiled
|
|
22
|
+
* into a TypeScript literal type. Any JSON value is allowed here.
|
|
23
|
+
*/
|
|
24
|
+
const: {
|
|
25
|
+
description: "The value of this keyword MAY be of any type, including null.",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { IJsonSchema } from "openapi-types";
|
|
2
|
+
/**
|
|
3
|
+
* The meta schema shipped with `openapi-schema-validator` describes OpenAPI
|
|
4
|
+
* 3.0, whose Schema Object is a subset of JSON Schema Draft Wright 00 and is
|
|
5
|
+
* declared with `additionalProperties: false`. Keywords that were only added to
|
|
6
|
+
* the Schema Object with OpenAPI 3.1 (which aligns with JSON Schema 2020-12)
|
|
7
|
+
* are therefore rejected as "additional properties" — even though they are
|
|
8
|
+
* perfectly understood by the code generation further down the pipeline.
|
|
9
|
+
*
|
|
10
|
+
* These extensions are deep-merged into the meta schema by
|
|
11
|
+
* `openapi-schema-validator` and re-allow such keywords.
|
|
12
|
+
*
|
|
13
|
+
* @see https://spec.openapis.org/oas/v3.1.0.html#schema-object
|
|
14
|
+
* @see https://json-schema.org/draft/2020-12/json-schema-validation#name-const
|
|
15
|
+
*/
|
|
16
|
+
export declare const openApiSchemaValidatorExtensions: IJsonSchema;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mittwald/api-code-generator",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.462.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": "https://github.com/mittwald/api-client-js.git",
|
|
6
6
|
"license": "MIT",
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"@oclif/plugin-plugins"
|
|
85
85
|
]
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "74172d743808cc6b5f91fa20dfc9d19daa6dc6cc"
|
|
88
88
|
}
|