Add emitter for OpenAPI 3.0 (#745)
This commit is contained in:
Родитель
d28a30a7a9
Коммит
0b024d674c
|
@ -400,14 +400,14 @@ The first step in using a library is to install it via `npm`. You can get `npm`
|
||||||
|
|
||||||
If you haven't already intiialized your Cadl project's package.json file, now would be a good time to do so. The package.json file lets you track the dependencies your project depends on, and is a best practice to check in along with any Cadl files you create. Run `npm init` create your package.json file.
|
If you haven't already intiialized your Cadl project's package.json file, now would be a good time to do so. The package.json file lets you track the dependencies your project depends on, and is a best practice to check in along with any Cadl files you create. Run `npm init` create your package.json file.
|
||||||
|
|
||||||
Then, in your Cadl project directory, type `npm install libraryName` to install a library. For example, to install the official Cadl REST API bindings and OpenAPI generator, you would type `npm install @cadl-lang/rest @azure-tools/cadl-autorest`.
|
Then, in your Cadl project directory, type `npm install libraryName` to install a library. For example, to install the official Cadl REST API bindings and OpenAPI generator, you would type `npm install @cadl-lang/rest @cadl-lang/openapi3`.
|
||||||
|
|
||||||
Lastly, you need to import the libraries into your Cadl program. By convention, all external dependencies are imported in your `main.cadl` file, but can be in any Cadl file imported into your program. Importing the two libraries we installed above would look like this:
|
Lastly, you need to import the libraries into your Cadl program. By convention, all external dependencies are imported in your `main.cadl` file, but can be in any Cadl file imported into your program. Importing the two libraries we installed above would look like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
// in main.cadl
|
// in main.cadl
|
||||||
import "@cadl-lang/rest";
|
import "@cadl-lang/rest";
|
||||||
import "@azure-tools/cadl-autorest";
|
import "@cadl-lang/openapi3";
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Creating libraries
|
#### Creating libraries
|
||||||
|
@ -420,9 +420,9 @@ The package.json file for an Cadl library requires one additional field: `cadlMa
|
||||||
|
|
||||||
With the language building blocks we've covered so far we're ready to author our first REST API. Cadl has an official REST API "binding" called `@cadl-lang/rest`. It's a set of Cadl declarations and decorators that describe REST APIs and can be used by code generators to generate OpenAPI descriptions, implementation code, and the like.
|
With the language building blocks we've covered so far we're ready to author our first REST API. Cadl has an official REST API "binding" called `@cadl-lang/rest`. It's a set of Cadl declarations and decorators that describe REST APIs and can be used by code generators to generate OpenAPI descriptions, implementation code, and the like.
|
||||||
|
|
||||||
Cadl also has an official OpenAPI emitter called `@azure-tools/cadl-autorest` that consumes the REST API bindings and emits standard OpenAPI descriptions. This can then be fed in to any OpenAPI code generation pipeline.
|
Cadl also has an official OpenAPI emitter called `@cadl-lang/openapi3` that consumes the REST API bindings and emits standard OpenAPI descriptions. This can then be fed in to any OpenAPI code generation pipeline.
|
||||||
|
|
||||||
The following examples assume you have imported both `@azure-tools/cadl-autorest` and `@cadl-lang/rest` somewhere in your Cadl program (though importing them in `main.cadl` is the standard convention).
|
The following examples assume you have imported both `@cadl-lang/openapi3` and `@cadl-lang/rest` somewhere in your Cadl program (though importing them in `main.cadl` is the standard convention).
|
||||||
|
|
||||||
#### Service definition and metadata
|
#### Service definition and metadata
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -0,0 +1 @@
|
||||||
|
# Change Log - @cadl-lang/cadl-openapi3
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"name": "@cadl-lang/openapi3",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"author": "Microsoft Corporation",
|
||||||
|
"description": "Cadl library for emitting OpenAPI 3.0 from the Cadl REST protocol binding",
|
||||||
|
"homepage": "https://github.com/Azure/adl",
|
||||||
|
"readme": "https://github.com/Azure/adl/blob/master/README.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/Azure/adl.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/Azure/adl/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"cadl"
|
||||||
|
],
|
||||||
|
"type": "module",
|
||||||
|
"main": "dist/src/openapi.js",
|
||||||
|
"cadlMain": "dist/src/openapi.js",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc -p .",
|
||||||
|
"watch": "tsc -p . --watch",
|
||||||
|
"test": "mocha --timeout 5000 --require source-map-support/register --ignore 'dist/test/manual/**/*.js' 'dist/test/**/*.js'",
|
||||||
|
"test-official": "mocha --forbid-only --timeout 5000 --require source-map-support/register --ignore 'dist/test/manual/**/*.js' 'dist/test/**/*.js'"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib/*.cadl",
|
||||||
|
"dist/**",
|
||||||
|
"!dist/test/**"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@cadl-lang/compiler": "0.19.0",
|
||||||
|
"@cadl-lang/rest": "0.6.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/mocha": "~7.0.2",
|
||||||
|
"@types/node": "~14.0.27",
|
||||||
|
"mocha": "~8.3.2",
|
||||||
|
"typescript": "~4.3.2"
|
||||||
|
}
|
||||||
|
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,410 @@
|
||||||
|
import { deepStrictEqual, ok, strictEqual } from "assert";
|
||||||
|
import { openApiFor } from "./testHost.js";
|
||||||
|
|
||||||
|
describe("openapi3: definitions", () => {
|
||||||
|
it("defines models", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Foo",
|
||||||
|
`model Foo {
|
||||||
|
x: int32;
|
||||||
|
};`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
deepStrictEqual(res.schemas.Foo, {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
x: { type: "integer", format: "int32" },
|
||||||
|
},
|
||||||
|
required: ["x"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("doesn't define anonymous or unconnected models", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"{ ... Foo }",
|
||||||
|
`model Foo {
|
||||||
|
x: int32;
|
||||||
|
};`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(!res.isRef);
|
||||||
|
strictEqual(Object.keys(res.schemas).length, 0);
|
||||||
|
deepStrictEqual(res.useSchema, {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
x: { type: "integer", format: "int32" },
|
||||||
|
},
|
||||||
|
required: ["x"],
|
||||||
|
"x-cadl-name": "(anonymous model)",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines templated models", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Foo<int32>",
|
||||||
|
`model Foo<T> {
|
||||||
|
x: T;
|
||||||
|
};`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas.Foo_int32, "expected definition named Foo_int32");
|
||||||
|
deepStrictEqual(res.schemas.Foo_int32, {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
x: { type: "integer", format: "int32" },
|
||||||
|
},
|
||||||
|
required: ["x"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines templated models when template param is in a namespace", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Foo<Test.M>",
|
||||||
|
`
|
||||||
|
namespace Test {
|
||||||
|
model M {}
|
||||||
|
}
|
||||||
|
model Foo<T> {
|
||||||
|
x: T;
|
||||||
|
};`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas["Foo_Test.M"], "expected definition named Foo_Test.M");
|
||||||
|
deepStrictEqual(res.schemas["Foo_Test.M"], {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
x: { $ref: "#/components/schemas/Test.M" },
|
||||||
|
},
|
||||||
|
required: ["x"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines models extended from models", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Bar",
|
||||||
|
`
|
||||||
|
model Foo {
|
||||||
|
y: int32;
|
||||||
|
};
|
||||||
|
model Bar extends Foo {}`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas.Foo, "expected definition named Foo");
|
||||||
|
ok(res.schemas.Bar, "expected definition named Bar");
|
||||||
|
deepStrictEqual(res.schemas.Bar, {
|
||||||
|
type: "object",
|
||||||
|
properties: {},
|
||||||
|
allOf: [{ $ref: "#/components/schemas/Foo" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
deepStrictEqual(res.schemas.Foo, {
|
||||||
|
type: "object",
|
||||||
|
properties: { y: { type: "integer", format: "int32" } },
|
||||||
|
required: ["y"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines models with properties extended from models", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Bar",
|
||||||
|
`
|
||||||
|
model Foo {
|
||||||
|
y: int32;
|
||||||
|
};
|
||||||
|
model Bar extends Foo {
|
||||||
|
x: int32;
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas.Foo, "expected definition named Foo");
|
||||||
|
ok(res.schemas.Bar, "expected definition named Bar");
|
||||||
|
deepStrictEqual(res.schemas.Bar, {
|
||||||
|
type: "object",
|
||||||
|
properties: { x: { type: "integer", format: "int32" } },
|
||||||
|
allOf: [{ $ref: "#/components/schemas/Foo" }],
|
||||||
|
required: ["x"],
|
||||||
|
});
|
||||||
|
|
||||||
|
deepStrictEqual(res.schemas.Foo, {
|
||||||
|
type: "object",
|
||||||
|
properties: { y: { type: "integer", format: "int32" } },
|
||||||
|
required: ["y"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines models extended from templated models", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Bar",
|
||||||
|
`
|
||||||
|
model Foo<T> {
|
||||||
|
y: T;
|
||||||
|
};
|
||||||
|
model Bar extends Foo<int32> {}`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas["Foo_int32"] === undefined, "no definition named Foo_int32");
|
||||||
|
ok(res.schemas.Bar, "expected definition named Bar");
|
||||||
|
deepStrictEqual(res.schemas.Bar, {
|
||||||
|
type: "object",
|
||||||
|
properties: { y: { type: "integer", format: "int32" } },
|
||||||
|
required: ["y"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines models with properties extended from templated models", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Bar",
|
||||||
|
`
|
||||||
|
model Foo<T> {
|
||||||
|
y: T;
|
||||||
|
};
|
||||||
|
model Bar extends Foo<int32> {
|
||||||
|
x: int32
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas.Foo_int32, "expected definition named Foo_int32");
|
||||||
|
ok(res.schemas.Bar, "expected definition named Bar");
|
||||||
|
deepStrictEqual(res.schemas.Bar, {
|
||||||
|
type: "object",
|
||||||
|
properties: { x: { type: "integer", format: "int32" } },
|
||||||
|
allOf: [{ $ref: "#/components/schemas/Foo_int32" }],
|
||||||
|
required: ["x"],
|
||||||
|
});
|
||||||
|
|
||||||
|
deepStrictEqual(res.schemas.Foo_int32, {
|
||||||
|
type: "object",
|
||||||
|
properties: { y: { type: "integer", format: "int32" } },
|
||||||
|
required: ["y"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines templated models with properties extended from templated models", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Bar<int32>",
|
||||||
|
`
|
||||||
|
model Foo<T> {
|
||||||
|
y: T;
|
||||||
|
};
|
||||||
|
model Bar<T> extends Foo<T> {
|
||||||
|
x: T
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas.Foo_int32, "expected definition named Foo_int32");
|
||||||
|
ok(res.schemas.Bar_int32, "expected definition named Bar_int32");
|
||||||
|
deepStrictEqual(res.schemas.Bar_int32, {
|
||||||
|
type: "object",
|
||||||
|
properties: { x: { type: "integer", format: "int32" } },
|
||||||
|
allOf: [{ $ref: "#/components/schemas/Foo_int32" }],
|
||||||
|
required: ["x"],
|
||||||
|
});
|
||||||
|
|
||||||
|
deepStrictEqual(res.schemas.Foo_int32, {
|
||||||
|
type: "object",
|
||||||
|
properties: { y: { type: "integer", format: "int32" } },
|
||||||
|
required: ["y"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines models with no properties extended", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Bar",
|
||||||
|
`
|
||||||
|
model Foo {};
|
||||||
|
model Bar extends Foo {};`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas.Foo, "expected definition named Foo");
|
||||||
|
ok(res.schemas.Bar, "expected definition named Bar");
|
||||||
|
deepStrictEqual(res.schemas.Bar, {
|
||||||
|
type: "object",
|
||||||
|
properties: {},
|
||||||
|
allOf: [{ $ref: "#/components/schemas/Foo" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
deepStrictEqual(res.schemas.Foo, {
|
||||||
|
type: "object",
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines models with no properties extended twice", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Baz",
|
||||||
|
`
|
||||||
|
model Foo { x: int32 };
|
||||||
|
model Bar extends Foo {};
|
||||||
|
model Baz extends Bar {};`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas.Foo, "expected definition named Foo");
|
||||||
|
ok(res.schemas.Bar, "expected definition named Bar");
|
||||||
|
ok(res.schemas.Baz, "expected definition named Baz");
|
||||||
|
deepStrictEqual(res.schemas.Baz, {
|
||||||
|
type: "object",
|
||||||
|
properties: {},
|
||||||
|
allOf: [{ $ref: "#/components/schemas/Bar" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
deepStrictEqual(res.schemas.Bar, {
|
||||||
|
type: "object",
|
||||||
|
properties: {},
|
||||||
|
allOf: [{ $ref: "#/components/schemas/Foo" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
deepStrictEqual(res.schemas.Foo, {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
x: {
|
||||||
|
format: "int32",
|
||||||
|
type: "integer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["x"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines models extended from primitives", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Pet",
|
||||||
|
`
|
||||||
|
model shortString extends string {}
|
||||||
|
model Pet { name: shortString };
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas.shortString, "expected definition named shortString");
|
||||||
|
ok(res.schemas.Pet, "expected definition named Pet");
|
||||||
|
deepStrictEqual(res.schemas.shortString, {
|
||||||
|
type: "string",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines models extended from primitives with attrs", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Pet",
|
||||||
|
`
|
||||||
|
@maxLength(10) @minLength(10)
|
||||||
|
model shortString extends string {}
|
||||||
|
model Pet { name: shortString };
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas.shortString, "expected definition named shortString");
|
||||||
|
ok(res.schemas.Pet, "expected definition named Pet");
|
||||||
|
deepStrictEqual(res.schemas.shortString, {
|
||||||
|
type: "string",
|
||||||
|
minLength: 10,
|
||||||
|
maxLength: 10,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("defines models extended from primitives with new attrs", async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Pet",
|
||||||
|
`
|
||||||
|
@maxLength(10)
|
||||||
|
model shortString extends string {}
|
||||||
|
@minLength(1)
|
||||||
|
model shortButNotEmptyString extends shortString {};
|
||||||
|
model Pet { name: shortButNotEmptyString, breed: shortString };
|
||||||
|
`
|
||||||
|
);
|
||||||
|
ok(res.isRef);
|
||||||
|
ok(res.schemas.shortString, "expected definition named shortString");
|
||||||
|
ok(res.schemas.shortButNotEmptyString, "expected definition named shortButNotEmptyString");
|
||||||
|
ok(res.schemas.Pet, "expected definition named Pet");
|
||||||
|
|
||||||
|
deepStrictEqual(res.schemas.shortString, {
|
||||||
|
type: "string",
|
||||||
|
maxLength: 10,
|
||||||
|
});
|
||||||
|
deepStrictEqual(res.schemas.shortButNotEmptyString, {
|
||||||
|
type: "string",
|
||||||
|
minLength: 1,
|
||||||
|
maxLength: 10,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("openapi3: primitives", () => {
|
||||||
|
const cases = [
|
||||||
|
["int32", { type: "integer", format: "int32" }],
|
||||||
|
["int64", { type: "integer", format: "int64" }],
|
||||||
|
["float32", { type: "number", format: "float" }],
|
||||||
|
["float64", { type: "number", format: "double" }],
|
||||||
|
["string", { type: "string" }],
|
||||||
|
["boolean", { type: "boolean" }],
|
||||||
|
["plainDate", { type: "string", format: "date" }],
|
||||||
|
["zonedDateTime", { type: "string", format: "date-time" }],
|
||||||
|
["plainTime", { type: "string", format: "time" }],
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const test of cases) {
|
||||||
|
it("knows schema for " + test[0], async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Pet",
|
||||||
|
`
|
||||||
|
model Pet { name: ${test[0]} };
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
const schema = res.schemas.Pet.properties.name;
|
||||||
|
deepStrictEqual(schema, test[1]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("openapi3: literals", () => {
|
||||||
|
const cases = [
|
||||||
|
["1", { type: "number", enum: [1] }],
|
||||||
|
['"hello"', { type: "string", enum: ["hello"] }],
|
||||||
|
["false", { type: "boolean", enum: [false] }],
|
||||||
|
["true", { type: "boolean", enum: [true] }],
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const test of cases) {
|
||||||
|
it("knows schema for " + test[0], async () => {
|
||||||
|
const res = await oapiForModel(
|
||||||
|
"Pet",
|
||||||
|
`
|
||||||
|
model Pet { name: ${test[0]} };
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
const schema = res.schemas.Pet.properties.name;
|
||||||
|
deepStrictEqual(schema, test[1]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function oapiForModel(name: string, modelDef: string) {
|
||||||
|
const oapi = await openApiFor(`
|
||||||
|
${modelDef};
|
||||||
|
@resource("/")
|
||||||
|
namespace root {
|
||||||
|
op read(): ${name};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const useSchema = oapi.paths["/"].get.responses[200].content["application/json"].schema;
|
||||||
|
|
||||||
|
return {
|
||||||
|
isRef: !!useSchema.$ref,
|
||||||
|
useSchema,
|
||||||
|
schemas: oapi.components.schemas || {},
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { createTestHost } from "@cadl-lang/compiler/dist/test/test-host.js";
|
||||||
|
import { resolve } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
export async function createOpenAPITestHost() {
|
||||||
|
const host = await createTestHost();
|
||||||
|
const root = resolve(fileURLToPath(import.meta.url), "../../../");
|
||||||
|
|
||||||
|
// load rest
|
||||||
|
await host.addRealCadlFile(
|
||||||
|
"./node_modules/rest/package.json",
|
||||||
|
resolve(root, "../rest/package.json")
|
||||||
|
);
|
||||||
|
await host.addRealCadlFile(
|
||||||
|
"./node_modules/rest/lib/rest.cadl",
|
||||||
|
resolve(root, "../rest/lib/rest.cadl")
|
||||||
|
);
|
||||||
|
await host.addRealJsFile(
|
||||||
|
"./node_modules/rest/dist/rest.js",
|
||||||
|
resolve(root, "../rest/dist/rest.js")
|
||||||
|
);
|
||||||
|
|
||||||
|
// load openapi
|
||||||
|
await host.addRealCadlFile(
|
||||||
|
"./node_modules/openapi3/package.json",
|
||||||
|
resolve(root, "../openapi3/package.json")
|
||||||
|
);
|
||||||
|
await host.addRealJsFile(
|
||||||
|
"./node_modules/openapi3/dist/src/openapi.js",
|
||||||
|
resolve(root, "../openapi3/dist/src/openapi.js")
|
||||||
|
);
|
||||||
|
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function openApiFor(code: string) {
|
||||||
|
const host = await createOpenAPITestHost();
|
||||||
|
const outPath = resolve("/openapi.json");
|
||||||
|
host.addCadlFile("./main.cadl", `import "rest"; import "openapi3";${code}`);
|
||||||
|
await host.compile("./main.cadl", { noEmit: false, swaggerOutputFile: outPath });
|
||||||
|
return JSON.parse(host.fs.get(outPath)!);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"references": [{ "path": "../compiler/tsconfig.json" }, { "path": "../rest/tsconfig.json" }],
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": ".",
|
||||||
|
"types": ["node", "mocha"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "test/**/*.ts"]
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче