swaggular 0.1.3 → 0.2.2

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.
@@ -4,20 +4,18 @@ exports.toVariables = toVariables;
4
4
  function toVariables(parsed) {
5
5
  const variables = {};
6
6
  if (parsed.args.mode) {
7
- variables.groupingMode = parsed.args.mode;
7
+ variables.groupingMode = parsed.args.mode || 'path';
8
8
  }
9
9
  if (parsed.args.segmentsToIgnore) {
10
10
  if (typeof parsed.args.segmentsToIgnore === 'string') {
11
11
  variables.segmentsToIgnore = parsed.args.segmentsToIgnore.split(',');
12
12
  }
13
13
  }
14
- if (parsed.args.ignoreVariables) {
15
- variables.ignoreVariables =
16
- parsed.args.ignoreVariables === 'true' || parsed.args.ignoreVariables === true;
17
- }
18
14
  return {
15
+ input: parsed.args.input || 'swagger.json',
16
+ output: parsed.args.output || 'results',
17
+ noGenerate: parsed.args.noGenerate || false,
19
18
  groupingMode: variables.groupingMode || 'path',
20
19
  segmentsToIgnore: variables.segmentsToIgnore || ['api'],
21
- ignoreVariables: variables.ignoreVariables || true,
22
20
  };
23
21
  }
