@trayio/tray-openapi 2.10.0 → 2.11.1

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,12 @@
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 { BuildConnectorResponse } from './OpenApiTypeDescriptors';
4
5
  export declare class OpenApiSchemaImporter {
5
6
  private generator;
6
7
  private fileStorage;
7
8
  constructor(generator: Generator, fileStorage: FileStorage);
8
- buildConnector(openApiSpecPath: string, connectorName: string): TE.TaskEither<Error, undefined>;
9
+ buildConnector(openApiSpecPath: string, connectorName: string): TE.TaskEither<Error, BuildConnectorResponse>;
9
10
  private generateOperationFromPath;
10
11
  private decodeOperation;
11
12
  private getOpenApiSpec;
@@ -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;AAyClE,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,SAAS,CAAC;IAiClC,OAAO,CAAC,yBAAyB;IA6CjC,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,cAAc;IAkCtB,OAAO,CAAC,oCAAoC;IAgF5C,OAAO,CAAC,0BAA0B;IAwBlC,OAAO,CAAC,0BAA0B;CAclC"}
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,EAEN,sBAAsB,EAMtB,MAAM,0BAA0B,CAAC;AAalC,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;IAoD/C,OAAO,CAAC,yBAAyB;IAyDjC,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,cAAc;IAoCtB,OAAO,CAAC,oCAAoC;IAgF5C,OAAO,CAAC,0BAA0B;IAwBlC,OAAO,CAAC,0BAA0B;CAclC"}
@@ -42,6 +42,7 @@ const GenerateInputTypes_1 = require("./file-generators/types/GenerateInputTypes
42
42
  const GenerateHandlerTest_1 = require("./file-generators/GenerateHandlerTest");
43
43
  const GenerateOperationJson_1 = require("./file-generators/GenerateOperationJson");
44
44
  const GenerateInputSchema_1 = require("./file-generators/types/GenerateInputSchema");
45
+ const OpenApiSchemaTransformer_1 = require("./OpenApiSchemaTransformer");
45
46
  class OpenApiSchemaImporter {
46
47
  constructor(generator, fileStorage) {
47
48
  this.generator = generator;
@@ -59,10 +60,28 @@ class OpenApiSchemaImporter {
59
60
  path: openApiSpec.paths[path][route],
60
61
  })),
61
62
  })));
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(() => undefined));
63
+ }), 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) => {
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))))(path.routes), TE.map(() => undefined));
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.fromEither((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}`)));
@@ -72,7 +91,7 @@ class OpenApiSchemaImporter {
72
91
  const resolver = new json_ref_resolver_1.Resolver({});
73
92
  const resolvedOpenApiSpec = resolver.resolve(openApiSpec, {});
74
93
  return resolvedOpenApiSpec;
75
- }, (reason) => new Error(`Failed to resolve references in schema: ${reason}`))), TE.chain((openApiSpec) => TE.right(openApiSpec.result)));
94
+ }, (reason) => new Error(`Failed to resolve references in schema: ${reason}`))), TE.chain((openApiSpec) => TE.fromEither((0, OpenApiSchemaTransformer_1.mergePathLevelParamsIntoHttpMethod)(openApiSpec.result))));
76
95
  }
77
96
  generateHandlerFilesAndOperationJson(method, path, operationPath, baseUrl, httpPath, connectorName, input, output) {
78
97
  const writeFile = (content, fileName) => this.fileStorage.write({
@@ -110,7 +129,7 @@ class OpenApiSchemaImporter {
110
129
  connectorNameKebabCase: (0, lodash_1.kebabCase)(connectorName),
111
130
  connectorNamePascalCase: StringExtensions_1.StringExtensions.pascalCase(connectorName),
112
131
  connectorNameCamelCase: (0, lodash_1.camelCase)(connectorName),
113
- }), TE.chain((parameters) => this.generator.generate(`${__dirname}/templates/connector-template.zip`, `${process.cwd()}/test`, parameters)), TE.map(() => `/test/${(0, lodash_1.kebabCase)(connectorName)}`), TE.mapLeft((error) => new Error(`Failed to generate connector directory: ${error}`)));
132
+ }), TE.chain((parameters) => this.generator.generate(`${__dirname}/templates/connector-template.zip`, process.cwd(), parameters)), TE.map(() => `/${(0, lodash_1.kebabCase)(connectorName)}`), TE.mapLeft((error) => new Error(`Failed to generate connector directory: ${error}`)));
114
133
  }
115
134
  generateOperationDirectory(connectorPath, operationName) {
116
135
  return (0, function_1.pipe)(TE.tryCatch(this.fileStorage.createDirectory(`${connectorPath}/src/${(0, lodash_1.kebabCase)(operationName)}`), (error) => new Error(`Failed to create operation directory: ${error}`)), TE.map(() => `${connectorPath}/src/${(0, lodash_1.kebabCase)(operationName)}`));
@@ -35,41 +35,51 @@ 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("./single-get-openapi.json"));
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();
44
45
  const openApiSchemaImporter = new OpenApiSchemaImporter_1.OpenApiSchemaImporter(generator, fileStorage);
45
46
  let result;
47
+ const spec = 'test-openapi-spec.json';
46
48
  beforeAll(() => __awaiter(void 0, void 0, void 0, function* () {
47
49
  fileStorage.write({
48
- key: `${__dirname}/single-get-openapi.json`,
49
- metadata: { name: 'single-get-openapi.json' },
50
+ key: `${__dirname}/${spec}`,
51
+ metadata: { name: spec },
50
52
  content: stream_1.Readable.from(Buffer.from(JSON.stringify(openApiSpec))),
51
53
  });
52
- result = yield openApiSchemaImporter.buildConnector(`${__dirname}/single-get-openapi.json`, 'testConnector')();
54
+ result = yield openApiSchemaImporter.buildConnector(`${__dirname}/${spec}`, 'testConnector')();
53
55
  }));
54
56
  afterAll(() => __awaiter(void 0, void 0, void 0, function* () {
55
57
  yield fs.rm(`${process.cwd()}/test/`, { recursive: true, force: true });
56
58
  }));
57
59
  it('should import successfully', () => {
58
- expect(result._tag).toEqual('Right');
60
+ const buildConnectorResponse = E.getOrElse((error) => {
61
+ console.error(error);
62
+ throw new Error('Should have been right');
63
+ })(result);
64
+ expect(buildConnectorResponse.successes.length).toEqual(1);
65
+ expect(buildConnectorResponse.errors.length).toEqual(1);
59
66
  });
60
67
  it('should generate a operation handler', () => {
61
- expect(fileStorage.files.has(`/test/test-connector/src/get-post/handler.ts`)).toEqual(true);
68
+ expect(fileStorage.files.has(`/test-connector/src/get-post/handler.ts`)).toEqual(true);
62
69
  });
63
70
  it('should generate an operation handler test', () => {
64
- expect(fileStorage.files.has(`/test/test-connector/src/get-post/handler.test.ts`)).toEqual(true);
71
+ expect(fileStorage.files.has(`/test-connector/src/get-post/handler.test.ts`)).toEqual(true);
65
72
  });
66
73
  it('should generate an operation.json', () => {
67
- expect(fileStorage.files.has(`/test/test-connector/src/get-post/operation.json`)).toEqual(true);
74
+ expect(fileStorage.files.has(`/test-connector/src/get-post/operation.json`)).toEqual(true);
68
75
  });
69
76
  it('should generate an output.ts', () => {
70
- expect(fileStorage.files.has(`/test/test-connector/src/get-post/output.ts`)).toEqual(true);
77
+ expect(fileStorage.files.has(`/test-connector/src/get-post/output.ts`)).toEqual(true);
71
78
  });
72
79
  it('should generate a operation directory', () => {
73
- expect(fileStorage.files.has(`/test/test-connector/src/get-post`)).toEqual(true);
80
+ expect(fileStorage.files.has(`/test-connector/src/get-post`)).toEqual(true);
81
+ });
82
+ it('should not generate a operation directory when failing to decode', () => {
83
+ expect(fileStorage.files.has(`/test-connector/src/update-post`)).toEqual(false);
74
84
  });
75
85
  });
@@ -0,0 +1,9 @@
1
+ import * as E from 'fp-ts/Either';
2
+ import { BaseOpenApiSpec } from './OpenApiTypeDescriptors';
3
+ export declare const isObject: (value: unknown) => value is Record<string, unknown>;
4
+ export declare const isObjectWithParameters: (value: unknown) => value is Record<string, unknown> & {
5
+ parameters: unknown[];
6
+ };
7
+ export declare const getMergedParameters: (pathValue: Record<string, unknown>, httpMethodValue: Record<string, unknown>) => unknown[];
8
+ export declare const mergePathLevelParamsIntoHttpMethod: (openApiSpec: BaseOpenApiSpec) => E.Either<Error, BaseOpenApiSpec>;
9
+ //# sourceMappingURL=OpenApiSchemaTransformer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenApiSchemaTransformer.d.ts","sourceRoot":"","sources":["../src/OpenApiSchemaTransformer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D,eAAO,MAAM,QAAQ,UAAW,OAAO,qCACK,CAAC;AAE7C,eAAO,MAAM,sBAAsB,UAC3B,OAAO;gBACqC,OAAO,EAAE;CACpB,CAAC;AAE1C,eAAO,MAAM,mBAAmB,cACpB,OAAO,MAAM,EAAE,OAAO,CAAC,mBACjB,OAAO,MAAM,EAAE,OAAO,CAAC,KACtC,OAAO,EAcT,CAAC;AAEF,eAAO,MAAM,kCAAkC,gBACjC,eAAe,KAC1B,EAAE,MAAM,CAAC,KAAK,EAAE,eAAe,CAkCjC,CAAC"}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.mergePathLevelParamsIntoHttpMethod = exports.getMergedParameters = exports.isObjectWithParameters = exports.isObject = void 0;
27
+ const E = __importStar(require("fp-ts/Either"));
28
+ const isObject = (value) => typeof value === 'object' && value !== null;
29
+ exports.isObject = isObject;
30
+ const isObjectWithParameters = (value) => (0, exports.isObject)(value) && 'parameters' in value;
31
+ exports.isObjectWithParameters = isObjectWithParameters;
32
+ const getMergedParameters = (pathValue, httpMethodValue) => {
33
+ if (!Array.isArray(pathValue.parameters)) {
34
+ throw new Error('Path parameters is not an array');
35
+ }
36
+ // if the method contains existing parameters, we merge them with the path parameters
37
+ if ('parameters' in httpMethodValue &&
38
+ Array.isArray(httpMethodValue.parameters)) {
39
+ return [...httpMethodValue.parameters, ...pathValue.parameters];
40
+ }
41
+ return pathValue.parameters;
42
+ };
43
+ exports.getMergedParameters = getMergedParameters;
44
+ const mergePathLevelParamsIntoHttpMethod = (openApiSpec) => {
45
+ const paths = Object.entries(openApiSpec.paths).reduce((pathAcc, [pathName, pathValue]) => {
46
+ // if the path doesn't contain top level params, we just return it
47
+ if (!(0, exports.isObjectWithParameters)(pathValue)) {
48
+ pathAcc[pathName] = pathValue;
49
+ return pathAcc;
50
+ }
51
+ const updatedPathValue = Object.entries(pathValue).reduce((methodAcc, [httpMethodName, httpMethodValue]) => {
52
+ if (!(0, exports.isObject)(httpMethodValue)) {
53
+ throw new Error('Http method value is not an object');
54
+ }
55
+ // we skip adding the parameters object to the path object
56
+ if (httpMethodName === 'parameters') {
57
+ return methodAcc;
58
+ }
59
+ const parameters = (0, exports.getMergedParameters)(pathValue, httpMethodValue);
60
+ methodAcc[httpMethodName] = Object.assign(Object.assign({}, httpMethodValue), { parameters });
61
+ return methodAcc;
62
+ }, {});
63
+ pathAcc[pathName] = updatedPathValue;
64
+ return pathAcc;
65
+ }, {});
66
+ return E.right(Object.assign(Object.assign({}, openApiSpec), { paths }));
67
+ };
68
+ exports.mergePathLevelParamsIntoHttpMethod = mergePathLevelParamsIntoHttpMethod;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=OpenApiSchemaTransformer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenApiSchemaTransformer.test.d.ts","sourceRoot":"","sources":["../src/OpenApiSchemaTransformer.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ const E = __importStar(require("fp-ts/Either"));
27
+ const OpenApiSchemaTransformer_1 = require("./OpenApiSchemaTransformer");
28
+ describe('mergePathLevelParamsIntoHttpMethod', () => {
29
+ it('should merge path level parameters into HTTP methods', () => {
30
+ const openApiSpec = {
31
+ openapi: '3.0.0',
32
+ info: {
33
+ version: '1.0.0',
34
+ title: 'JSON Placeholder API',
35
+ description: 'See https://jsonplaceholder.typicode.com/',
36
+ },
37
+ servers: [
38
+ {
39
+ url: 'https://jsonplaceholder.typicode.com',
40
+ },
41
+ ],
42
+ paths: {
43
+ '/path': {
44
+ parameters: [{ name: 'param1', in: 'query', required: false }],
45
+ get: {
46
+ parameters: [{ name: 'param2', in: 'query', required: true }],
47
+ },
48
+ },
49
+ },
50
+ };
51
+ const result = (0, OpenApiSchemaTransformer_1.mergePathLevelParamsIntoHttpMethod)(openApiSpec);
52
+ E.fold((error) => {
53
+ throw new Error(`Test failed with error: ${error.message}`);
54
+ }, (updatedOpenApiSpec) => {
55
+ expect(updatedOpenApiSpec.paths['/path'].get.parameters).toEqual([
56
+ { name: 'param2', in: 'query', required: true },
57
+ { name: 'param1', in: 'query', required: false },
58
+ ]);
59
+ })(result);
60
+ });
61
+ it('should return the original spec if no top level parameters', () => {
62
+ const openApiSpec = {
63
+ openapi: '3.0.0',
64
+ info: {
65
+ version: '1.0.0',
66
+ title: 'JSON Placeholder API',
67
+ description: 'See https://jsonplaceholder.typicode.com/',
68
+ },
69
+ servers: [
70
+ {
71
+ url: 'https://jsonplaceholder.typicode.com',
72
+ },
73
+ ],
74
+ paths: {
75
+ '/path': {
76
+ get: {
77
+ parameters: [{ name: 'param2', in: 'query', required: true }],
78
+ },
79
+ },
80
+ },
81
+ };
82
+ const result = (0, OpenApiSchemaTransformer_1.mergePathLevelParamsIntoHttpMethod)(openApiSpec);
83
+ E.fold((error) => {
84
+ throw new Error(`Test failed with error: ${error.message}`);
85
+ }, (updatedOpenApiSpec) => {
86
+ expect(updatedOpenApiSpec).toEqual(openApiSpec);
87
+ })(result);
88
+ });
89
+ it('should add path level parameters into HTTP methods, if no method params already exist', () => {
90
+ const openApiSpec = {
91
+ openapi: '3.0.0',
92
+ info: {
93
+ version: '1.0.0',
94
+ title: 'JSON Placeholder API',
95
+ description: 'See https://jsonplaceholder.typicode.com/',
96
+ },
97
+ servers: [
98
+ {
99
+ url: 'https://jsonplaceholder.typicode.com',
100
+ },
101
+ ],
102
+ paths: {
103
+ '/path': {
104
+ parameters: [{ name: 'param1', in: 'query', required: false }],
105
+ get: {},
106
+ },
107
+ },
108
+ };
109
+ const result = (0, OpenApiSchemaTransformer_1.mergePathLevelParamsIntoHttpMethod)(openApiSpec);
110
+ E.fold((error) => {
111
+ throw new Error(`Test failed with error: ${error.message}`);
112
+ }, (updatedOpenApiSpec) => {
113
+ expect(updatedOpenApiSpec.paths['/path'].get.parameters).toEqual([
114
+ { name: 'param1', in: 'query', required: false },
115
+ ]);
116
+ })(result);
117
+ });
118
+ });
@@ -1,5 +1,41 @@
1
1
  import * as t from 'io-ts';
2
2
  import * as O from 'fp-ts/Option';
3
+ export type FlatPath = {
4
+ path: string;
5
+ routes: {
6
+ method: OpenApiHttpMethod;
7
+ path: unknown;
8
+ }[];
9
+ };
10
+ export type Server = {
11
+ url: string;
12
+ };
13
+ export type BaseOpenApiSpec = {
14
+ openapi: string;
15
+ info: {
16
+ version: string;
17
+ title: string;
18
+ description: string;
19
+ };
20
+ servers: Server[];
21
+ paths: Record<string, Record<string, unknown>>;
22
+ };
23
+ export type BaseOperation = {
24
+ httpMethod: OpenApiHttpMethod;
25
+ path: string;
26
+ };
27
+ export type OperationError = BaseOperation & {
28
+ tag: 'error';
29
+ errorMessage: string;
30
+ };
31
+ export type OperationSuccess = BaseOperation & {
32
+ tag: 'success';
33
+ };
34
+ export type Operation = OperationError | OperationSuccess;
35
+ export type BuildConnectorResponse = {
36
+ successes: OperationSuccess[];
37
+ errors: OperationError[];
38
+ };
3
39
  export type Path = {
4
40
  description: string;
5
41
  tags: O.Option<string[]>;
@@ -12,12 +48,8 @@ export type Parameter = {
12
48
  name: string;
13
49
  in: string;
14
50
  description: string;
15
- required: boolean;
16
- schema: {
17
- type: string;
18
- format: O.Option<string>;
19
- items: O.Option<SchemaObject>;
20
- };
51
+ required: O.Option<boolean>;
52
+ schema: SchemaObject;
21
53
  };
22
54
  export type RequestBody = {
23
55
  content: Record<string, MediaTypeObject>;
@@ -1 +1 @@
1
- {"version":3,"file":"OpenApiTypeDescriptors.d.ts","sourceRoot":"","sources":["../src/OpenApiTypeDescriptors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OpenApiTypeDescriptors.d.ts","sourceRoot":"","sources":["../src/OpenApiTypeDescriptors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,OAAO,CAAC;AAE3B,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAKlC,MAAM,MAAM,QAAQ,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,MAAM,EAAE,iBAAiB,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC3B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG;IAC5C,GAAG,EAAE,OAAO,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG;IAC9C,GAAG,EAAE,SAAS,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,cAAc,GAAG,gBAAgB,CAAC;AAE1D,MAAM,MAAM,sBAAsB,GAAG;IACpC,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,MAAM,EAAE,cAAc,EAAE,CAAC;CACzB,CAAC;AAIF,MAAM,MAAM,IAAI,GAAG;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAClC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACnC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5B,MAAM,EAAE,YAAY,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CACzC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;CAC/B;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAEjD,MAAM,MAAM,QAAQ,GAAG;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE5E,KAAK,eAAe,GAAG;IACtB,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,OAAO,EAAE;QACR,GAAG,EAAE,MAAM,CAAC;KACZ,EAAE,CAAC;IACJ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAChE,UAAU,EAAE,eAAe,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;AAEpE,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IAC1D,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,YAAY,CAAC;CACpB;AAED,MAAM,MAAM,wBAAwB,GACjC,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,SAAS,CAAC;AAEb,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC7D,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC;CACzC;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC9B,oBAAoB,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,GAAG,YAAY,CAAC,CAAC;IACvD,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IACnD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;IAChC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;IAChC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;IAChC,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAC5B,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;CACrB,CAAC;AAYF,eAAO,MAAM,oCAAoC,EAAE,CAAC,CAAC,IAAI,CACxD,gBAAgB,EAChB,OAAO,CAgBP,CAAC;AAyBF,eAAO,MAAM,0BAA0B,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAMnE,CAAC;AAEH,eAAO,MAAM,qBAAqB,wDAEjC,CAAC;AAuBF,eAAO,MAAM,6BAA6B,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAQpE,CAAC;AAEF,eAAO,MAAM,6BAA6B,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAO9D,CAAC;AAUH,eAAO,MAAM,yBAAyB,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAWpE,CAAC"}
@@ -27,7 +27,7 @@ exports.openApiSpecTypeDescriptor = exports.openApiSpecPathTypeDescriptor = expo
27
27
  const t = __importStar(require("io-ts"));
28
28
  const io_ts_types_1 = require("io-ts-types");
29
29
  const TypeCodec_1 = require("@trayio/commons/codec/TypeCodec");
30
- // Codecs
30
+ // Type Descriptors
31
31
  const nonArraySchemaObjectType = t.keyof({
32
32
  boolean: null,
33
33
  object: null,
@@ -61,10 +61,7 @@ exports.schemaObjectTypeDescriptor = t.recursion('schemaObject', () => t.union([
61
61
  arraySchemaObjectSchemaTypeDescriptor,
62
62
  nonArraySchemaObjectSchemaTypeDescriptor,
63
63
  ]));
64
- exports.schemaObjectTypeCodec = TypeCodec_1.TypeCodec.fromDescriptor(t.recursion('schemaObject', () => t.union([
65
- arraySchemaObjectSchemaTypeDescriptor,
66
- nonArraySchemaObjectSchemaTypeDescriptor,
67
- ])));
64
+ exports.schemaObjectTypeCodec = TypeCodec_1.TypeCodec.fromDescriptor(exports.schemaObjectTypeDescriptor);
68
65
  const componentSchemaTypeDescriptor = t.type({
69
66
  schemas: (0, io_ts_types_1.optionFromNullable)(t.record(t.string, exports.schemaObjectTypeDescriptor)),
70
67
  });
@@ -82,12 +79,8 @@ exports.parameterSchemaTypeDescriptor = t.type({
82
79
  name: t.string,
83
80
  in: t.string,
84
81
  description: t.string,
85
- required: t.boolean,
86
- schema: t.type({
87
- type: t.string,
88
- format: (0, io_ts_types_1.optionFromNullable)(t.string),
89
- items: (0, io_ts_types_1.optionFromNullable)(exports.schemaObjectTypeDescriptor),
90
- }),
82
+ required: (0, io_ts_types_1.optionFromNullable)(t.boolean),
83
+ schema: exports.schemaObjectTypeDescriptor,
91
84
  });
92
85
  exports.openApiSpecPathTypeDescriptor = t.type({
93
86
  description: t.string,
@@ -1,3 +1,4 @@
1
+ import * as E from 'fp-ts/Either';
1
2
  import { Path, SchemaObject } from '../../OpenApiTypeDescriptors';
2
- export declare const generateInputSchema: (endpoint: Path) => SchemaObject;
3
+ export declare const generateInputSchema: (endpoint: Path) => E.Either<Error, SchemaObject>;
3
4
  //# sourceMappingURL=GenerateInputSchema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GenerateInputSchema.d.ts","sourceRoot":"","sources":["../../../src/file-generators/types/GenerateInputSchema.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,IAAI,EAGJ,YAAY,EAEZ,MAAM,8BAA8B,CAAC;AA8HtC,eAAO,MAAM,mBAAmB,aAAc,IAAI,KAAG,YAiCpD,CAAC"}
1
+ {"version":3,"file":"GenerateInputSchema.d.ts","sourceRoot":"","sources":["../../../src/file-generators/types/GenerateInputSchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAQlC,OAAO,EACN,IAAI,EAGJ,YAAY,EAEZ,MAAM,8BAA8B,CAAC;AAyItC,eAAO,MAAM,mBAAmB,aACrB,IAAI,KACZ,EAAE,MAAM,CAAC,KAAK,EAAE,YAAY,CAiC9B,CAAC"}
@@ -24,6 +24,8 @@ var __importStar = (this && this.__importStar) || function (mod) {
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.generateInputSchema = void 0;
27
+ const E = __importStar(require("fp-ts/Either"));
28
+ const function_1 = require("fp-ts/function");
27
29
  const lodash_1 = require("lodash");
28
30
  const O = __importStar(require("fp-ts/Option"));
29
31
  const openapi_schema_to_json_schema_1 = require("@openapi-contrib/openapi-schema-to-json-schema");
@@ -62,7 +64,7 @@ const transformInput = (parameters) => {
62
64
  throw new Error('Failed to decode params');
63
65
  }
64
66
  transformedParameters[parameter.name] = decodedParam.right;
65
- if (parameter.required === true) {
67
+ if (O.isSome(parameter.required) && parameter.required.value === true) {
66
68
  requiredParameters.push(parameter.name);
67
69
  }
68
70
  });
@@ -85,49 +87,42 @@ const appendInPropertyToBody = (body) => O.fold(() => body, (properties) => {
85
87
  const propsWithInBody = Object.keys(properties).reduce((acc, name) => (Object.assign(Object.assign({}, acc), { [name]: Object.assign(Object.assign({}, properties[name]), { in: O.some('body') }) })), {});
86
88
  return Object.assign(Object.assign({}, body), { properties: O.some(propsWithInBody) });
87
89
  })(body.properties);
90
+ const blankOutputSchema = () => ({
91
+ type: O.none,
92
+ properties: O.none,
93
+ required: O.none,
94
+ additionalProperties: O.some(false),
95
+ allOf: O.none,
96
+ anyOf: O.none,
97
+ oneOf: O.none,
98
+ not: O.none,
99
+ in: O.none,
100
+ });
88
101
  const getApplicationJsonMediaType = (content) => {
102
+ if (!Object.keys(content).length)
103
+ return E.right({ schema: O.none });
89
104
  // TODO: we should support any value here, but there may also be more than 1 media type so we need to iterate through
90
105
  // and combine all the schemas within into a single set of inputs
91
106
  const mediaTypeApplicationJson = Object.keys(content).find((mediaType) => /^application\/.*json$/.test(mediaType));
92
- // TODO: fold here instead of getOrElse
93
107
  if (mediaTypeApplicationJson) {
94
- return O.getOrElse(() => {
95
- throw new Error('');
96
- })(content[mediaTypeApplicationJson].schema);
108
+ return E.right(content[mediaTypeApplicationJson]);
97
109
  }
98
110
  const mediaTypes = Object.keys(content);
99
- throw new Error(`Unsupported media type: ${mediaTypes}`);
111
+ return E.left(new Error(`Unsupported media type: ${mediaTypes}`));
100
112
  };
101
113
  const generateInputSchema = (endpoint) => {
102
- // TODO: use the encode method instead of this, or fold over the values
103
114
  const parameters = O.toNullable(endpoint.parameters);
104
115
  const requestBody = O.toNullable(endpoint.requestBody);
105
116
  if (parameters && !requestBody) {
106
- return transformInput(parameters);
117
+ return E.right(transformInput(parameters));
107
118
  }
108
119
  if (requestBody && !parameters) {
109
- // TODO: update all uses to this now that getAppJsonMedia doesn't do get content within it? return getApplicationJsonMediaType(getContent(requestBody.content));
110
- const body = getApplicationJsonMediaType(requestBody.content);
111
- return appendInPropertyToBody(body);
112
- // return getApplicationJsonMediaType(getContent(requestBody.content));
120
+ return (0, function_1.pipe)(getApplicationJsonMediaType(requestBody.content), E.map((mediaTypeObject) => O.fold(() => blankOutputSchema(), (schema) => appendInPropertyToBody(schema))(mediaTypeObject.schema)));
113
121
  }
114
122
  if (requestBody && parameters) {
115
123
  const params = transformInput(parameters);
116
- const body = getApplicationJsonMediaType(requestBody.content);
117
- const bodyWithInProperty = appendInPropertyToBody(body);
118
- const mergedParamsAndBody = mergeParamsAndBody(params, bodyWithInProperty);
119
- return mergedParamsAndBody;
124
+ return (0, function_1.pipe)(getApplicationJsonMediaType(requestBody.content), E.map((mediaTypeObject) => O.fold(() => blankOutputSchema(), (schema) => appendInPropertyToBody(schema))(mediaTypeObject.schema)), E.chain((schema) => E.right(mergeParamsAndBody(params, schema))));
120
125
  }
121
- return {
122
- type: O.none,
123
- properties: O.none,
124
- required: O.none,
125
- additionalProperties: O.some(false),
126
- allOf: O.none,
127
- anyOf: O.none,
128
- oneOf: O.none,
129
- not: O.none,
130
- in: O.none,
131
- };
126
+ return E.right(blankOutputSchema());
132
127
  };
133
128
  exports.generateInputSchema = generateInputSchema;
@@ -32,6 +32,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
32
32
  });
33
33
  };
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
+ const E = __importStar(require("fp-ts/Either"));
35
36
  const TE = __importStar(require("fp-ts/TaskEither"));
36
37
  const O = __importStar(require("fp-ts/Option"));
37
38
  const GenerateInputSchema_1 = require("./GenerateInputSchema");
@@ -46,12 +47,38 @@ describe('GenerateInputSchema', () => {
46
47
  {
47
48
  name: 'id',
48
49
  in: 'path',
49
- required: true,
50
+ required: O.some(true),
50
51
  description: 'The user id.',
51
52
  schema: {
52
- type: 'integer',
53
- format: O.some('int64'),
53
+ type: O.some('integer'),
54
+ properties: O.none,
55
+ additionalProperties: O.some(false),
56
+ required: O.none,
57
+ allOf: O.none,
58
+ anyOf: O.none,
59
+ oneOf: O.none,
60
+ not: O.none,
61
+ in: O.none,
62
+ },
63
+ additionalProperties: O.none,
64
+ },
65
+ {
66
+ name: 'date',
67
+ in: 'query',
68
+ required: O.none,
69
+ description: 'The date of the post.',
70
+ schema: {
71
+ type: O.some('string'),
72
+ properties: O.none,
73
+ additionalProperties: O.some(false),
74
+ format: O.none,
54
75
  items: O.none,
76
+ required: O.none,
77
+ allOf: O.none,
78
+ anyOf: O.none,
79
+ oneOf: O.none,
80
+ not: O.none,
81
+ in: O.none,
55
82
  },
56
83
  additionalProperties: O.none,
57
84
  },
@@ -59,7 +86,9 @@ describe('GenerateInputSchema', () => {
59
86
  requestBody: O.none,
60
87
  responses: O.none,
61
88
  };
62
- const inputSchema = (0, GenerateInputSchema_1.generateInputSchema)(endpoint);
89
+ const inputSchema = E.getOrElse(() => {
90
+ throw new Error('Should have been right');
91
+ })((0, GenerateInputSchema_1.generateInputSchema)(endpoint));
63
92
  const inputTypes = (0, GenerateInputTypes_1.generateInputTypes)(endpoint.operationId, inputSchema);
64
93
  const generatedInputFile = yield TE.getOrElse((error) => {
65
94
  throw new Error('Should have been right');
@@ -69,6 +98,10 @@ describe('GenerateInputSchema', () => {
69
98
  * @description The user id.
70
99
  */
71
100
  id: number;
101
+ /**
102
+ * @description The date of the post.
103
+ */
104
+ date?: string;
72
105
  }
73
106
  `;
74
107
  expect(generatedInputFile).toEqual(expectedInputFile);
@@ -143,7 +176,9 @@ describe('GenerateInputSchema', () => {
143
176
  }),
144
177
  responses: O.none,
145
178
  };
