Skip to content

Commit 2ecc049

Browse files
committed
feat: add include\exclude filters
1 parent c31661b commit 2ecc049

File tree

8 files changed

+157
-19
lines changed

8 files changed

+157
-19
lines changed

bin/saddleback.cli.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const params = program
1717
.option('-p, --password <value>', 'Password')
1818
.option('-e, --environment <value>', 'Environment dev | stage | stage2')
1919
.option('-s, --service <value>', 'Service ')
20+
.option('-m, --filterMethod <value>', 'Filter method include(default) | exclude')
21+
.option('-f, --filterArray <value>', 'Filter array')
2022
.parse(process.argv)
2123
.opts();
2224

@@ -47,6 +49,8 @@ if (OpenAPI) {
4749
password: params.password,
4850
useEnvironment: params.environment,
4951
useService: params.service,
52+
filterMethod: params.filterMethod,
53+
filterArray: params.filterArray,
5054
})
5155
.then(() => {
5256
process.exit(0);

src/generateSaddlebackSpec.ts

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { generate, Options } from './generate';
22
import { OpenApi } from './openApi/v3/interfaces/OpenApi';
3+
import { OpenApiOperation } from './openApi/v3/interfaces/OpenApiOperation';
4+
import { OpenApiParameter } from './openApi/v3/interfaces/OpenApiParameter';
35
import { OpenApiSchema } from './openApi/v3/interfaces/OpenApiSchema';
6+
import { OpenApiServer } from './openApi/v3/interfaces/OpenApiServer';
47
import { getOpenApiSpec } from './utils/getOpenApiSpec';
58
import { isString } from './utils/isString';
69
import { mapSwaggerRef } from './utils/mapSwaggerRef';
710
import { removeLodashPrefix } from './utils/removeLodashPrefix';
811
import { removeLodashPrefixFromRef } from './utils/removeLodashPrefixFromRef';
12+
import { getSchemaRefFromContent } from './utils/saddleback/getSchemaRefFromContent';
913
import { getSwaggerJsonByEnv } from './utils/saddleback/getSwaggerJsonByEnv';
1014
import { Environment, Service } from './utils/saddleback/getUrlByServiceEnv';
15+
import { recursiveAddAllUnknownModels } from './utils/saddleback/recursiveAddAllUnknownModels';
1116
import { Dictionary } from './utils/types';
1217

1318
type Config = Options & {
@@ -19,6 +24,8 @@ type Config = Options & {
1924
password: string;
2025
useEnvironment?: Environment;
2126
useService?: Service;
27+
filterMethod: 'include' | 'exclude';
28+
filterArray: string[];
2229
};
2330

2431
export const generateSaddlebackSpec = async (config: Config) => {
@@ -48,7 +55,95 @@ export const generateSaddlebackSpec = async (config: Config) => {
4855

4956
mapSwaggerRef(openApi, removeLodashPrefixFromRef);
5057

51-
await generate({ ...config, input: openApi });
58+
const list: OpenApi = openApi;
59+
60+
const requiredPaths: OpenApi['paths'] = {};
61+
62+
for (const path in list.paths) {
63+
if (!list.paths.hasOwnProperty(path)) return;
64+
65+
if (config.filterMethod === 'include') {
66+
if (config.filterArray.some(it => it === path)) requiredPaths[path] = list.paths[path];
67+
}
68+
if (config.filterMethod === 'exclude') {
69+
if (!config.filterArray.some(it => it === path)) requiredPaths[path] = list.paths[path];
70+
}
71+
}
72+
73+
const requiredSchemasSet: Set<string> = new Set();
74+
75+
for (const pathName in requiredPaths) {
76+
const pathElement = requiredPaths[pathName];
77+
78+
const openApiPathValues = Object.values(pathElement) as (
79+
| OpenApiOperation
80+
| OpenApiServer
81+
| OpenApiParameter
82+
| string
83+
)[];
84+
85+
openApiPathValues.forEach(requestMethodData => {
86+
if (typeof requestMethodData !== 'string') {
87+
if (!('url' in requestMethodData)) {
88+
if ('parameters' in requestMethodData) {
89+
// add schemas from {apiPath}/{method}/parameters
90+
requestMethodData.parameters?.forEach(parameter => {
91+
const modelName = getSchemaRefFromContent(parameter);
92+
93+
requiredSchemasSet.add(modelName);
94+
recursiveAddAllUnknownModels(modelName, openApi, requiredSchemasSet);
95+
});
96+
}
97+
if ('responses' in requestMethodData) {
98+
const responsesCodeData = Object.values(requestMethodData.responses);
99+
100+
responsesCodeData.forEach(response => {
101+
const contentTypeData = Object.values(response.content ?? {});
102+
103+
// add schemas from {apiPath}/{method}/responses/{responseType}/content
104+
contentTypeData.forEach(content => {
105+
const modelName = getSchemaRefFromContent(content);
106+
107+
requiredSchemasSet.add(getSchemaRefFromContent(content));
108+
recursiveAddAllUnknownModels(modelName, openApi, requiredSchemasSet);
109+
});
110+
});
111+
}
112+
if ('requestBody' in requestMethodData) {
113+
const requestBodyContent = Object.values(requestMethodData.requestBody?.content ?? {});
114+
115+
// add schemas from {apiPath}/{method}/responses/{responseType}/requestBody/content
116+
requestBodyContent.forEach(content => {
117+
const modelName = getSchemaRefFromContent(content);
118+
119+
requiredSchemasSet.add(getSchemaRefFromContent(content));
120+
recursiveAddAllUnknownModels(modelName, openApi, requiredSchemasSet);
121+
});
122+
}
123+
}
124+
}
125+
});
126+
}
127+
128+
const requiredSchemas: Dictionary<OpenApiSchema> = {};
129+
130+
if (list && list.components && list.components.schemas) {
131+
for (const schema in list.components.schemas) {
132+
if (requiredSchemasSet.has(schema)) {
133+
requiredSchemas[schema] = list.components.schemas[schema];
134+
}
135+
}
136+
}
137+
138+
const listWithRequiredPaths: OpenApi = {
139+
...list,
140+
paths: requiredPaths,
141+
components: {
142+
schemas: requiredSchemas,
143+
},
144+
};
145+
146+
await generate({ ...config, input: listWithRequiredPaths });
52147
};
53148

54149
export default generateSaddlebackSpec;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const getNameFromRef = (ref: string): string => {
2+
return ref.split('/').slice(-1)[0];
3+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { OpenApiMediaType } from '../../openApi/v3/interfaces/OpenApiMediaType';
2+
import { getNameFromRef } from './getNameFromRef';
3+
4+
export const getSchemaRefFromContent = (content: OpenApiMediaType): string => {
5+
let ref: string = '';
6+
7+
ref = content.$ref || content.schema?.$ref || content.schema?.items?.$ref || '';
8+
9+
return getNameFromRef(ref);
10+
};

src/utils/saddleback/getUrlByServiceEnv.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export enum Environment {
99
Dev = 'dev',
1010
Stage = 'stage',
1111
Stage2 = 'stage2',
12+
Feature = 'feature',
1213
}
1314

1415
export const getUrlByServiceEnv = ({ env, service }: { service: Service; env: Environment }): string => {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { OpenApi } from '../../openApi/v3/interfaces/OpenApi';
2+
import { getNameFromRef } from './getNameFromRef';
3+
4+
export const recursiveAddAllUnknownModels = (
5+
modelName: string,
6+
openApi: OpenApi,
7+
requiredSchemasSet: Set<string>
8+
): void => {
9+
const model = openApi.components?.schemas ? openApi.components.schemas[modelName] : undefined;
10+
if (model === undefined) return;
11+
12+
for (const property in model.properties) {
13+
const ref = model.properties[property].$ref || model.properties[property].items?.$ref || '';
14+
const modelName = getNameFromRef(ref);
15+
16+
if (!requiredSchemasSet.has(modelName)) {
17+
requiredSchemasSet.add(modelName);
18+
recursiveAddAllUnknownModels(modelName, openApi, requiredSchemasSet);
19+
}
20+
}
21+
};

test/index.js

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,32 +75,34 @@ const main = async () => {
7575
removeLodashPrefixes: true,
7676
username: '[email protected]',
7777
password: `&cY8at<'S5PfJa#k`,
78-
useEnvironment: 'stage2',
78+
useEnvironment: 'feature',
79+
filterMethod: 'include',
7980
};
8081
// await OpenAPI.generateSaddlebackSpec({
8182
// output: './test/auto',
8283
// ...config,
8384
// });
84-
await OpenAPI.generateSaddlebackSpec({
85-
useService: 'core',
86-
output: './auto/core',
87-
...config,
88-
});
85+
// await OpenAPI.generateSaddlebackSpec({
86+
// useService: 'core',
87+
// output: './auto/core',
88+
// ...config,
89+
// });
8990
await OpenAPI.generateSaddlebackSpec({
9091
useService: 'event',
91-
output: './auto/event',
92-
...config,
93-
});
94-
await OpenAPI.generateSaddlebackSpec({
95-
useService: 'workflows',
96-
output: './auto/workflows',
97-
...config,
98-
});
99-
await OpenAPI.generateSaddlebackSpec({
100-
useService: 'notifications',
101-
output: './auto/notifications',
92+
output: './auto2/event',
93+
filterArray: ['/api/event/{id}'],
10294
...config,
10395
});
96+
// await OpenAPI.generateSaddlebackSpec({
97+
// useService: 'workflows',
98+
// output: './auto/workflows',
99+
// ...config,
100+
// });
101+
// await OpenAPI.generateSaddlebackSpec({
102+
// useService: 'notifications',
103+
// output: './auto/notifications',
104+
// ...config,
105+
// });
104106

105107
// await generateRealWorldSpecs();
106108
};

types/index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ export type CustomConfig = Options & {
4343
useAutoEventService?: boolean;
4444
useAutoNotificationService?: boolean;
4545
useAutoWorkflowsService?: boolean;
46-
useEnvironment?: Environment | 'dev' | 'stage' | 'stage2';
46+
useEnvironment?: Environment | 'dev' | 'stage' | 'stage2' | 'feature';
4747
useService?: Service | 'workflows' | 'event' | 'notifications' | 'core';
48+
filterMethod: 'include' | 'exclude';
49+
filterArray: string[];
4850
};
4951

5052
export declare function generate(options: Options): Promise<void>;

0 commit comments

Comments
 (0)