package/dist/index.js CHANGED
@@ -16,25 +16,25 @@ async function main() {
16
16
  try {
17
17
  const parsed = (0, args_1.getParseArgs)(process.argv.slice(2));
18
18
  const variables = (0, variables_1.toVariables)(parsed);
19
- swagger_parser_1.SwaggerParser.parse(parsed.args.input, {
19
+ swagger_parser_1.SwaggerParser.parse(variables.input, {
20
20
  mode: variables.groupingMode,
21
21
  segmentsToIgnore: variables.segmentsToIgnore,
22
- ignoreVariables: variables.ignoreVariables,
22
+ ignoreVariables: true,
23
23
  });
24
24
  (0, generate_interface_1.generateInterfaces)();
25
25
  (0, generate_service_1.generateServices)();
26
26
  const interfaceFiles = (0, generate_interface_1.generateInterfacesFiles)();
27
27
  const serviceFiles = (0, generate_service_1.generateServiceFiles)();
28
- if (parsed.args.noGenerate) {
29
- console.log('No se generaron archivos');
28
+ if (variables.noGenerate) {
29
+ console.log('Files not generated');
30
30
  return;
31
31
  }
32
- if (fs_1.default.existsSync('results')) {
33
- fs_1.default.rmSync('results', { recursive: true, force: true });
32
+ if (fs_1.default.existsSync(variables.output)) {
33
+ fs_1.default.rmSync(variables.output, { recursive: true, force: true });
34
34
  }
35
- await (0, create_file_1.createFileFromFileContents)('results/models', interfaceFiles);
36
- await (0, create_file_1.createFileFromFileContents)('results', serviceFiles);
37
- console.log('Archivo creado correctamente');
35
+ await (0, create_file_1.createFileFromFileContents)(`${variables.output}/models`, interfaceFiles);
36
+ await (0, create_file_1.createFileFromFileContents)(variables.output, serviceFiles);
37
+ console.log('Files created successfully');
38
38
  }
39
39
  catch (err) {
40
40
  console.error('Failed to read JSON:', err);
@@ -194,6 +194,8 @@ class PathGrouper {
194
194
  const common = [];
195
195
  for (let i = 0; i < firstPath.length; i++) {
196
196
  const segment = firstPath[i];
197
+ if ((0, path_utils_1.isVariable)(segment))
198
+ break;
197
199
  if (pathSegments.every((ps) => ps[i] === segment)) {
198
200
  common.push(segment);
199
201
  }
@@ -26,8 +26,9 @@ function generateInterfaceComments(param) {
26
26
  }
27
27
  function generateServiceComments(summary, responseType, parameters) {
28
28
  const summarySplited = [];
29
- for (let i = 0; i < summary.length; i += 100) {
30
- summarySplited.push(summary.slice(i, i + 100));
29
+ const summaryWords = summary.split(' ');
30
+ for (let i = 0; i < summaryWords.length; i += 10) {
31
+ summarySplited.push(summaryWords.slice(i, i + 10).join(' '));
31
32
  }
32
33
  const comments = summarySplited;
33
34
  if (parameters && parameters.length > 0) {
@@ -36,5 +37,5 @@ function generateServiceComments(summary, responseType, parameters) {
36
37
  if (responseType) {
37
38
  comments.push(`@returns Observable<${responseType}>`);
38
39
  }
39
- return `\t/**\n${comments.map((c) => `\t* ${c}\n`).join('')}\n */`;
40
+ return `\t/**\n${comments.map((c) => `\t* ${c}\n`).join('')} */`;
40
41
  }
@@ -1,3 +1,4 @@
1
+ import { OpenAPIV3 } from 'openapi-types';
1
2
  import { FileContent } from '../types/file-content';
2
3
  import { GroupedPath } from '../types/grouped-paths';
3
4
  import { ServiceDataMethod } from '../types/service-data';
@@ -6,4 +7,4 @@ import { ServiceDataMethod } from '../types/service-data';
6
7
  */
7
8
  export declare function generateServices(): void;
8
9
  export declare function generateServiceFiles(locations?: Record<string, string[]>): FileContent[];
9
- export declare function buildMethods(groupedPath: GroupedPath): ServiceDataMethod[];
10
+ export declare function buildMethods(groupedPath: GroupedPath, pathData: OpenAPIV3.PathsObject): ServiceDataMethod[];
@@ -22,21 +22,17 @@ function generateServices() {
22
22
  if (!groupedPaths || !paths)
23
23
  return;
24
24
  for (const [key, value] of Object.entries(groupedPaths)) {
25
- const serviceMethods = buildMethods(value);
25
+ const serviceMethods = buildMethods(value, paths);
26
26
  const imports = new Set();
27
27
  serviceMethods.forEach((method) => {
28
- if (!(0, type_guard_1.isNativeType)(method.responseType)) {
29
- imports.add(method.responseType);
30
- }
28
+ addTypeToImports(method.responseType, imports);
31
29
  method.parameters.forEach((param) => {
32
- if (!(0, type_guard_1.isNativeType)(param.type)) {
33
- imports.add(param.type);
34
- }
30
+ addTypeToImports(param.type, imports);
35
31
  });
36
32
  });
37
33
  const serviceData = {
38
34
  name: key,
39
- imports: Array.from(imports),
35
+ imports: Array.from(imports).sort(),
40
36
  baseUrl: value.baseUrl,
41
37
  methods: serviceMethods,
42
38
  };
@@ -44,6 +40,22 @@ function generateServices() {
44
40
  }
45
41
  service_state_1.serviceState.addServices(servicesData);
46
42
  }
43
+ function addTypeToImports(type, imports) {
44
+ if (!type || (0, type_guard_1.isNativeType)(type))
45
+ return;
46
+ const baseType = type.replace('[]', '');
47
+ if (baseType.includes('<')) {
48
+ const parts = baseType.split(/[<>]/);
49
+ parts.forEach((p) => {
50
+ if (p && !(0, type_guard_1.isNativeType)(p)) {
51
+ imports.add(p);
52
+ }
53
+ });
54
+ }
55
+ else {
56
+ imports.add(baseType);
57
+ }
58
+ }
47
59
  function generateServiceFiles(locations) {
48
60
  const services = Object.values(service_state_1.serviceState.services);
49
61
  const filesContent = [];
@@ -71,22 +83,22 @@ function generateServiceFiles(locations) {
71
83
  }
72
84
  return filesContent;
73
85
  }
74
- function buildMethods(groupedPath) {
86
+ function buildMethods(groupedPath, pathData) {
75
87
  const methods = [];
76
- const pathData = swagger_state_1.swaggerState.getPaths();
77
88
  const usedNames = [];
78
89
  for (const path of groupedPath.paths) {
79
90
  if (!pathData || !pathData[path])
80
91
  continue;
81
- const pathItem = pathItemToMethods(pathData[path]);
82
- for (const [method, operation] of Object.entries(pathItem)) {
92
+ const pathItem = pathData[path];
93
+ const operations = pathItemToMethods(pathItem);
94
+ for (const [method, operation] of Object.entries(operations)) {
83
95
  const name = buildMethodName(method, path, groupedPath, usedNames);
84
96
  usedNames.push(name);
85
- const parameters = buildParameters(operation.parameters ?? []);
97
+ const parameters = buildParameters(operation, pathItem.parameters);
86
98
  const responseType = buildResponseType(operation.responses);
87
99
  methods.push({
88
100
  name,
89
- path: (0, path_utils_1.removeVariablesFromPath)((0, path_utils_1.getExtraSegments)(path, (0, path_utils_1.createBaseUrl)(groupedPath.baseSegments)).join('')),
101
+ path: (0, path_utils_1.getExtraSegments)(path, groupedPath.baseUrl).join('/'),
90
102
  method: method,
91
103
  parameters,
92
104
  responseType,
@@ -114,16 +126,20 @@ function buildMethodName(method, path, groupedPath, usedNames) {
114
126
  patch: 'patch',
115
127
  };
116
128
  const prefix = dict[method];
117
- const extra = (0, string_utils_1.kebabToPascalCase)((0, path_utils_1.removeVariablesFromPath)((0, path_utils_1.getExtraSegments)(path, (0, path_utils_1.createBaseUrl)(groupedPath.baseSegments)).join('')));
129
+ const extraSegments = (0, path_utils_1.getExtraSegments)(path, groupedPath.baseUrl);
130
+ // Create name segments, removing variables from name
131
+ const nameSegments = extraSegments.map((s) => s.replace(/[{}]/g, ''));
132
+ const extra = (0, string_utils_1.kebabToPascalCase)(nameSegments.join(''));
118
133
  let name = prefix + (0, string_utils_1.upFirst)(extra);
119
134
  if (usedNames.includes(name)) {
120
135
  name += (0, string_utils_1.upFirst)(method);
121
136
  }
122
137
  return name;
123
138
  }
124
- function buildParameters(parameters) {
139
+ function buildParameters(operation, pathParameters) {
125
140
  const results = [];
126
- for (const param of parameters) {
141
+ const allParams = [...(pathParameters ?? []), ...(operation.parameters ?? [])];
142
+ for (const param of allParams) {
127
143
  if ((0, type_guard_1.isReference)(param)) {
128
144
  const ref = param.$ref.split('/').pop();
129
145
  results.push({
@@ -141,12 +157,30 @@ function buildParameters(parameters) {
141
157
  type: (0, build_types_1.switchTypeJson)(param.schema),
142
158
  });
143
159
  }
144
- // Check for body (simplified)
145
- // In a real implementation we would look at requestBody
160
+ // Handle requestBody in V3
161
+ if (operation.requestBody) {
162
+ let bodyType = 'any';
163
+ if (!(0, type_guard_1.isReference)(operation.requestBody)) {
164
+ const content = operation.requestBody.content?.['application/json'] ||
165
+ operation.requestBody.content?.['multipart/form-data'];
166
+ if (content && content.schema) {
167
+ bodyType = (0, build_types_1.switchTypeJson)(content.schema);
168
+ }
169
+ }
170
+ else {
171
+ bodyType = operation.requestBody.$ref.split('/').pop();
172
+ }
173
+ results.push({
174
+ name: bodyType === 'FormData' ? 'formData' : 'body',
175
+ in: 'body',
176
+ required: true,
177
+ type: bodyType,
178
+ });
179
+ }
146
180
  return results;
147
181
  }
148
182
  function buildResponseType(responses) {
149
- const success = responses['200'] || responses['201'] || responses['default'];
183
+ const success = responses['200'] || responses['201'] || responses['204'] || responses['default'];
150
184
  if (!success)
151
185
  return 'any';
152
186
  if ((0, type_guard_1.isReference)(success))
@@ -160,19 +194,28 @@ function buildMethodTemplate(method) {
160
194
  const params = method.parameters
161
195
  .map((p) => `${p.name}${p.required ? '' : '?'}: ${p.type}`)
162
196
  .join(', ');
163
- const path = method.path
197
+ let pathStr = method.path
164
198
  .split('/')
199
+ .filter((s) => s !== '')
165
200
  .map((s) => ((0, path_utils_1.isVariable)(s) ? `\${${s.replace(/[{}]/g, '')}}` : s))
166
201
  .join('/');
202
+ if (pathStr) {
203
+ pathStr = '/' + pathStr;
204
+ }
167
205
  const queryParams = method.parameters.filter((p) => p.in === 'query');
168
206
  const bodyParam = method.parameters.find((p) => p.in === 'body');
169
207
  let options = '';
170
208
  if (queryParams.length > 0) {
171
209
  options = `, { params: HttpHelper.toHttpParams({ ${queryParams.map((p) => p.name).join(', ')} }) }`;
172
210
  }
173
- const httpCall = `this.http.${method.method}<${method.responseType}>(this.baseUrl + \`${path}\`${bodyParam ? `, ${bodyParam.name}` : ['post', 'put', 'patch'].includes(method.method) ? ', {}' : ''}${options})`;
211
+ const url = `\`\${this.baseUrl}${pathStr}\``;
212
+ const methodCall = bodyParam
213
+ ? `this.http.${method.method}<${method.responseType}>(${url}, ${bodyParam.name}${options})`
214
+ : ['post', 'put', 'patch'].includes(method.method)
215
+ ? `this.http.${method.method}<${method.responseType}>(${url}, {}${options})`
216
+ : `this.http.${method.method}<${method.responseType}>(${url}${options})`;
174
217
  return `${method.comments}
175
218
  ${method.name}(${params}): Observable<${method.responseType}> {
176
- return ${httpCall};
219
+ return ${methodCall};
177
220
  }`;
178
221
  }
@@ -1,3 +1,6 @@
1
+ /**
2
+ * Modify this interface according to your needs and your template
3
+ */
1
4
  export interface AngularServiceTemplate {
2
5
  name: string;
3
6
  baseUrl: string;
@@ -7,7 +7,7 @@ const angularTemplate = (params) => {
7
7
  import { HttpClient } from "@angular/common/http";
8
8
  import { Observable } from "rxjs";
9
9
  import { inject, Injectable } from '@angular/core';
10
- import { ${params.imports} } from '../../models';
10
+ import { ${params.imports} } from '../models';
11
11
  ${params.hasHttpParamsHandler ? (0, http_params_handler_1.httpParamsHandlerImport)() : ''}
12
12
 
13
13
  @Injectable({
@@ -15,7 +15,7 @@ ${params.hasHttpParamsHandler ? (0, http_params_handler_1.httpParamsHandlerImpor
15
15
  })
16
16
  export class ${params.name}Service {
17
17
 
18
- private readonly baseUrl = "${params.baseUrl}";
18
+ private readonly baseUrl = \`${params.baseUrl}\`;
19
19
  private readonly http = inject(HttpClient);
20
20
 
21
21
  ${params.methods}
@@ -1,2 +1,11 @@
1
+ /**
2
+ * Modify this function according to your needs and your template.
3
+ * This function is used to handle the http params and how you want it to be
4
+ * used in your service.
5
+ */
1
6
  export declare function httpParamsHandler(params: any): string;
7
+ /**
8
+ * Modify this function according to your needs and your template.
9
+ * This function is used to import anything you need to handle the http params.
10
+ */
2
11
  export declare function httpParamsHandlerImport(): string;
@@ -2,11 +2,20 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.httpParamsHandler = httpParamsHandler;
4
4
  exports.httpParamsHandlerImport = httpParamsHandlerImport;
5
+ /**
6
+ * Modify this function according to your needs and your template.
7
+ * This function is used to handle the http params and how you want it to be
8
+ * used in your service.
9
+ */
5
10
  function httpParamsHandler(params) {
6
11
  return `
7
12
  const params = HttpHelper.toHttpParams(${params} || {})
8
13
  `;
9
14
  }
15
+ /**
16
+ * Modify this function according to your needs and your template.
17
+ * This function is used to import anything you need to handle the http params.
18
+ */
10
19
  function httpParamsHandlerImport() {
11
20
  return `import { HttpHelper } from '@umss/shared/utils';`;
12
21
  }
@@ -1,3 +1,8 @@
1
1
  import { InterfaceData } from '../../types/interface-data';
2
+ /**
3
+ * Modify this to add your extends from types.
4
+ * This will be used by the extends from types function
5
+ * to determine if a new interface extends from one of the extends from types.
6
+ */
2
7
  export declare const extendsFromTypes: InterfaceData[];
3
8
  export declare function computeExtendsFromType(newInterface: InterfaceData): InterfaceData;
@@ -2,6 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.extendsFromTypes = void 0;
4
4
  exports.computeExtendsFromType = computeExtendsFromType;
5
+ /**
6
+ * Modify this to add your extends from types.
7
+ * This will be used by the extends from types function
8
+ * to determine if a new interface extends from one of the extends from types.
9
+ */
5
10
  exports.extendsFromTypes = [
6
11
  {
7
12
  name: 'PagedRequestDto',
@@ -1,4 +1,9 @@
1
1
  import { InterfaceData } from '../../types/interface-data';
2
+ /**
3
+ * Modify this to add your generic types.
4
+ * This will be used by the generic types function
5
+ * to determine if a new interface is a generic type.
6
+ */
2
7
  export declare const genericTypesData: InterfaceData[];
3
8
  export declare function isGenericType(newInterface: InterfaceData): InterfaceData | undefined;
4
9
  export declare function computeGenericType(newInterface: InterfaceData, genericType: InterfaceData): string;
@@ -3,6 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.genericTypesData = void 0;
4
4
  exports.isGenericType = isGenericType;
5
5
  exports.computeGenericType = computeGenericType;
6
+ /**
7
+ * Modify this to add your generic types.
8
+ * This will be used by the generic types function
9
+ * to determine if a new interface is a generic type.
10
+ */
6
11
  exports.genericTypesData = [
7
12
  {
8
13
  name: 'PagedResultDto',
@@ -5,5 +5,7 @@ export type ParsedArgs = {
5
5
  export interface ArgsVariables {
6
6
  groupingMode: 'tags' | 'path';
7
7
  segmentsToIgnore: string[];
8
- ignoreVariables: boolean;
8
+ input: string;
9
+ output: string;
10
+ noGenerate: boolean;
9
11
  }
@@ -3,5 +3,4 @@ export declare function getExtraSegments(fullPath: string, baseUrl: string): str
3
3
  export declare function removeVariablesFromPath(path: string): string;
4
4
  export declare function createBaseUrl(baseSegments: string[]): string;
5
5
  export declare function findCommonBaseUrl(paths: string[]): string;
6
- export declare function pathWithoutVariables(segments: string[]): string;
7
6
  export declare function standarizedPath(path: string): string;
@@ -5,54 +5,58 @@ exports.getExtraSegments = getExtraSegments;
5
5
  exports.removeVariablesFromPath = removeVariablesFromPath;
6
6
  exports.createBaseUrl = createBaseUrl;
7
7
  exports.findCommonBaseUrl = findCommonBaseUrl;
8
- exports.pathWithoutVariables = pathWithoutVariables;
9
8
  exports.standarizedPath = standarizedPath;
10
9
  function isVariable(segment) {
11
10
  return /^\{.+\}$/.test(segment);
12
11
  }
13
12
  function getExtraSegments(fullPath, baseUrl) {
14
- if (!fullPath.startsWith('api') && !fullPath.startsWith('/api')) {
15
- fullPath = '/api/' + fullPath;
13
+ const p1 = fullPath.split('/').filter((s) => s !== '');
14
+ const p2 = baseUrl.split('/').filter((s) => s !== '');
15
+ // Find where baseUrl ends in fullPath
16
+ let i = 0;
17
+ while (i < p1.length && i < p2.length && p1[i].toLowerCase() === p2[i].toLowerCase()) {
18
+ i++;
16
19
  }
17
- const path = fullPath.replace(baseUrl, '');
18
- return path.split('/').filter((p) => p !== '');
20
+ return p1.slice(i);
19
21
  }
20
22
  function removeVariablesFromPath(path) {
21
- return path.replace(/{.+}/g, '');
23
+ return path.replace(/\{.+?\}/g, '');
22
24
  }
23
25
  function createBaseUrl(baseSegments) {
24
- if (!baseSegments || baseSegments.length === 0)
25
- return 'api';
26
- if (baseSegments[0] === 'api')
27
- return baseSegments.join('/');
28
- return 'api/' + baseSegments.join('/');
26
+ const filtered = baseSegments.filter((s) => s !== '');
27
+ if (filtered.length === 0)
28
+ return '/api';
29
+ if (filtered[0].toLowerCase() === 'api')
30
+ return '/' + filtered.join('/');
31
+ return '/api/' + filtered.join('/');
29
32
  }
30
33
  function findCommonBaseUrl(paths) {
31
34
  if (!paths || paths.length === 0)
32
35
  return '';
33
- if (paths.length === 1)
34
- return pathWithoutVariables(paths[0].split('/'));
35
- const splittedPaths = paths.map((p) => p.split('/'));
36
+ const splittedPaths = paths.map((p) => p.split('/').filter((s) => s !== ''));
37
+ if (splittedPaths.length === 1)
38
+ return createBaseUrl(pathSegmentsWithoutVariables(splittedPaths[0]));
36
39
  const firstPath = splittedPaths[0];
37
40
  const commonSegments = [];
38
41
  for (let i = 0; i < firstPath.length; i++) {
39
42
  const segment = firstPath[i];
40
43
  const isCommon = splittedPaths.every((p) => p[i] === segment);
41
- if (isCommon) {
44
+ const isVariableSegment = isVariable(segment);
45
+ if (isCommon && !isVariableSegment) {
42
46
  commonSegments.push(segment);
43
47
  }
44
48
  else {
45
49
  break;
46
50
  }
47
51
  }
48
- return pathWithoutVariables(commonSegments);
52
+ return createBaseUrl(commonSegments);
49
53
  }
50
- function pathWithoutVariables(segments) {
54
+ function pathSegmentsWithoutVariables(segments) {
51
55
  const firstVariableIndex = segments.findIndex((segment) => isVariable(segment));
52
56
  if (firstVariableIndex !== -1) {
53
- return segments.slice(0, firstVariableIndex).join('/');
57
+ return segments.slice(0, firstVariableIndex);
54
58
  }
55
- return segments.join('/');
59
+ return segments;
56
60
  }
57
61
  function standarizedPath(path) {
58
62
  const splitted = path.split('/');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swaggular",
3
- "version": "0.1.3",
3
+ "version": "0.2.2",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "bin": {