146
- const inputSchema = (0, GenerateInputSchema_1.generateInputSchema)(endpoint);
179
+ const inputSchema = E.getOrElse(() => {
180
+ throw new Error('Should have been right');
181
+ })((0, GenerateInputSchema_1.generateInputSchema)(endpoint));
147
182
  const inputTypes = (0, GenerateInputTypes_1.generateInputTypes)(endpoint.operationId, inputSchema);
148
183
  const generatedInputFile = yield TE.getOrElse((error) => {
149
184
  throw new Error('Should have been right');
@@ -166,12 +201,12 @@ describe('GenerateInputSchema', () => {
166
201
  {
167
202
  name: 'tags',
168
203
  in: 'query',
169
- required: true,
204
+ required: O.some(true),
170
205
  description: 'tags to filter by.',
171
206
  schema: {
172
207
  type: 'array',
173
208
  format: O.none,
174
- items: O.some({
209
+ items: {
175
210
  type: O.some('string'),
176
211
  properties: O.none,
177
212
  additionalProperties: O.some(false),
@@ -181,7 +216,15 @@ describe('GenerateInputSchema', () => {
181
216
  oneOf: O.none,
182
217
  not: O.none,
183
218
  in: O.none,
184
- }),
219
+ },
220
+ properties: O.none,
221
+ additionalProperties: O.some(false),
222
+ required: O.none,
223
+ allOf: O.none,
224
+ anyOf: O.none,
225
+ oneOf: O.none,
226
+ not: O.none,
227
+ in: O.none,
185
228
  },
186
229
  additionalProperties: O.none,
187
230
  },
@@ -189,7 +232,9 @@ describe('GenerateInputSchema', () => {
189
232
  requestBody: O.none,
190
233
  responses: O.none,
191
234
  };
192
- const inputSchema = (0, GenerateInputSchema_1.generateInputSchema)(endpoint);
235
+ const inputSchema = E.getOrElse(() => {
236
+ throw new Error('Should have been right');
237
+ })((0, GenerateInputSchema_1.generateInputSchema)(endpoint));
193
238
  const inputTypes = (0, GenerateInputTypes_1.generateInputTypes)(endpoint.operationId, inputSchema);
194
239
  const generatedInputFile = yield TE.getOrElse((error) => {
195
240
  throw new Error('Should have been right');
@@ -32,6 +32,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
32
32
  });
33
33
  };
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
+ const E = __importStar(require("fp-ts/Either"));
35
36
  const TE = __importStar(require("fp-ts/TaskEither"));
36
37
  const O = __importStar(require("fp-ts/Option"));
37
38
  const GenerateInputSchema_1 = require("./GenerateInputSchema");
@@ -107,7 +108,9 @@ describe('GenerateInputType', () => {
107
108
  }),
108
109
  responses: O.none,
109
110
  };
110
- const inputSchema = (0, GenerateInputSchema_1.generateInputSchema)(endpoint);
111
+ const inputSchema = E.getOrElse(() => {
112
+ throw new Error('Should have been right');
113
+ })((0, GenerateInputSchema_1.generateInputSchema)(endpoint));
111
114
  const expectedInputSchema = {
112
115
  type: O.some('object'),
113
116
  required: O.some(['id', 'userId', 'title', 'body']),
@@ -175,23 +178,37 @@ describe('GenerateInputType', () => {
175
178
  {
176
179
  name: 'id',
177
180
  in: 'path',
178
- required: true,
181
+ required: O.some(true),
179
182
  description: 'The user id.',
180
183
  schema: {
181
- type: 'integer',
184
+ type: O.some('integer'),
182
185
  format: O.some('int64'),
183
- items: O.none,
186
+ properties: O.none,
187
+ additionalProperties: O.some(false),
188
+ required: O.none,
189
+ allOf: O.none,
190
+ anyOf: O.none,
191
+ oneOf: O.none,
192
+ not: O.none,
193
+ in: O.none,
184
194
  },
185
195
  },
186
196
  {
187
197
  name: 'categoryId',
188
198
  in: 'path',
189
- required: false,
199
+ required: O.some(false),
190
200
  description: 'The category id.',
191
201
  schema: {
192
- type: 'integer',
202
+ type: O.some('integer'),
193
203
  format: O.some('int64'),
194
- items: O.none,
204
+ properties: O.none,
205
+ additionalProperties: O.some(false),
206
+ required: O.none,
207
+ allOf: O.none,
208
+ anyOf: O.none,
209
+ oneOf: O.none,
210
+ not: O.none,
211
+ in: O.none,
195
212
  },
196
213
  },
197
214
  ]),
@@ -248,7 +265,9 @@ describe('GenerateInputType', () => {
248
265
  }),
249
266
  responses: O.none,
250
267
  };
251
- const inputSchema = (0, GenerateInputSchema_1.generateInputSchema)(endpoint);
268
+ const inputSchema = E.getOrElse(() => {
269
+ throw new Error('Should have been right');
270
+ })((0, GenerateInputSchema_1.generateInputSchema)(endpoint));
252
271
  const inputTypes = (0, GenerateInputTypes_1.generateInputTypes)(endpoint.operationId, inputSchema);
253
272
  const generatedInputFile = yield TE.getOrElse((error) => {
254
273
  throw new Error('Should have been right');
@@ -278,7 +297,9 @@ describe('GenerateInputType', () => {
278
297
  requestBody: O.none,
279
298
  responses: O.none,
280
299
  };
281
- const inputSchema = (0, GenerateInputSchema_1.generateInputSchema)(endpoint);
300
+ const inputSchema = E.getOrElse(() => {
301
+ throw new Error('Should have been right');
302
+ })((0, GenerateInputSchema_1.generateInputSchema)(endpoint));
282
303
  const inputTypes = (0, GenerateInputTypes_1.generateInputTypes)(endpoint.operationId, inputSchema);
283
304
  const generatedInputFile = yield TE.getOrElse((error) => {
284
305
  throw new Error('Should have been right');
@@ -312,7 +333,9 @@ describe('GenerateInputType', () => {
312
333
  }),
313
334
  responses: O.none,
314
335
  };
315
- const inputSchema = (0, GenerateInputSchema_1.generateInputSchema)(endpoint);
336
+ const inputSchema = E.getOrElse(() => {
337
+ throw new Error('Should have been right');
338
+ })((0, GenerateInputSchema_1.generateInputSchema)(endpoint));
316
339
  const inputTypes = (0, GenerateInputTypes_1.generateInputTypes)(endpoint.operationId, inputSchema);
317
340
  const generatedInputFile = yield TE.getOrElse((error) => {
318
341
  throw new Error('Should have been right');
@@ -393,8 +416,10 @@ describe('GenerateInputType', () => {
393
416
  }),
394
417
  responses: O.none,
395
418
  };
396
- yield expect(() => __awaiter(void 0, void 0, void 0, function* () {
397
- (0, GenerateInputSchema_1.generateInputSchema)(endpoint);
398
- })).rejects.toThrow('Unsupported media type: unsupported-media-type');
419
+ const inputSchema = (0, GenerateInputSchema_1.generateInputSchema)(endpoint);
420
+ if (E.isRight(inputSchema)) {
421
+ throw new Error('Should have been left');
422
+ }
423
+ expect(inputSchema.left.message).toEqual('Unsupported media type: unsupported-media-type');
399
424
  }));
400
425
  });
@@ -1 +1 @@
1
- {"version":3,"file":"GenerateOutput.d.ts","sourceRoot":"","sources":["../../../src/file-generators/types/GenerateOutput.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAKlC,OAAO,EACN,SAAS,EAET,eAAe,EACf,YAAY,EAEZ,MAAM,8BAA8B,CAAC;AAuCtC,eAAO,MAAM,2BAA2B,YAC9B,OAAO,MAAM,EAAE,eAAe,CAAC,KACtC,EAAE,MAAM,CAAC,KAAK,EAAE,eAAe,CAWjC,CAAC;AAEF,eAAO,MAAM,oBAAoB,cACrB,EAAE,MAAM,CAAC,SAAS,CAAC,KAC5B,GAAG,UAAU,CAAC,KAAK,EAAE,YAAY,CAelC,CAAC;AAWH,eAAO,MAAM,mBAAmB,2BACP,MAAM,gBAChB,YAAY,KACxB,GAAG,UAAU,CAAC,KAAK,EAAE,MAAM,CAe7B,CAAC"}
1
+ {"version":3,"file":"GenerateOutput.d.ts","sourceRoot":"","sources":["../../../src/file-generators/types/GenerateOutput.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAKlC,OAAO,EACN,SAAS,EAET,eAAe,EACf,YAAY,EAEZ,MAAM,8BAA8B,CAAC;AAuCtC,eAAO,MAAM,2BAA2B,YAC9B,OAAO,MAAM,EAAE,eAAe,CAAC,KACtC,EAAE,MAAM,CAAC,KAAK,EAAE,eAAe,CAWjC,CAAC;AAEF,eAAO,MAAM,oBAAoB,cACrB,EAAE,MAAM,CAAC,SAAS,CAAC,KAC5B,GAAG,UAAU,CAAC,KAAK,EAAE,YAAY,CAelC,CAAC;AAWH,eAAO,MAAM,mBAAmB,2BACP,MAAM,gBAChB,YAAY,KACxB,GAAG,UAAU,CAAC,KAAK,EAAE,MAAM,CAY7B,CAAC"}
@@ -72,9 +72,6 @@ const encodeOutputSchema = (outputSchema) => {
72
72
  };
73
73
  const generateOutputTypes = (operationNameCamelCase, outputSchema) => {
74
74
  const resolvedOutputSchema = encodeOutputSchema(outputSchema);
75
- return TE.tryCatch(() => (0, json_schema_to_typescript_1.compile)(resolvedOutputSchema, `${operationNameCamelCase}Output`, JsonSchemaToTypescriptOptions_1.JsonSchemaToTypescriptOptions), (error) => {
76
- console.log(JSON.stringify(resolvedOutputSchema));
77
- return new Error(`Failed to generate output types: ${error}`);
78
- });
75
+ return TE.tryCatch(() => (0, json_schema_to_typescript_1.compile)(resolvedOutputSchema, `${operationNameCamelCase}Output`, JsonSchemaToTypescriptOptions_1.JsonSchemaToTypescriptOptions), (error) => new Error(`Failed to generate output types: ${error}`));
79
76
  };
80
77
  exports.generateOutputTypes = generateOutputTypes;
@@ -0,0 +1,160 @@
1
+ {
2
+ "openapi": "3.0.0",
3
+ "info": {
4
+ "version": "1.0.0",
5
+ "title": "JSON Placeholder API",
6
+ "description": "See https://jsonplaceholder.typicode.com/"
7
+ },
8
+ "servers": [
9
+ {
10
+ "url": "https://jsonplaceholder.typicode.com"
11
+ }
12
+ ],
13
+ "paths": {
14
+ "/posts/{id}/{uuid}": {
15
+ "parameters": [
16
+ {
17
+ "name": "id",
18
+ "in": "path",
19
+ "required": true,
20
+ "description": "The user id.",
21
+ "schema": {
22
+ "type": "integer",
23
+ "format": "int64"
24
+ },
25
+ "additionalProperties": false
26
+ },
27
+ {
28
+ "in": "path",
29
+ "name": "uuid",
30
+ "description": "UUID.",
31
+ "required": true,
32
+ "schema": {
33
+ "allOf": [
34
+ {
35
+ "title": "UUID Property",
36
+ "description": "A string that has to be a format matching the industry standard uuid",
37
+ "type": "string",
38
+ "format": "uuid",
39
+ "example": "04ca6ae2-0dc3-487b-953e-86d6abbdf7d3"
40
+ },
41
+ {
42
+ "example": "b054dd32-efb6-444a-b4a6-a797a18315ef"
43
+ }
44
+ ]
45
+ }
46
+ }
47
+ ],
48
+ "put": {
49
+ "tags": ["post"],
50
+ "requestBody": {
51
+ "content": {
52
+ "application/json": {
53
+ "schema": {
54
+ "type": "object",
55
+ "required": ["id", "userId", "title", "body"],
56
+ "properties": {
57
+ "id": {
58
+ "type": "integer"
59
+ },
60
+ "userId": {
61
+ "type": "integer"
62
+ },
63
+ "title": {
64
+ "type": "string"
65
+ },
66
+ "body": {
67
+ "type": "string"
68
+ }
69
+ }
70
+ }
71
+ }
72
+ },
73
+ "required": true
74
+ },
75
+ "responses": {
76
+ "200": {
77
+ "description": "All went well",
78
+ "content": {
79
+ "application/json": {
80
+ "schema": {
81
+ "$ref": "#/components/schemas/Post"
82
+ }
83
+ }
84
+ }
85
+ },
86
+ "404": {
87
+ "description": "Post not found",
88
+ "content": {
89
+ "application/json": {
90
+ "schema": {
91
+ "type": "object",
92
+ "properties": {}
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ },
99
+ "get": {
100
+ "description": "Returns a post by id",
101
+ "tags": ["Posts"],
102
+ "operationId": "getPost",
103
+ "parameters": [
104
+ {
105
+ "name": "date",
106
+ "in": "query",
107
+ "description": "The post date.",
108
+ "schema": {
109
+ "type": "string"
110
+ },
111
+ "additionalProperties": false
112
+ }
113
+ ],
114
+ "responses": {
115
+ "200": {
116
+ "description": "Successful response",
117
+ "content": {
118
+ "application/json": {
119
+ "schema": {
120
+ "$ref": "#/components/schemas/PostsList"
121
+ }
122
+ }
123
+ }
124
+ },
125
+ "404": {
126
+ "description": "Post not found"
127
+ }
128
+ }
129
+ }
130
+ }
131
+ },
132
+ "components": {
133
+ "schemas": {
134
+ "PostsList": {
135
+ "type": "array",
136
+ "items": {
137
+ "$ref": "#/components/schemas/Post"
138
+ }
139
+ },
140
+ "Post": {
141
+ "type": "object",
142
+ "required": ["id", "userId", "title", "body"],
143
+ "properties": {
144
+ "id": {
145
+ "type": "integer"
146
+ },
147
+ "userId": {
148
+ "type": "integer"
149
+ },
150
+ "title": {
151
+ "type": "string"
152
+ },
153
+ "body": {
154
+ "type": "string"
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trayio/tray-openapi",
3
- "version": "2.10.0",
3
+ "version": "2.11.1",
4
4
  "description": "Creating CDK Projects from OpenAPI 3.0 Schemas",
5
5
  "exports": {
6
6
  "./*": "./dist/*.js"
@@ -1,78 +0,0 @@
1
- {
2
- "openapi": "3.0.0",
3
- "info": {
4
- "version": "1.0.0",
5
- "title": "JSON Placeholder API",
6
- "description": "See https://jsonplaceholder.typicode.com/"
7
- },
8
- "servers": [
9
- {
10
- "url": "https://jsonplaceholder.typicode.com"
11
- }
12
- ],
13
- "paths": {
14
- "/posts/{id}": {
15
- "get": {
16
- "description": "Returns a post by id",
17
- "tags": ["Posts"],
18
- "operationId": "getPost",
19
- "parameters": [
20
- {
21
- "name": "id",
22
- "in": "path",
23
- "required": true,
24
- "description": "The user id.",
25
- "schema": {
26
- "type": "integer",
27
- "format": "int64"
28
- },
29
- "additionalProperties": false
30
- }
31
- ],
32
- "responses": {
33
- "200": {
34
- "description": "Successful response",
35
- "content": {
36
- "application/json": {
37
- "schema": {
38
- "$ref": "#/components/schemas/PostsList"
39
- }
40
- }
41
- }
42
- },
43
- "404": {
44
- "description": "Post not found"
45
- }
46
- }
47
- }
48
- }
49
- },
50
- "components": {
51
- "schemas": {
52
- "PostsList": {
53
- "type": "array",
54
- "items": {
55
- "$ref": "#/components/schemas/Post"
56
- }
57
- },
58
- "Post": {
59
- "type": "object",
60
- "required": ["id", "userId", "title", "body"],
61
- "properties": {
62
- "id": {
63
- "type": "integer"
64
- },
65
- "userId": {
66
- "type": "integer"
67
- },
68
- "title": {
69
- "type": "string"
70
- },
71
- "body": {
72
- "type": "string"
73
- }
74
- }
75
- }
76
- }
77
- }
78
- }