@trayio/tray-openapi 2.10.0 → 2.11.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.
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import * as TE from 'fp-ts/TaskEither';
|
|
2
2
|
import { FileStorage } from '@trayio/commons/file/File';
|
|
3
3
|
import { Generator } from '@trayio/generator/generator/Generator';
|
|
4
|
+
import { OpenApiHttpMethod } from './OpenApiTypeDescriptors';
|
|
5
|
+
type BaseOperation = {
|
|
6
|
+
httpMethod: OpenApiHttpMethod;
|
|
7
|
+
path: string;
|
|
8
|
+
};
|
|
9
|
+
type OperationError = BaseOperation & {
|
|
10
|
+
tag: 'error';
|
|
11
|
+
errorMessage: string;
|
|
12
|
+
};
|
|
13
|
+
type OperationSuccess = BaseOperation & {
|
|
14
|
+
tag: 'success';
|
|
15
|
+
};
|
|
16
|
+
export type BuildConnectorResponse = {
|
|
17
|
+
successes: OperationSuccess[];
|
|
18
|
+
errors: OperationError[];
|
|
19
|
+
};
|
|
4
20
|
export declare class OpenApiSchemaImporter {
|
|
5
21
|
private generator;
|
|
6
22
|
private fileStorage;
|
|
7
23
|
constructor(generator: Generator, fileStorage: FileStorage);
|
|
8
|
-
buildConnector(openApiSpecPath: string, connectorName: string): TE.TaskEither<Error,
|
|
24
|
+
buildConnector(openApiSpecPath: string, connectorName: string): TE.TaskEither<Error, BuildConnectorResponse>;
|
|
9
25
|
private generateOperationFromPath;
|
|
10
26
|
private decodeOperation;
|
|
11
27
|
private getOpenApiSpec;
|
|
@@ -13,4 +29,5 @@ export declare class OpenApiSchemaImporter {
|
|
|
13
29
|
private generateConnectorDirectory;
|
|
14
30
|
private generateOperationDirectory;
|
|
15
31
|
}
|
|
32
|
+
export {};
|
|
16
33
|
//# sourceMappingURL=OpenApiSchemaImporter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpenApiSchemaImporter.d.ts","sourceRoot":"","sources":["../src/OpenApiSchemaImporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AASvC,OAAO,EAAQ,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"OpenApiSchemaImporter.d.ts","sourceRoot":"","sources":["../src/OpenApiSchemaImporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AASvC,OAAO,EAAQ,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,uCAAuC,CAAC;AAGlE,OAAO,EACN,iBAAiB,EAGjB,MAAM,0BAA0B,CAAC;AAgClC,KAAK,aAAa,GAAG;IACpB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,KAAK,cAAc,GAAG,aAAa,GAAG;IACrC,GAAG,EAAE,OAAO,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,gBAAgB,GAAG,aAAa,GAAG;IACvC,GAAG,EAAE,SAAS,CAAC;CACf,CAAC;AAIF,MAAM,MAAM,sBAAsB,GAAG;IACpC,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,MAAM,EAAE,cAAc,EAAE,CAAC;CACzB,CAAC;AAEF,qBAAa,qBAAqB;IACrB,OAAO,CAAC,SAAS;IAAa,OAAO,CAAC,WAAW;gBAAzC,SAAS,EAAE,SAAS,EAAU,WAAW,EAAE,WAAW;IAE1E,cAAc,CACb,eAAe,EAAE,MAAM,EACvB,aAAa,EAAE,MAAM,GACnB,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,sBAAsB,CAAC;IAqD/C,OAAO,CAAC,yBAAyB;IAyDjC,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,cAAc;IAkCtB,OAAO,CAAC,oCAAoC;IAgF5C,OAAO,CAAC,0BAA0B;IAwBlC,OAAO,CAAC,0BAA0B;CAclC"}
|
|
@@ -59,10 +59,29 @@ class OpenApiSchemaImporter {
|
|
|
59
59
|
path: openApiSpec.paths[path][route],
|
|
60
60
|
})),
|
|
61
61
|
})));
|
|
62
|
-
}), TE.chain(({ openApiSpec, connectorPath, paths }) => (0, Array_1.traverse)(TE.ApplicativeSeq)((path) => this.generateOperationFromPath(path, connectorPath, openApiSpec.servers[0].url, connectorName))(paths)), TE.map(() =>
|
|
62
|
+
}), TE.chain(({ openApiSpec, connectorPath, paths }) => (0, Array_1.traverse)(TE.ApplicativeSeq)((path) => this.generateOperationFromPath(path, connectorPath, openApiSpec.servers[0].url, connectorName))(paths)), TE.map((operation) => {
|
|
63
|
+
console.log(operation);
|
|
64
|
+
const flatOperations = operation.flat();
|
|
65
|
+
const buildConnectorResponse = flatOperations.reduce((acc, curr) => {
|
|
66
|
+
if (curr.tag === 'error') {
|
|
67
|
+
return Object.assign(Object.assign({}, acc), { errors: [...acc.errors, curr] });
|
|
68
|
+
}
|
|
69
|
+
return Object.assign(Object.assign({}, acc), { successes: [...acc.successes, curr] });
|
|
70
|
+
}, { successes: [], errors: [] });
|
|
71
|
+
return buildConnectorResponse;
|
|
72
|
+
}));
|
|
63
73
|
}
|
|
64
74
|
generateOperationFromPath(path, connectorPath, baseUrl, connectorName) {
|
|
65
|
-
return (0, function_1.pipe)((0, Array_1.traverse)(TE.ApplicativeSeq)((route) => (0, function_1.pipe)(this.decodeOperation(route.path, path.path, route.method), TE.bindTo('decodedPath'), TE.chain(({ decodedPath }) => (0, function_1.pipe)(this.generateOperationDirectory(connectorPath, decodedPath.operationId), TE.bind('inputs', () => TE.right((0, GenerateInputSchema_1.generateInputSchema)(decodedPath))), TE.bind('outputs', () => (0, GenerateOutput_1.generateOutputSchema)(decodedPath.responses)), TE.bind('decodedPath', () => TE.right(decodedPath)))), TE.chain(({ inputs, outputs, decodedPath }) => this.generateHandlerFilesAndOperationJson(route.method, decodedPath, `${connectorPath}/src/${(0, lodash_1.kebabCase)(decodedPath.operationId)}`, baseUrl, path.path, connectorName, inputs, outputs))
|
|
75
|
+
return (0, function_1.pipe)((0, Array_1.traverse)(TE.ApplicativeSeq)((route) => (0, function_1.pipe)(this.decodeOperation(route.path, path.path, route.method), TE.bindTo('decodedPath'), TE.chain(({ decodedPath }) => (0, function_1.pipe)(this.generateOperationDirectory(connectorPath, decodedPath.operationId), TE.bind('inputs', () => TE.right((0, GenerateInputSchema_1.generateInputSchema)(decodedPath))), TE.bind('outputs', () => (0, GenerateOutput_1.generateOutputSchema)(decodedPath.responses)), TE.bind('decodedPath', () => TE.right(decodedPath)))), TE.chain(({ inputs, outputs, decodedPath }) => this.generateHandlerFilesAndOperationJson(route.method, decodedPath, `${connectorPath}/src/${(0, lodash_1.kebabCase)(decodedPath.operationId)}`, baseUrl, path.path, connectorName, inputs, outputs)), TE.map(() => ({
|
|
76
|
+
tag: 'success',
|
|
77
|
+
httpMethod: route.method,
|
|
78
|
+
path: path.path,
|
|
79
|
+
})), TE.orElseW((error) => TE.right({
|
|
80
|
+
tag: 'error',
|
|
81
|
+
httpMethod: route.method,
|
|
82
|
+
path: path.path,
|
|
83
|
+
errorMessage: error.message,
|
|
84
|
+
}))))(path.routes));
|
|
66
85
|
}
|
|
67
86
|
decodeOperation(path, httpPath, method) {
|
|
68
87
|
return (0, function_1.pipe)(TE.fromEither(OpenApiCodecs_1.pathCodec.decode(path)), TE.mapLeft((error) => new Error(`Failed to decode httpPath: ${httpPath} and method: ${method} : ${error}`)));
|
|
@@ -35,9 +35,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
35
35
|
const NodeFsGenerator_1 = require("@trayio/generator/generator/NodeFsGenerator");
|
|
36
36
|
const FakeFileStorage_1 = require("@trayio/commons/file/FakeFileStorage");
|
|
37
37
|
const stream_1 = require("stream");
|
|
38
|
+
const E = __importStar(require("fp-ts/Either"));
|
|
38
39
|
const fs = __importStar(require("fs-extra"));
|
|
39
40
|
const OpenApiSchemaImporter_1 = require("./OpenApiSchemaImporter");
|
|
40
|
-
const openApiSpec = __importStar(require("./
|
|
41
|
+
const openApiSpec = __importStar(require("./test-openapi-spec.json"));
|
|
41
42
|
describe('openApiSchemaImporter', () => {
|
|
42
43
|
const generator = new NodeFsGenerator_1.NodeFsGenerator();
|
|
43
44
|
const fileStorage = new FakeFileStorage_1.FakeFileStorage();
|
|
@@ -45,17 +46,21 @@ describe('openApiSchemaImporter', () => {
|
|
|
45
46
|
let result;
|
|
46
47
|
beforeAll(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
48
|
fileStorage.write({
|
|
48
|
-
key: `${__dirname}/
|
|
49
|
-
metadata: { name: '
|
|
49
|
+
key: `${__dirname}/test-openapi-spec.json`,
|
|
50
|
+
metadata: { name: 'test-openapi-spec.json' },
|
|
50
51
|
content: stream_1.Readable.from(Buffer.from(JSON.stringify(openApiSpec))),
|
|
51
52
|
});
|
|
52
|
-
result = yield openApiSchemaImporter.buildConnector(`${__dirname}/
|
|
53
|
+
result = yield openApiSchemaImporter.buildConnector(`${__dirname}/test-openapi-spec.json`, 'testConnector')();
|
|
53
54
|
}));
|
|
54
55
|
afterAll(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
55
56
|
yield fs.rm(`${process.cwd()}/test/`, { recursive: true, force: true });
|
|
56
57
|
}));
|
|
57
58
|
it('should import successfully', () => {
|
|
58
|
-
|
|
59
|
+
const buildConnectorResponse = E.getOrElse((error) => {
|
|
60
|
+
throw new Error('Should have been right');
|
|
61
|
+
})(result);
|
|
62
|
+
expect(buildConnectorResponse.successes.length).toEqual(1);
|
|
63
|
+
expect(buildConnectorResponse.errors.length).toEqual(1);
|
|
59
64
|
});
|
|
60
65
|
it('should generate a operation handler', () => {
|
|
61
66
|
expect(fileStorage.files.has(`/test/test-connector/src/get-post/handler.ts`)).toEqual(true);
|
|
@@ -72,4 +77,7 @@ describe('openApiSchemaImporter', () => {
|
|
|
72
77
|
it('should generate a operation directory', () => {
|
|
73
78
|
expect(fileStorage.files.has(`/test/test-connector/src/get-post`)).toEqual(true);
|
|
74
79
|
});
|
|
80
|
+
it('should not generate a operation directory when failing to decode', () => {
|
|
81
|
+
expect(fileStorage.files.has(`/test/test-connector/src/update-post`)).toEqual(false);
|
|
82
|
+
});
|
|
75
83
|
});
|
|
@@ -12,9 +12,66 @@
|
|
|
12
12
|
],
|
|
13
13
|
"paths": {
|
|
14
14
|
"/posts/{id}": {
|
|
15
|
+
"put": {
|
|
16
|
+
"description": "Update a post",
|
|
17
|
+
"tags": [
|
|
18
|
+
"post"
|
|
19
|
+
],
|
|
20
|
+
"operationId": "updatePost",
|
|
21
|
+
"requestBody": {
|
|
22
|
+
"content": {
|
|
23
|
+
"application/json": {
|
|
24
|
+
"schema": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": 1,
|
|
27
|
+
"properties": {
|
|
28
|
+
"id": {
|
|
29
|
+
"type": "integer"
|
|
30
|
+
},
|
|
31
|
+
"userId": {
|
|
32
|
+
"type": "integer"
|
|
33
|
+
},
|
|
34
|
+
"title": {
|
|
35
|
+
"type": "string"
|
|
36
|
+
},
|
|
37
|
+
"body": {
|
|
38
|
+
"type": "string"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"required": true
|
|
45
|
+
},
|
|
46
|
+
"responses": {
|
|
47
|
+
"200": {
|
|
48
|
+
"description": "All went well",
|
|
49
|
+
"content": {
|
|
50
|
+
"application/json": {
|
|
51
|
+
"schema": {
|
|
52
|
+
"$ref": "#/components/schemas/Post"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"404": {
|
|
58
|
+
"description": "Post not found",
|
|
59
|
+
"content": {
|
|
60
|
+
"application/json": {
|
|
61
|
+
"schema": {
|
|
62
|
+
"type": "object",
|
|
63
|
+
"properties": {}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
15
70
|
"get": {
|
|
16
71
|
"description": "Returns a post by id",
|
|
17
|
-
"tags": [
|
|
72
|
+
"tags": [
|
|
73
|
+
"Posts"
|
|
74
|
+
],
|
|
18
75
|
"operationId": "getPost",
|
|
19
76
|
"parameters": [
|
|
20
77
|
{
|
|
@@ -57,7 +114,12 @@
|
|
|
57
114
|
},
|
|
58
115
|
"Post": {
|
|
59
116
|
"type": "object",
|
|
60
|
-
"required": [
|
|
117
|
+
"required": [
|
|
118
|
+
"id",
|
|
119
|
+
"userId",
|
|
120
|
+
"title",
|
|
121
|
+
"body"
|
|
122
|
+
],
|
|
61
123
|
"properties": {
|
|
62
124
|
"id": {
|
|
63
125
|
"type": "integer"
|
|
@@ -75,4 +137,4 @@
|
|
|
75
137
|
}
|
|
76
138
|
}
|
|
77
139
|
}
|
|
78
|
-
}
|
|
140
|
+
}
|