vls-openapi-generator 1.14.0 → 2.0.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/.eslintcache ADDED
@@ -0,0 +1 @@
1
+ [{"/__w/vls-packages/vls-packages/.commitlintrc.js":"1","/__w/vls-packages/vls-packages/dist/generate-openapi.js":"2","/__w/vls-packages/vls-packages/dist/index.js":"3","/__w/vls-packages/vls-packages/dist/lambda-type.js":"4","/__w/vls-packages/vls-packages/dist/openapi-type.js":"5","/__w/vls-packages/vls-packages/dist/postman-service.js":"6","/__w/vls-packages/vls-packages/dist/upload-openapi.js":"7","/__w/vls-packages/vls-packages/eslint.config.mjs":"8","/__w/vls-packages/vls-packages/release.config.js":"9","/__w/vls-packages/vls-packages/src/generate-openapi.ts":"10","/__w/vls-packages/vls-packages/src/index.ts":"11","/__w/vls-packages/vls-packages/src/lambda-type.ts":"12","/__w/vls-packages/vls-packages/src/openapi-type.ts":"13","/__w/vls-packages/vls-packages/src/postman-service.ts":"14","/__w/vls-packages/vls-packages/src/upload-openapi.ts":"15"},{"size":103,"mtime":1767342111724,"results":"16","hashOfConfig":"17"},{"size":9432,"mtime":1767342117884,"results":"18","hashOfConfig":"17"},{"size":530,"mtime":1767342117932,"results":"19","hashOfConfig":"17"},{"size":77,"mtime":1767342117844,"results":"20","hashOfConfig":"17"},{"size":77,"mtime":1767342117848,"results":"21","hashOfConfig":"17"},{"size":3073,"mtime":1767342117900,"results":"22","hashOfConfig":"17"},{"size":5361,"mtime":1767342117928,"results":"23","hashOfConfig":"17"},{"size":1247,"mtime":1767342111724,"results":"24","hashOfConfig":"17"},{"size":414,"mtime":1767342111728,"results":"25","hashOfConfig":"17"},{"size":9628,"mtime":1767342111728,"results":"26","hashOfConfig":"27"},{"size":441,"mtime":1767342111728,"results":"28","hashOfConfig":"27"},{"size":90,"mtime":1767342111728,"results":"29","hashOfConfig":"27"},{"size":1630,"mtime":1767342111728,"results":"30","hashOfConfig":"27"},{"size":3666,"mtime":1767342111728,"results":"31","hashOfConfig":"27"},{"size":3960,"mtime":1767342111728,"results":"32","hashOfConfig":"27"},{"filePath":"33","messages":"34","suppressedMessages":"35","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"47mvrp",{"filePath":"36","messages":"37","suppressedMessages":"38","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"39","messages":"40","suppressedMessages":"41","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"42","messages":"43","suppressedMessages":"44","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"45","messages":"46","suppressedMessages":"47","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"48","messages":"49","suppressedMessages":"50","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"51","messages":"52","suppressedMessages":"53","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"54","messages":"55","suppressedMessages":"56","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"57","messages":"58","suppressedMessages":"59","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"60","messages":"61","suppressedMessages":"62","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"1js9mgi",{"filePath":"63","messages":"64","suppressedMessages":"65","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"66","messages":"67","suppressedMessages":"68","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"69","messages":"70","suppressedMessages":"71","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"72","messages":"73","suppressedMessages":"74","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"75","messages":"76","suppressedMessages":"77","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"/__w/vls-packages/vls-packages/.commitlintrc.js",[],[],"/__w/vls-packages/vls-packages/dist/generate-openapi.js",[],[],"/__w/vls-packages/vls-packages/dist/index.js",[],[],"/__w/vls-packages/vls-packages/dist/lambda-type.js",[],[],"/__w/vls-packages/vls-packages/dist/openapi-type.js",[],[],"/__w/vls-packages/vls-packages/dist/postman-service.js",[],[],"/__w/vls-packages/vls-packages/dist/upload-openapi.js",[],[],"/__w/vls-packages/vls-packages/eslint.config.mjs",[],[],"/__w/vls-packages/vls-packages/release.config.js",[],[],"/__w/vls-packages/vls-packages/src/generate-openapi.ts",[],[],"/__w/vls-packages/vls-packages/src/index.ts",[],[],"/__w/vls-packages/vls-packages/src/lambda-type.ts",[],[],"/__w/vls-packages/vls-packages/src/openapi-type.ts",[],[],"/__w/vls-packages/vls-packages/src/postman-service.ts",[],[],"/__w/vls-packages/vls-packages/src/upload-openapi.ts",[],[]]
@@ -34,13 +34,14 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.generateOpenAPI = void 0;
37
- const zod_openapi_1 = require("@anatine/zod-openapi");
37
+ const zod_openapi_1 = require("zod-openapi");
38
38
  const node_child_process_1 = require("node:child_process");
39
39
  const node_fs_1 = require("node:fs");
40
40
  require("module-alias/register.js");
41
41
  const path = __importStar(require("node:path"));
42
42
  const node_util_1 = require("node:util");
43
43
  const zod_1 = require("zod");
44
+ const axios_1 = require("axios");
44
45
  const OPENAPI_FILE = path.join(process.cwd(), 'openapi.json');
45
46
  const HANDLERS_DIR = path.join(process.cwd(), 'dist', 'src', 'handlers');
46
47
  const SCHEMAS_DIR = path.join(process.cwd(), 'dist', 'src', 'schemas');
@@ -48,7 +49,10 @@ const OUTPUT_FILE = path.join(process.cwd(), 'openapi.json');
48
49
  const generateOpenAPI = async () => {
49
50
  await (0, node_util_1.promisify)(node_child_process_1.exec)('rm -rf ./dist');
50
51
  await (0, node_util_1.promisify)(node_child_process_1.exec)('npx tsc');
51
- const handlerFiles = await node_fs_1.promises.readdir(HANDLERS_DIR).catch(() => []);
52
+ const handlerFiles = await node_fs_1.promises.readdir(HANDLERS_DIR).catch((error) => {
53
+ console.error(error);
54
+ return [];
55
+ });
52
56
  if (handlerFiles.length === 0) {
53
57
  console.log('No handler found in handlers folder.');
54
58
  return;
@@ -61,7 +65,10 @@ const generateOpenAPI = async () => {
61
65
  if (fileName.match(/^([a-z]+-)+queue-(publisher|consumer)$/)) {
62
66
  continue;
63
67
  }
64
- const { bodySchema, queryParametersSchema, eventResponseParametersSchema, eventResponseModulesSchema, OPENAPI_CONFIG: openAPIConfig } = (await Promise.resolve(`${path.join(SCHEMAS_DIR, fileName + '.js')}`).then(s => __importStar(require(s))).catch(() => ({})));
68
+ const { requestSchema, successfulResponseSchema, errorResponseSchema, OPENAPI_CONFIG: openAPIConfig } = (await Promise.resolve(`${path.join(SCHEMAS_DIR, fileName + '.js')}`).then(s => __importStar(require(s))).catch((error) => {
69
+ console.error(error);
70
+ return {};
71
+ }));
65
72
  if (!openAPIConfig) {
66
73
  throw new Error(`${fileName} handler schema not found.`);
67
74
  }
@@ -69,69 +76,142 @@ const generateOpenAPI = async () => {
69
76
  continue;
70
77
  }
71
78
  const { LAMBDA_CONFIG } = (await Promise.resolve(`${path.join(HANDLERS_DIR, `${fileName}.js`)}`).then(s => __importStar(require(s))));
72
- const bodyComponent = (0, zod_openapi_1.generateSchema)(bodySchema ?? zod_1.z.never(), undefined, '3.0');
73
- const queryParametersComponent = (0, zod_openapi_1.generateSchema)(queryParametersSchema ?? zod_1.z.never(), undefined, '3.0');
74
- const eventResponseComponent = generateEventResponseComponent(openAPIConfig.responseType, eventResponseParametersSchema, eventResponseModulesSchema);
75
- const queryParameters = [];
76
- if (queryParametersSchema) {
77
- for (const key in queryParametersComponent.properties) {
78
- const properties = queryParametersComponent?.properties;
79
- if (!properties?.[key]) {
80
- continue;
79
+ const responses = {};
80
+ for (const label in successfulResponseSchema) {
81
+ const response = successfulResponseSchema[label];
82
+ responses[`Success - ${axios_1.HttpStatusCode[response.status]} - ${label}`] = {
83
+ description: axios_1.HttpStatusCode[response.status].toString(),
84
+ content: {
85
+ 'application/json': {
86
+ schema: response.type === 'mindbehind'
87
+ ? (0, zod_openapi_1.createSchema)(zod_1.z.object({
88
+ content: zod_1.z.object({
89
+ params: response.parameters,
90
+ modules: response.modules,
91
+ fallback: zod_1.z.literal(false)
92
+ })
93
+ }), {
94
+ openapiVersion: '3.1.1',
95
+ io: 'output'
96
+ }).schema
97
+ : (0, zod_openapi_1.createSchema)(response.parameters, {
98
+ openapiVersion: '3.1.1',
99
+ io: 'output'
100
+ }).schema
101
+ }
81
102
  }
82
- queryParameters.push({
83
- name: key,
84
- in: 'query',
85
- schema: properties[key]
86
- });
87
- }
103
+ };
88
104
  }
89
- existingOpenAPIFile.paths['/' + fileName] = {
90
- [LAMBDA_CONFIG?.api?.method?.toLowerCase() ?? 'post']: {
91
- tags: openAPIConfig.tags,
92
- parameters: queryParametersSchema ? queryParameters : undefined,
93
- requestBody: bodySchema
94
- ? {
95
- required: true,
96
- content: {
97
- 'application/json': {
98
- schema: bodyComponent
99
- }
100
- }
105
+ for (const label in errorResponseSchema) {
106
+ const response = errorResponseSchema[label];
107
+ responses[`Error - ${axios_1.HttpStatusCode[response.status]} - ${label}`] = {
108
+ description: axios_1.HttpStatusCode[response.status].toString(),
109
+ content: {
110
+ 'application/json': {
111
+ schema: response.type === 'mindbehind'
112
+ ? (0, zod_openapi_1.createSchema)(zod_1.z.object({
113
+ content: zod_1.z.object({
114
+ params: response.parameters,
115
+ modules: response.modules,
116
+ fallback: zod_1.z.literal(false)
117
+ })
118
+ }), {
119
+ openapiVersion: '3.1.1',
120
+ io: 'output'
121
+ }).schema
122
+ : (0, zod_openapi_1.createSchema)(response.parameters, {
123
+ openapiVersion: '3.1.1',
124
+ io: 'output'
125
+ }).schema
101
126
  }
102
- : undefined,
103
- responses: {
104
- '200': {
105
- description: `Successful response.`,
106
- content: {
107
- 'application/json': {
108
- schema: eventResponseComponent
127
+ }
128
+ };
129
+ }
130
+ const parameters = [];
131
+ const bodySchemas = [];
132
+ const headersSchemas = [];
133
+ const queryParametersSchemas = [];
134
+ for (const label in requestSchema) {
135
+ const { body, headers, queryParameters } = requestSchema[label];
136
+ if (!(body instanceof zod_1.z.ZodNever)) {
137
+ bodySchemas.push(body.meta({
138
+ title: label
139
+ }));
140
+ }
141
+ if (!(headers instanceof zod_1.z.ZodNever)) {
142
+ headersSchemas.push(headers.meta({
143
+ title: label
144
+ }));
145
+ }
146
+ if (!(queryParameters instanceof zod_1.z.ZodNever)) {
147
+ queryParametersSchemas.push(queryParameters.meta({
148
+ title: label
149
+ }));
150
+ }
151
+ }
152
+ const bodyComponent = bodySchemas.length !== 0
153
+ ? (0, zod_openapi_1.createSchema)(zod_1.z.union(bodySchemas), {
154
+ openapiVersion: '3.1.1',
155
+ io: 'input'
156
+ })
157
+ : null;
158
+ const headersComponent = headersSchemas.length !== 0
159
+ ? (0, zod_openapi_1.createSchema)(zod_1.z.union(headersSchemas), {
160
+ openapiVersion: '3.1.1',
161
+ io: 'input'
162
+ })
163
+ : null;
164
+ const queryParametersComponent = queryParametersSchemas.length !== 0
165
+ ? (0, zod_openapi_1.createSchema)(zod_1.z.union(queryParametersSchemas), {
166
+ openapiVersion: '3.1.1',
167
+ io: 'input'
168
+ })
169
+ : null;
170
+ if (queryParametersComponent) {
171
+ explodeObject(queryParametersComponent.schema, parameters, 'query');
172
+ }
173
+ if (headersComponent) {
174
+ explodeObject(headersComponent.schema, parameters, 'header');
175
+ }
176
+ for (const method of LAMBDA_CONFIG?.api?.methods ?? ['POST']) {
177
+ existingOpenAPIFile.paths['/' + fileName] = {
178
+ [method.toLowerCase()]: {
179
+ tags: openAPIConfig.tags,
180
+ parameters: parameters,
181
+ requestBody: bodyComponent
182
+ ? {
183
+ content: {
184
+ 'application/json': {
185
+ schema: bodyComponent.schema
186
+ }
109
187
  }
110
188
  }
111
- }
189
+ : undefined,
190
+ responses
112
191
  }
113
- }
114
- };
192
+ };
193
+ }
115
194
  }
116
195
  await node_fs_1.promises.writeFile(OUTPUT_FILE, JSON.stringify(existingOpenAPIFile, undefined, 4));
117
196
  console.info('Open API documentation generated successfully');
118
197
  };
119
198
  exports.generateOpenAPI = generateOpenAPI;
120
- function generateEventResponseComponent(responseType = 'mindbehind-chat-bot-response', eventResponseParametersSchema, eventResponseModulesSchema) {
121
- if (responseType === 'mindbehind-chat-bot-response') {
122
- return (0, zod_openapi_1.generateSchema)(zod_1.z.object({
123
- ...(eventResponseParametersSchema
124
- ? { params: eventResponseParametersSchema }
125
- : { params: zod_1.z.object({}) }),
126
- ...(eventResponseModulesSchema
127
- ? { modules: eventResponseModulesSchema }
128
- : {
129
- modules: (0, zod_openapi_1.extendApi)(zod_1.z.array(zod_1.z.unknown()), {
130
- example: []
131
- })
132
- }),
133
- fallback: zod_1.z.boolean().default(false)
134
- }), undefined, '3.0');
199
+ function explodeObject(schema, parameters, type, isRequiredIncluded = true) {
200
+ if (!('type' in schema) || schema.type !== 'object') {
201
+ parameters.push({
202
+ name: '-',
203
+ in: type,
204
+ schema: schema,
205
+ required: true
206
+ });
207
+ return;
208
+ }
209
+ for (const element in schema.properties) {
210
+ parameters.push({
211
+ name: element,
212
+ in: type,
213
+ schema: schema.properties[element],
214
+ required: isRequiredIncluded ? schema.required?.includes(element) ?? false : false
215
+ });
135
216
  }
136
- return (0, zod_openapi_1.generateSchema)(eventResponseParametersSchema ?? zod_1.z.object({}), undefined, '3.0');
137
217
  }
@@ -1,10 +1,4 @@
1
1
  export type OpenAPIConfig = {
2
2
  tags: string[];
3
3
  isDocumentationNeeded?: boolean;
4
- responseType?: 'mindbehind-chat-bot-response' | 'api-response';
5
- };
6
- export type LambdaConfig = {
7
- api?: {
8
- method?: 'GET' | 'POST';
9
- };
10
4
  };
@@ -1,6 +1,6 @@
1
- import { generateSchema } from '@anatine/zod-openapi';
1
+ import { createSchema } from 'zod-openapi';
2
2
  export type OpenAPI = {
3
- openapi: '3.0.0';
3
+ openapi: '3.1.1';
4
4
  info: {
5
5
  title: string;
6
6
  version: string;
@@ -16,26 +16,23 @@ export type OpenAPI = {
16
16
  parameters: {
17
17
  name: string;
18
18
  in: 'query';
19
- schema: ReturnType<typeof generateSchema>;
19
+ schema: ReturnType<typeof createSchema>['schema'];
20
20
  }[] | undefined;
21
21
  requestBody: {
22
22
  required: true;
23
23
  content: {
24
24
  'application/json': {
25
- schema: ReturnType<typeof generateSchema>;
25
+ schema: ReturnType<typeof createSchema>['schema'];
26
26
  };
27
27
  };
28
28
  } | undefined;
29
- responses: {
30
- 200: {
31
- description: string;
32
- content: {
33
- 'application/json': {
34
- schema: ReturnType<typeof generateSchema>;
35
- };
36
- } | undefined;
37
- };
38
- };
29
+ responses: Record<number, {
30
+ content: {
31
+ 'application/json': {
32
+ schema: ReturnType<typeof createSchema>['schema'];
33
+ };
34
+ } | undefined;
35
+ }>;
39
36
  tags: string[];
40
37
  }>>>;
41
38
  };
@@ -26,13 +26,13 @@ export declare class PostmanService {
26
26
  };
27
27
  }>;
28
28
  createCollection: (partnerName: string) => Promise<string>;
29
- deleteFolder: (collectionID: string, folderID: string) => Promise<import("axios").AxiosResponse<any, any, {}>>;
30
- createFolder: (collectionID: string, folderName: string) => Promise<import("axios").AxiosResponse<any, any, {}>>;
31
- updateCollection: (collectionID: string, collection: object) => Promise<import("axios").AxiosResponse<any, any, {}>>;
29
+ deleteFolder: (collectionID: string, folderID: string) => Promise<import("axios").AxiosResponse<any, any>>;
30
+ createFolder: (collectionID: string, folderName: string) => Promise<import("axios").AxiosResponse<any, any>>;
31
+ updateCollection: (collectionID: string, collection: object) => Promise<import("axios").AxiosResponse<any, any>>;
32
32
  updateVariablesCollection: (collectionID: string, variables: {
33
33
  key: string;
34
34
  value: string;
35
- }[]) => Promise<import("axios").AxiosResponse<any, any, {}>>;
35
+ }[]) => Promise<import("axios").AxiosResponse<any, any>>;
36
36
  getAllEnvironments: () => Promise<{
37
37
  environments: {
38
38
  id: string;
@@ -42,9 +42,9 @@ export declare class PostmanService {
42
42
  createEnvironment: (name: string, values: {
43
43
  key: string;
44
44
  value: string;
45
- }[]) => Promise<import("axios").AxiosResponse<any, any, {}>>;
45
+ }[]) => Promise<import("axios").AxiosResponse<any, any>>;
46
46
  updateEnvironment: (id: string, name: string, values: {
47
47
  key: string;
48
48
  value: string;
49
- }[]) => Promise<import("axios").AxiosResponse<any, any, {}>>;
49
+ }[]) => Promise<import("axios").AxiosResponse<any, any>>;
50
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vls-openapi-generator",
3
- "version": "1.14.0",
3
+ "version": "2.0.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -15,10 +15,10 @@
15
15
  "license": "ISC",
16
16
  "description": "VLS Open API Generator",
17
17
  "dependencies": {
18
- "@anatine/zod-openapi": "^2.2.7",
19
- "axios": "^1.12.0",
20
18
  "module-alias": "^2.2.3",
21
- "openapi-to-postmanv2": "^4.25.0"
19
+ "openapi-to-postmanv2": "^4.25.0",
20
+ "zod": "^4.2.1",
21
+ "zod-openapi": "^5.4.5"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@commitlint/config-conventional": "^19.5.0",
@@ -27,13 +27,15 @@
27
27
  "@semantic-release/npm": "^12.0.1",
28
28
  "@types/node": "^20.14.8",
29
29
  "@types/openapi-to-postmanv2": "^3.2.4",
30
+ "axios": "^1.8.3",
30
31
  "commitlint": "^19.5.0",
31
32
  "eslint": "^9.12.0",
32
33
  "husky": "^9.1.6",
33
34
  "prettier": "^2.7.1",
34
35
  "semantic-release": "^24.1.2",
35
36
  "typescript": "^5.6.3",
36
- "typescript-eslint": "^8.9.0"
37
+ "typescript-eslint": "^8.9.0",
38
+ "vls-sam-template-generator": "^2.22.0"
37
39
  },
38
40
  "bin": {
39
41
  "vls-openapi-generator": "dist/index.js"
package/release.config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  module.exports = {
2
- branches: ['main'],
2
+ branches: ['openapi-generator'],
3
3
  plugins: [
4
4
  '@semantic-release/commit-analyzer',
5
5
  '@semantic-release/release-notes-generator',
@@ -1,12 +1,14 @@
1
- import { extendApi, generateSchema, OpenApiZodAny } from '@anatine/zod-openapi';
1
+ import { createSchema, ZodOpenApiSchemaObject } from 'zod-openapi';
2
2
  import { exec } from 'node:child_process';
3
3
  import { promises as fs } from 'node:fs';
4
4
  import 'module-alias/register.js';
5
5
  import * as path from 'node:path';
6
6
  import { promisify } from 'node:util';
7
7
  import { z } from 'zod';
8
- import { LambdaConfig, OpenAPIConfig } from './lambda-type';
8
+ import { OpenAPIConfig } from './lambda-type';
9
9
  import { OpenAPI } from './openapi-type';
10
+ import type { LambdaConfig } from 'vls-sam-template-generator';
11
+ import { HttpStatusCode } from 'axios';
10
12
 
11
13
  const OPENAPI_FILE = path.join(process.cwd(), 'openapi.json');
12
14
  const HANDLERS_DIR = path.join(process.cwd(), 'dist', 'src', 'handlers');
@@ -17,7 +19,11 @@ export const generateOpenAPI = async (): Promise<void> => {
17
19
  await promisify(exec)('rm -rf ./dist');
18
20
  await promisify(exec)('npx tsc');
19
21
 
20
- const handlerFiles = await fs.readdir(HANDLERS_DIR).catch(() => []);
22
+ const handlerFiles = await fs.readdir(HANDLERS_DIR).catch((error) => {
23
+ console.error(error);
24
+
25
+ return [];
26
+ });
21
27
 
22
28
  if (handlerFiles.length === 0) {
23
29
  console.log('No handler found in handlers folder.');
@@ -39,16 +45,18 @@ export const generateOpenAPI = async (): Promise<void> => {
39
45
  }
40
46
 
41
47
  const {
42
- bodySchema,
43
- queryParametersSchema,
44
- eventResponseParametersSchema,
45
- eventResponseModulesSchema,
48
+ requestSchema,
49
+ successfulResponseSchema,
50
+ errorResponseSchema,
46
51
  OPENAPI_CONFIG: openAPIConfig
47
- } = (await import(path.join(SCHEMAS_DIR, fileName + '.js')).catch(() => ({}))) as {
48
- bodySchema?: OpenApiZodAny;
49
- eventResponseParametersSchema?: OpenApiZodAny;
50
- eventResponseModulesSchema?: OpenApiZodAny;
51
- queryParametersSchema?: OpenApiZodAny;
52
+ } = (await import(path.join(SCHEMAS_DIR, fileName + '.js')).catch((error) => {
53
+ console.error(error);
54
+
55
+ return {};
56
+ })) as {
57
+ requestSchema: RequestSchema;
58
+ successfulResponseSchema?: ResponseSchema;
59
+ errorResponseSchema?: ResponseSchema;
52
60
  OPENAPI_CONFIG: OpenAPIConfig;
53
61
  };
54
62
 
@@ -64,58 +72,157 @@ export const generateOpenAPI = async (): Promise<void> => {
64
72
  LAMBDA_CONFIG?: LambdaConfig;
65
73
  };
66
74
 
67
- const bodyComponent = generateSchema(bodySchema ?? z.never(), undefined, '3.0');
68
- const queryParametersComponent = generateSchema(queryParametersSchema ?? z.never(), undefined, '3.0');
69
- const eventResponseComponent = generateEventResponseComponent(
70
- openAPIConfig.responseType,
71
- eventResponseParametersSchema,
72
- eventResponseModulesSchema
73
- );
75
+ const responses: Record<
76
+ string,
77
+ { description: string; content: { 'application/json': { schema: ZodOpenApiSchemaObject } } }
78
+ > = {};
74
79
 
75
- const queryParameters = [];
80
+ for (const label in successfulResponseSchema) {
81
+ const response = successfulResponseSchema[label];
76
82
 
77
- if (queryParametersSchema) {
78
- for (const key in queryParametersComponent.properties) {
79
- const properties = queryParametersComponent?.properties;
83
+ responses[`Success - ${HttpStatusCode[response.status]} - ${label}`] = {
84
+ description: HttpStatusCode[response.status].toString(),
85
+ content: {
86
+ 'application/json': {
87
+ schema:
88
+ response.type === 'mindbehind'
89
+ ? createSchema(
90
+ z.object({
91
+ content: z.object({
92
+ params: response.parameters,
93
+ modules: response.modules,
94
+ fallback: z.literal(false)
95
+ })
96
+ }),
97
+ {
98
+ openapiVersion: '3.1.1',
99
+ io: 'output'
100
+ }
101
+ ).schema
102
+ : createSchema(response.parameters, {
103
+ openapiVersion: '3.1.1',
104
+ io: 'output'
105
+ }).schema
106
+ }
107
+ }
108
+ };
109
+ }
80
110
 
81
- if (!properties?.[key]) {
82
- continue;
111
+ for (const label in errorResponseSchema) {
112
+ const response = errorResponseSchema[label];
113
+
114
+ responses[`Error - ${HttpStatusCode[response.status]} - ${label}`] = {
115
+ description: HttpStatusCode[response.status].toString(),
116
+ content: {
117
+ 'application/json': {
118
+ schema:
119
+ response.type === 'mindbehind'
120
+ ? createSchema(
121
+ z.object({
122
+ content: z.object({
123
+ params: response.parameters,
124
+ modules: response.modules,
125
+ fallback: z.literal(false)
126
+ })
127
+ }),
128
+ {
129
+ openapiVersion: '3.1.1',
130
+ io: 'output'
131
+ }
132
+ ).schema
133
+ : createSchema(response.parameters, {
134
+ openapiVersion: '3.1.1',
135
+ io: 'output'
136
+ }).schema
137
+ }
83
138
  }
139
+ };
140
+ }
141
+
142
+ const parameters: Parameter[] = [];
84
143
 
85
- queryParameters.push({
86
- name: key,
87
- in: 'query',
88
- schema: properties[key]
89
- } as const);
144
+ const bodySchemas = [];
145
+ const headersSchemas = [];
146
+ const queryParametersSchemas = [];
147
+
148
+ for (const label in requestSchema) {
149
+ const { body, headers, queryParameters } = requestSchema[label];
150
+
151
+ if (!(body instanceof z.ZodNever)) {
152
+ bodySchemas.push(
153
+ body.meta({
154
+ title: label
155
+ })
156
+ );
157
+ }
158
+
159
+ if (!(headers instanceof z.ZodNever)) {
160
+ headersSchemas.push(
161
+ headers.meta({
162
+ title: label
163
+ })
164
+ );
90
165
  }
166
+
167
+ if (!(queryParameters instanceof z.ZodNever)) {
168
+ queryParametersSchemas.push(
169
+ queryParameters.meta({
170
+ title: label
171
+ })
172
+ );
173
+ }
174
+ }
175
+
176
+ const bodyComponent =
177
+ bodySchemas.length !== 0
178
+ ? createSchema(z.union(bodySchemas), {
179
+ openapiVersion: '3.1.1',
180
+ io: 'input'
181
+ })
182
+ : null;
183
+
184
+ const headersComponent =
185
+ headersSchemas.length !== 0
186
+ ? createSchema(z.union(headersSchemas), {
187
+ openapiVersion: '3.1.1',
188
+ io: 'input'
189
+ })
190
+ : null;
191
+
192
+ const queryParametersComponent =
193
+ queryParametersSchemas.length !== 0
194
+ ? createSchema(z.union(queryParametersSchemas), {
195
+ openapiVersion: '3.1.1',
196
+ io: 'input'
197
+ })
198
+ : null;
199
+
200
+ if (queryParametersComponent) {
201
+ explodeObject(queryParametersComponent.schema, parameters, 'query');
202
+ }
203
+
204
+ if (headersComponent) {
205
+ explodeObject(headersComponent.schema, parameters, 'header');
91
206
  }
92
207
 
93
- existingOpenAPIFile.paths['/' + fileName] = {
94
- [LAMBDA_CONFIG?.api?.method?.toLowerCase() ?? 'post']: {
95
- tags: openAPIConfig.tags,
96
- parameters: queryParametersSchema ? queryParameters : undefined,
97
- requestBody: bodySchema
98
- ? {
99
- required: true,
100
- content: {
101
- 'application/json': {
102
- schema: bodyComponent
208
+ for (const method of LAMBDA_CONFIG?.api?.methods ?? ['POST']) {
209
+ existingOpenAPIFile.paths['/' + fileName] = {
210
+ [method.toLowerCase()]: {
211
+ tags: openAPIConfig.tags,
212
+ parameters: parameters,
213
+ requestBody: bodyComponent
214
+ ? {
215
+ content: {
216
+ 'application/json': {
217
+ schema: bodyComponent.schema
218
+ }
103
219
  }
104
220
  }
105
- }
106
- : undefined,
107
- responses: {
108
- '200': {
109
- description: `Successful response.`,
110
- content: {
111
- 'application/json': {
112
- schema: eventResponseComponent
113
- }
114
- }
115
- }
221
+ : undefined,
222
+ responses
116
223
  }
117
- }
118
- };
224
+ };
225
+ }
119
226
  }
120
227
 
121
228
  await fs.writeFile(OUTPUT_FILE, JSON.stringify(existingOpenAPIFile, undefined, 4));
@@ -123,30 +230,60 @@ export const generateOpenAPI = async (): Promise<void> => {
123
230
  console.info('Open API documentation generated successfully');
124
231
  };
125
232
 
126
- function generateEventResponseComponent(
127
- responseType: OpenAPIConfig['responseType'] = 'mindbehind-chat-bot-response',
128
- eventResponseParametersSchema?: OpenApiZodAny,
129
- eventResponseModulesSchema?: OpenApiZodAny
233
+ function explodeObject(
234
+ schema: ZodOpenApiSchemaObject,
235
+ parameters: Parameter[],
236
+ type: 'header' | 'query',
237
+ isRequiredIncluded = true
130
238
  ) {
131
- if (responseType === 'mindbehind-chat-bot-response') {
132
- return generateSchema(
133
- z.object({
134
- ...(eventResponseParametersSchema
135
- ? { params: eventResponseParametersSchema }
136
- : { params: z.object({}) }),
137
- ...(eventResponseModulesSchema
138
- ? { modules: eventResponseModulesSchema }
139
- : {
140
- modules: extendApi(z.array(z.unknown()), {
141
- example: []
142
- })
143
- }),
144
- fallback: z.boolean().default(false)
145
- }),
146
- undefined,
147
- '3.0'
148
- );
239
+ if (!('type' in schema) || schema.type !== 'object') {
240
+ parameters.push({
241
+ name: '-',
242
+ in: type,
243
+ schema: schema,
244
+ required: true
245
+ } as const);
246
+
247
+ return;
149
248
  }
150
249
 
151
- return generateSchema(eventResponseParametersSchema ?? z.object({}), undefined, '3.0');
250
+ for (const element in schema.properties) {
251
+ parameters.push({
252
+ name: element,
253
+ in: type,
254
+ schema: schema.properties[element],
255
+ required: isRequiredIncluded ? schema.required?.includes(element) ?? false : false
256
+ } as const);
257
+ }
152
258
  }
259
+
260
+ type Parameter = {
261
+ name: string;
262
+ in: 'query' | 'header';
263
+ schema: unknown;
264
+ required: boolean;
265
+ };
266
+
267
+ type RequestSchema = Record<
268
+ string,
269
+ {
270
+ body: z.ZodType;
271
+ queryParameters: z.ZodObject | z.ZodNever;
272
+ headers: z.ZodObject | z.ZodNever;
273
+ }
274
+ >;
275
+
276
+ type ResponseSchema = Record<
277
+ string,
278
+ | {
279
+ type: 'mindbehind';
280
+ status: keyof typeof HttpStatusCode;
281
+ parameters: z.ZodType;
282
+ modules: z.ZodArray;
283
+ }
284
+ | {
285
+ type: 'api';
286
+ status: keyof typeof HttpStatusCode;
287
+ parameters: z.ZodType;
288
+ }
289
+ >;
@@ -1,11 +1,4 @@
1
1
  export type OpenAPIConfig = {
2
2
  tags: string[];
3
3
  isDocumentationNeeded?: boolean;
4
- responseType?: 'mindbehind-chat-bot-response' | 'api-response';
5
- };
6
-
7
- export type LambdaConfig = {
8
- api?: {
9
- method?: 'GET' | 'POST';
10
- };
11
4
  };
@@ -1,7 +1,7 @@
1
- import { generateSchema } from '@anatine/zod-openapi';
1
+ import { createSchema } from 'zod-openapi';
2
2
 
3
3
  export type OpenAPI = {
4
- openapi: '3.0.0';
4
+ openapi: '3.1.1';
5
5
  info: { title: string; version: string };
6
6
  servers: [{ url: string }, { url: string }, { url: string }];
7
7
  paths: Record<
@@ -14,7 +14,7 @@ export type OpenAPI = {
14
14
  | {
15
15
  name: string;
16
16
  in: 'query';
17
- schema: ReturnType<typeof generateSchema>;
17
+ schema: ReturnType<typeof createSchema>['schema'];
18
18
  }[]
19
19
  | undefined;
20
20
  requestBody:
@@ -22,23 +22,23 @@ export type OpenAPI = {
22
22
  required: true;
23
23
  content: {
24
24
  'application/json': {
25
- schema: ReturnType<typeof generateSchema>;
25
+ schema: ReturnType<typeof createSchema>['schema'];
26
26
  };
27
27
  };
28
28
  }
29
29
  | undefined;
30
- responses: {
31
- 200: {
32
- description: string;
30
+ responses: Record<
31
+ number,
32
+ {
33
33
  content:
34
34
  | {
35
35
  'application/json': {
36
- schema: ReturnType<typeof generateSchema>;
36
+ schema: ReturnType<typeof createSchema>['schema'];
37
37
  };
38
38
  }
39
39
  | undefined;
40
- };
41
- };
40
+ }
41
+ >;
42
42
  tags: string[];
43
43
  }
44
44
  >
@@ -1,74 +0,0 @@
1
- name: CI/CD Pipeline
2
-
3
- on:
4
- pull_request:
5
- branches:
6
- - main
7
- push:
8
- branches:
9
- - main
10
-
11
- permissions:
12
- contents: write
13
-
14
- jobs:
15
- release:
16
- if: github.event_name == 'push'
17
- runs-on: [mb-runner]
18
- container:
19
- image: ubuntu:latest
20
- steps:
21
- - name: Install Git
22
- run: apt-get update && apt-get -y install git
23
-
24
- - name: Checkout code
25
- uses: actions/checkout@v4
26
- with:
27
- fetch-depth: 0
28
-
29
- - name: Setup Node
30
- uses: actions/setup-node@v4
31
- with:
32
- node-version: 20
33
- cache: 'npm'
34
-
35
- - name: Set ownership
36
- run: chown -R $(whoami) .
37
-
38
- - name: Install dependencies
39
- run: npm install
40
-
41
- - name: Build project
42
- run: npm run build
43
-
44
- - name: Release with Semantic
45
- run: npx semantic-release
46
- env:
47
- NPM_TOKEN: ${{ secrets.NPM_TOKEN_FOR_VLS }}
48
-
49
- build:
50
- if: github.event_name == 'pull_request'
51
- runs-on: [mb-runner]
52
- container:
53
- image: ubuntu:latest
54
- steps:
55
- - name: Checkout code
56
- uses: actions/checkout@v4
57
-
58
- - name: Setup Node
59
- uses: actions/setup-node@v4
60
- with:
61
- node-version: 20
62
- cache: 'npm'
63
-
64
- - name: Install dependencies
65
- run: npm install
66
-
67
- - name: Run Prettier check
68
- run: npx prettier --check .
69
-
70
- - name: Lint check
71
- run: npx eslint .
72
-
73
- - name: Build project
74
- run: npm run build
@@ -1,23 +0,0 @@
1
- name: Security AllInOne
2
- on:
3
- push:
4
- branches:
5
- - feature/*
6
- pull_request:
7
- types:
8
- - opened
9
- - closed
10
- - ready_for_review
11
- jobs:
12
- build:
13
- runs-on:
14
- group: default
15
- labels: self-hosted
16
- steps:
17
- - name: Trigger to Insider Security
18
- run: |
19
- curl -X POST -H "Content-Type: application/json" \
20
- -d '{"type": "security-allinone", "version": "v1", "repository": "${{ github.event.repository.name }}", "ref": "${{ github.head_ref || github.ref_name }}", "event_name": "${{ github.event_name }}", "event_action": "${{ github.event.action }}", "default_branch": "${{ github.event.repository.default_branch }}"}' \
21
- $INSECPROXY_HOOK
22
- env:
23
- INSECPROXY_HOOK: ${{ secrets.INSECPROXY_HOOK }}