@necessarylion/adonis-autoswagger 0.0.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.
@@ -0,0 +1,73 @@
1
+ export default class ExampleGenerator {
2
+ schemas: {};
3
+ constructor(schemas: any);
4
+ jsonToRef(json: any): {};
5
+ parseRef(line: string, exampleOnly?: boolean): {};
6
+ exampleByValidatorRule(rule: string): "user@example.com" | "Some string";
7
+ getSchemaExampleBasedOnAnnotation(schema: string, inc?: string, exc?: string, onl?: string, first?: string, parent?: string, deepRels?: string[]): any;
8
+ exampleByType(type: any): any;
9
+ exampleByField(field: any, type?: string): any;
10
+ getPaginatedData(line: string): {
11
+ dataName: string;
12
+ metaName: string;
13
+ };
14
+ }
15
+ export declare abstract class ExampleInterfaces {
16
+ static paginationInterface(): {
17
+ PaginationMeta: {
18
+ type: string;
19
+ properties: {
20
+ total: {
21
+ type: string;
22
+ example: number;
23
+ nullable: boolean;
24
+ };
25
+ page: {
26
+ type: string;
27
+ example: number;
28
+ nullable: boolean;
29
+ };
30
+ perPage: {
31
+ type: string;
32
+ example: number;
33
+ nullable: boolean;
34
+ };
35
+ currentPage: {
36
+ type: string;
37
+ example: number;
38
+ nullable: boolean;
39
+ };
40
+ lastPage: {
41
+ type: string;
42
+ example: number;
43
+ nullable: boolean;
44
+ };
45
+ firstPage: {
46
+ type: string;
47
+ example: number;
48
+ nullable: boolean;
49
+ };
50
+ lastPageUrl: {
51
+ type: string;
52
+ example: string;
53
+ nullable: boolean;
54
+ };
55
+ firstPageUrl: {
56
+ type: string;
57
+ example: string;
58
+ nullable: boolean;
59
+ };
60
+ nextPageUrl: {
61
+ type: string;
62
+ example: string;
63
+ nullable: boolean;
64
+ };
65
+ previousPageUrl: {
66
+ type: string;
67
+ example: string;
68
+ nullable: boolean;
69
+ };
70
+ };
71
+ };
72
+ };
73
+ }
@@ -0,0 +1,349 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExampleInterfaces = void 0;
4
+ const lodash_1 = require("lodash");
5
+ const helpers_1 = require("./helpers");
6
+ class ExampleGenerator {
7
+ schemas = {};
8
+ constructor(schemas) {
9
+ this.schemas = schemas;
10
+ }
11
+ jsonToRef(json) {
12
+ const jsonObjectIsArray = Array.isArray(json);
13
+ let out = {};
14
+ let outArr = [];
15
+ for (let [k, v] of Object.entries(json)) {
16
+ if (typeof v === "object") {
17
+ if (!Array.isArray(v)) {
18
+ v = this.jsonToRef(v);
19
+ }
20
+ }
21
+ if (typeof v === "string") {
22
+ v = this.parseRef(v, true);
23
+ }
24
+ if (jsonObjectIsArray) {
25
+ outArr.push(v);
26
+ }
27
+ else {
28
+ out[k] = v;
29
+ }
30
+ }
31
+ return outArr.length > 0 ? outArr.flat() : out;
32
+ }
33
+ parseRef(line, exampleOnly = false) {
34
+ let rawRef = line.substring(line.indexOf("<") + 1, line.lastIndexOf(">"));
35
+ if (rawRef === "") {
36
+ if (exampleOnly) {
37
+ return line;
38
+ }
39
+ // No format valid, returning the line as text/plain
40
+ return {
41
+ content: {
42
+ "text/plain": {
43
+ example: line,
44
+ },
45
+ },
46
+ };
47
+ }
48
+ let inc = (0, helpers_1.getBetweenBrackets)(line, "with");
49
+ let exc = (0, helpers_1.getBetweenBrackets)(line, "exclude");
50
+ const append = (0, helpers_1.getBetweenBrackets)(line, "append");
51
+ let only = (0, helpers_1.getBetweenBrackets)(line, "only");
52
+ const paginated = (0, helpers_1.getBetweenBrackets)(line, "paginated");
53
+ const serializer = (0, helpers_1.getBetweenBrackets)(line, "serialized");
54
+ if (serializer) {
55
+ // we override to be sure
56
+ inc = "";
57
+ exc = "";
58
+ only = "";
59
+ if (this.schemas[serializer].fields.pick) {
60
+ only += this.schemas[serializer].fields.pick.join(",");
61
+ }
62
+ if (this.schemas[serializer].fields.omit) {
63
+ exc += this.schemas[serializer].fields.omit.join(",");
64
+ }
65
+ if (this.schemas[serializer].relations) {
66
+ // get relations names and add them to inc
67
+ const relations = Object.keys(this.schemas[serializer].relations);
68
+ inc = relations.join(",");
69
+ // we need to add the relation name to only and also we add the relation fields we want to only
70
+ // ex : comment,comment.id,comment.createdAt
71
+ relations.forEach((relation) => {
72
+ const relationFields = this.schemas[serializer].relations[relation].map((field) => relation + "." + field);
73
+ only += "," + relation + "," + relationFields.join(",");
74
+ });
75
+ }
76
+ }
77
+ let app = {};
78
+ try {
79
+ app = JSON.parse("{" + append + "}");
80
+ }
81
+ catch { }
82
+ const cleanedRef = rawRef.replace("[]", "");
83
+ let ex = {};
84
+ try {
85
+ ex = Object.assign(this.getSchemaExampleBasedOnAnnotation(cleanedRef, inc, exc, only), app);
86
+ }
87
+ catch (e) {
88
+ console.error("Error", cleanedRef);
89
+ }
90
+ const { dataName, metaName } = this.getPaginatedData(line);
91
+ const paginatedEx = {
92
+ [dataName]: [ex],
93
+ [metaName]: this.getSchemaExampleBasedOnAnnotation("PaginationMeta"),
94
+ };
95
+ const paginatedSchema = {
96
+ type: "object",
97
+ properties: {
98
+ [dataName]: {
99
+ type: "array",
100
+ items: { $ref: "#/components/schemas/" + cleanedRef },
101
+ },
102
+ [metaName]: { $ref: "#/components/schemas/PaginationMeta" },
103
+ },
104
+ };
105
+ const normalArraySchema = {
106
+ type: "array",
107
+ items: { $ref: "#/components/schemas/" + cleanedRef },
108
+ };
109
+ if (rawRef.includes("[]")) {
110
+ if (exampleOnly) {
111
+ return paginated === "true" ? paginatedEx : [ex];
112
+ }
113
+ return {
114
+ content: {
115
+ "application/json": {
116
+ schema: paginated === "true" ? paginatedSchema : normalArraySchema,
117
+ example: paginated === "true" ? paginatedEx : [ex],
118
+ },
119
+ },
120
+ };
121
+ }
122
+ if (exampleOnly) {
123
+ return ex;
124
+ }
125
+ return {
126
+ content: {
127
+ "application/json": {
128
+ schema: {
129
+ $ref: "#/components/schemas/" + rawRef,
130
+ },
131
+ example: ex,
132
+ },
133
+ },
134
+ };
135
+ }
136
+ exampleByValidatorRule(rule) {
137
+ switch (rule) {
138
+ case "email":
139
+ return "user@example.com";
140
+ default:
141
+ return "Some string";
142
+ }
143
+ }
144
+ getSchemaExampleBasedOnAnnotation(schema, inc = "", exc = "", onl = "", first = "", parent = "", deepRels = [""]) {
145
+ let props = {};
146
+ if (!this.schemas[schema]) {
147
+ return props;
148
+ }
149
+ if (this.schemas[schema].example) {
150
+ return this.schemas[schema].example;
151
+ }
152
+ let properties = this.schemas[schema].properties;
153
+ let include = inc.toString().split(",");
154
+ let exclude = exc.toString().split(",");
155
+ let only = onl.toString().split(",");
156
+ only = only.length === 1 && only[0] === "" ? [] : only;
157
+ if (typeof properties === "undefined")
158
+ return null;
159
+ // skip nested if not requested
160
+ if (parent !== "" &&
161
+ schema !== "" &&
162
+ parent.includes(".") &&
163
+ this.schemas[schema].description.includes("Model") &&
164
+ !inc.includes("relations") &&
165
+ !inc.includes(parent) &&
166
+ !inc.includes(parent + ".relations") &&
167
+ !inc.includes(first + ".relations")) {
168
+ return null;
169
+ }
170
+ deepRels.push(schema);
171
+ for (const [key, value] of Object.entries(properties)) {
172
+ let isArray = false;
173
+ if (exclude.includes(key))
174
+ continue;
175
+ if (exclude.includes(parent + "." + key))
176
+ continue;
177
+ if (key === "password" &&
178
+ !include.includes("password") &&
179
+ !only.includes("password"))
180
+ continue;
181
+ if (key === "password_confirmation" &&
182
+ !include.includes("password_confirmation") &&
183
+ !only.includes("password_confirmation"))
184
+ continue;
185
+ if ((key === "created_at" ||
186
+ key === "updated_at" ||
187
+ key === "deleted_at") &&
188
+ exc.includes("timestamps"))
189
+ continue;
190
+ let rel = "";
191
+ let example = value["example"];
192
+ if (parent === "" && only.length > 0 && !only.includes(key))
193
+ continue;
194
+ // for relations we can select the fields we want with this syntax
195
+ // ex : comment.id,comment.createdAt
196
+ if (parent !== "" &&
197
+ only.length > 0 &&
198
+ !only.includes(parent + "." + key))
199
+ continue;
200
+ if (typeof value["$ref"] !== "undefined") {
201
+ rel = value["$ref"].replace("#/components/schemas/", "");
202
+ }
203
+ if (typeof value["items"] !== "undefined" &&
204
+ typeof value["items"]["$ref"] !== "undefined") {
205
+ rel = value["items"]["$ref"].replace("#/components/schemas/", "");
206
+ }
207
+ if (typeof value["items"] !== "undefined") {
208
+ isArray = true;
209
+ example = value["items"]["example"];
210
+ }
211
+ if (rel !== "") {
212
+ // skip related models of main schema
213
+ if (parent === "" &&
214
+ typeof this.schemas[rel] !== "undefined" &&
215
+ this.schemas[rel].description?.includes("Model") &&
216
+ !include.includes("relations") &&
217
+ !include.includes(key)) {
218
+ continue;
219
+ }
220
+ if (parent !== "" &&
221
+ !include.includes(parent + ".relations") &&
222
+ !include.includes(parent + "." + key)) {
223
+ continue;
224
+ }
225
+ if (typeof value["items"] !== "undefined" &&
226
+ typeof value["items"]["$ref"] !== "undefined") {
227
+ rel = value["items"]["$ref"].replace("#/components/schemas/", "");
228
+ }
229
+ if (rel == "") {
230
+ return;
231
+ }
232
+ let propdata = "";
233
+ // if (!deepRels.includes(rel)) {
234
+ // deepRels.push(rel);
235
+ propdata = this.getSchemaExampleBasedOnAnnotation(rel, inc, exc, onl, parent, parent === "" ? key : parent + "." + key, deepRels);
236
+ if (propdata === null) {
237
+ continue;
238
+ }
239
+ props[key] = isArray ? [propdata] : propdata;
240
+ }
241
+ else {
242
+ props[key] = isArray ? [example] : example;
243
+ }
244
+ }
245
+ return props;
246
+ }
247
+ exampleByType(type) {
248
+ switch (type) {
249
+ case "string":
250
+ return this.exampleByField("title");
251
+ case "number":
252
+ return Math.floor(Math.random() * 1000);
253
+ case "integer":
254
+ return Math.floor(Math.random() * 1000);
255
+ case "boolean":
256
+ return true;
257
+ case "DateTime":
258
+ return this.exampleByField("datetime");
259
+ case "datetime":
260
+ return this.exampleByField("datetime");
261
+ case "date":
262
+ return this.exampleByField("date");
263
+ case "object":
264
+ return {};
265
+ default:
266
+ return null;
267
+ }
268
+ }
269
+ exampleByField(field, type = "") {
270
+ const ex = {
271
+ datetime: "2021-03-23T16:13:08.489+01:00",
272
+ DateTime: "2021-03-23T16:13:08.489+01:00",
273
+ date: "2021-03-23",
274
+ title: "Lorem Ipsum",
275
+ year: 2023,
276
+ description: "Lorem ipsum dolor sit amet",
277
+ name: "John Doe",
278
+ full_name: "John Doe",
279
+ first_name: "John",
280
+ last_name: "Doe",
281
+ email: "johndoe@example.com",
282
+ address: "1028 Farland Street",
283
+ street: "1028 Farland Street",
284
+ country: "United States of America",
285
+ country_code: "US",
286
+ zip: 60617,
287
+ city: "Chicago",
288
+ password: "S3cur3P4s5word!",
289
+ password_confirmation: "S3cur3P4s5word!",
290
+ lat: 41.705,
291
+ long: -87.475,
292
+ price: 10.5,
293
+ avatar: "https://example.com/avatar.png",
294
+ url: "https://example.com",
295
+ };
296
+ if (typeof ex[field] !== "undefined") {
297
+ return ex[field];
298
+ }
299
+ if (typeof ex[(0, lodash_1.snakeCase)(field)] !== "undefined") {
300
+ return ex[(0, lodash_1.snakeCase)(field)];
301
+ }
302
+ return null;
303
+ }
304
+ getPaginatedData(line) {
305
+ const match = line.match(/<.*>\.paginated\((.*)\)/);
306
+ if (!match) {
307
+ return { dataName: "data", metaName: "meta" };
308
+ }
309
+ const params = match[1].split(",").map((s) => s.trim());
310
+ const dataName = params[0] || "data";
311
+ const metaName = params[1] || "meta";
312
+ return { dataName, metaName };
313
+ }
314
+ }
315
+ exports.default = ExampleGenerator;
316
+ class ExampleInterfaces {
317
+ static paginationInterface() {
318
+ return {
319
+ PaginationMeta: {
320
+ type: "object",
321
+ properties: {
322
+ total: { type: "number", example: 100, nullable: false },
323
+ page: { type: "number", example: 2, nullable: false },
324
+ perPage: { type: "number", example: 10, nullable: false },
325
+ currentPage: { type: "number", example: 3, nullable: false },
326
+ lastPage: { type: "number", example: 10, nullable: false },
327
+ firstPage: { type: "number", example: 1, nullable: false },
328
+ lastPageUrl: {
329
+ type: "string",
330
+ example: "/?page=10",
331
+ nullable: false,
332
+ },
333
+ firstPageUrl: {
334
+ type: "string",
335
+ example: "/?page=1",
336
+ nullable: false,
337
+ },
338
+ nextPageUrl: { type: "string", example: "/?page=6", nullable: false },
339
+ previousPageUrl: {
340
+ type: "string",
341
+ example: "/?page=5",
342
+ nullable: false,
343
+ },
344
+ },
345
+ },
346
+ };
347
+ }
348
+ }
349
+ exports.ExampleInterfaces = ExampleInterfaces;
@@ -0,0 +1,7 @@
1
+ export declare function isJSONString(str: string): boolean;
2
+ export declare function getBetweenBrackets(value: string, start: string): string;
3
+ export declare function mergeParams(initial: any, custom: any): any[];
4
+ /**
5
+ * Helpers
6
+ */
7
+ export declare function formatOperationId(inputString: string): string;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isJSONString = isJSONString;
4
+ exports.getBetweenBrackets = getBetweenBrackets;
5
+ exports.mergeParams = mergeParams;
6
+ exports.formatOperationId = formatOperationId;
7
+ /**
8
+ * Check if a string is a valid JSON
9
+ */
10
+ const lodash_1 = require("lodash");
11
+ function isJSONString(str) {
12
+ try {
13
+ JSON.parse(str);
14
+ return true;
15
+ }
16
+ catch (error) {
17
+ return false;
18
+ }
19
+ }
20
+ function getBetweenBrackets(value, start) {
21
+ let match = value.match(new RegExp(start + "\\(([^()]*)\\)", "g"));
22
+ if (match !== null) {
23
+ let m = match[0].replace(start + "(", "").replace(")", "");
24
+ if (start !== "example") {
25
+ m = m.replace(/ /g, "");
26
+ }
27
+ if (start === "paginated") {
28
+ return "true";
29
+ }
30
+ return m;
31
+ }
32
+ return "";
33
+ }
34
+ function mergeParams(initial, custom) {
35
+ let merge = Object.assign(initial, custom);
36
+ let params = [];
37
+ for (const [key, value] of Object.entries(merge)) {
38
+ params.push(value);
39
+ }
40
+ return params;
41
+ }
42
+ /**
43
+ * Helpers
44
+ */
45
+ function formatOperationId(inputString) {
46
+ // Remove non-alphanumeric characters and split the string into words
47
+ const cleanedWords = inputString.replace(/[^a-zA-Z0-9]/g, " ").split(" ");
48
+ // Pascal casing words
49
+ const pascalCasedWords = cleanedWords.map((word) => (0, lodash_1.startCase)((0, lodash_1.camelCase)(word)));
50
+ // Generate operationId by joining every parts
51
+ const operationId = pascalCasedWords.join();
52
+ // CamelCase the operationId
53
+ return (0, lodash_1.camelCase)(operationId);
54
+ }
@@ -0,0 +1,3 @@
1
+ import { AutoSwagger } from "./autoswagger";
2
+ declare const _default: AutoSwagger;
3
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const autoswagger_1 = require("./autoswagger");
4
+ exports.default = new autoswagger_1.AutoSwagger();
@@ -0,0 +1,73 @@
1
+ import ExampleGenerator from "./example";
2
+ import type { options } from "./types";
3
+ import { VineValidator } from "@vinejs/vine";
4
+ export declare class CommentParser {
5
+ private parsedFiles;
6
+ exampleGenerator: ExampleGenerator;
7
+ options: options;
8
+ constructor(options: options);
9
+ private parseAnnotations;
10
+ private parseParam;
11
+ private parseResponseHeader;
12
+ private parseResponseBody;
13
+ private parseRequestFormDataBody;
14
+ private parseBody;
15
+ arrayItems(json: any): {
16
+ oneOf: any[];
17
+ type?: undefined;
18
+ } | {
19
+ type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function";
20
+ oneOf?: undefined;
21
+ };
22
+ jsonToObj(json: any): {
23
+ type: string;
24
+ properties: {
25
+ [x: string]: any;
26
+ };
27
+ };
28
+ getAnnotations(file: string, action: string): Promise<{}>;
29
+ }
30
+ export declare class RouteParser {
31
+ options: options;
32
+ constructor(options: options);
33
+ extractInfos(p: string): {
34
+ tags: any[];
35
+ parameters: {};
36
+ pattern: string;
37
+ };
38
+ }
39
+ export declare class ModelParser {
40
+ exampleGenerator: ExampleGenerator;
41
+ snakeCase: boolean;
42
+ constructor(snakeCase: boolean);
43
+ parseModelProperties(data: any): {
44
+ name: string;
45
+ props: {};
46
+ required: any[];
47
+ };
48
+ }
49
+ export declare class ValidatorParser {
50
+ exampleGenerator: ExampleGenerator;
51
+ constructor();
52
+ validatorToObject(validator: VineValidator<any, any>): Promise<any>;
53
+ parsePropsAndMeta(obj: any, testObj: any, validator: VineValidator<any, any>): Promise<any>;
54
+ objToTest(obj: any): {};
55
+ parseSchema(json: any, refs: any): {};
56
+ }
57
+ export declare class InterfaceParser {
58
+ exampleGenerator: ExampleGenerator;
59
+ snakeCase: boolean;
60
+ schemas: any;
61
+ constructor(snakeCase: boolean, schemas?: any);
62
+ objToExample(obj: any): {};
63
+ parseProps(obj: any): {};
64
+ getInheritedProperties(baseType: string): any;
65
+ parseInterfaces(data: any): {};
66
+ parseExtends(line: string): string[];
67
+ parseType(type: string | any, field: string): any;
68
+ }
69
+ export declare class EnumParser {
70
+ constructor();
71
+ parseEnums(data: string): Record<string, any>;
72
+ private parseEnumValue;
73
+ }