@goast/kotlin 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.
Files changed (51) hide show
  1. package/README.md +7 -0
  2. package/cjs/index.js +9 -0
  3. package/cjs/lib/common-results.js +2 -0
  4. package/cjs/lib/config.js +9 -0
  5. package/cjs/lib/file-builder.js +63 -0
  6. package/cjs/lib/generators/file-generator.js +17 -0
  7. package/cjs/lib/generators/index.js +6 -0
  8. package/cjs/lib/generators/models/index.js +6 -0
  9. package/cjs/lib/generators/models/model-generator.js +337 -0
  10. package/cjs/lib/generators/models/models-generator.js +28 -0
  11. package/cjs/lib/generators/models/models.js +5 -0
  12. package/cjs/lib/generators/services/spring-controllers/index.js +6 -0
  13. package/cjs/lib/generators/services/spring-controllers/models.js +5 -0
  14. package/cjs/lib/generators/services/spring-controllers/spring-controller-generator.js +416 -0
  15. package/cjs/lib/generators/services/spring-controllers/spring-controllers-generator.js +30 -0
  16. package/cjs/lib/import-collection.js +70 -0
  17. package/cjs/lib/utils.js +22 -0
  18. package/cjs/package.json +1 -0
  19. package/esm/index.js +6 -0
  20. package/esm/lib/common-results.js +1 -0
  21. package/esm/lib/config.js +6 -0
  22. package/esm/lib/file-builder.js +59 -0
  23. package/esm/lib/generators/file-generator.js +13 -0
  24. package/esm/lib/generators/index.js +3 -0
  25. package/esm/lib/generators/models/index.js +3 -0
  26. package/esm/lib/generators/models/model-generator.js +333 -0
  27. package/esm/lib/generators/models/models-generator.js +24 -0
  28. package/esm/lib/generators/models/models.js +2 -0
  29. package/esm/lib/generators/services/spring-controllers/index.js +3 -0
  30. package/esm/lib/generators/services/spring-controllers/models.js +2 -0
  31. package/esm/lib/generators/services/spring-controllers/spring-controller-generator.js +412 -0
  32. package/esm/lib/generators/services/spring-controllers/spring-controllers-generator.js +26 -0
  33. package/esm/lib/import-collection.js +66 -0
  34. package/esm/lib/utils.js +17 -0
  35. package/package.json +29 -0
  36. package/types/index.d.ts +6 -0
  37. package/types/lib/common-results.d.ts +4 -0
  38. package/types/lib/config.d.ts +6 -0
  39. package/types/lib/file-builder.d.ts +14 -0
  40. package/types/lib/generators/file-generator.d.ts +8 -0
  41. package/types/lib/generators/index.d.ts +3 -0
  42. package/types/lib/generators/models/index.d.ts +3 -0
  43. package/types/lib/generators/models/model-generator.d.ts +31 -0
  44. package/types/lib/generators/models/models-generator.d.ts +17 -0
  45. package/types/lib/generators/models/models.d.ts +22 -0
  46. package/types/lib/generators/services/spring-controllers/index.d.ts +3 -0
  47. package/types/lib/generators/services/spring-controllers/models.d.ts +24 -0
  48. package/types/lib/generators/services/spring-controllers/spring-controller-generator.d.ts +63 -0
  49. package/types/lib/generators/services/spring-controllers/spring-controllers-generator.d.ts +17 -0
  50. package/types/lib/import-collection.d.ts +13 -0
  51. package/types/lib/utils.d.ts +3 -0
@@ -0,0 +1,412 @@
1
+ import { ensureDirSync, writeFileSync } from 'fs-extra';
2
+ import { toCasing } from '@goast/core';
3
+ import { KotlinFileBuilder } from '../../../file-builder';
4
+ import { KotlinFileGenerator } from '../../file-generator';
5
+ export class DefaultKotlinSpringControllerGenerator extends KotlinFileGenerator {
6
+ generate(ctx) {
7
+ const packageName = this.getPackageName(ctx);
8
+ const dirPath = this.getDirectoryPath(ctx, packageName);
9
+ ensureDirSync(dirPath);
10
+ console.log(`Generating service ${ctx.service.id} to ${dirPath}...`);
11
+ return {
12
+ apiInterface: this.generateApiInterfaceFile(ctx, dirPath, packageName),
13
+ apiController: this.generateApiControllerFile(ctx, dirPath, packageName),
14
+ apiDelegate: this.generateApiDelegateInterfaceFile(ctx, dirPath, packageName),
15
+ };
16
+ }
17
+ generateApiInterfaceFile(ctx, dirPath, packageName) {
18
+ const typeName = this.getApiInterfaceName(ctx);
19
+ const fileName = `${typeName}.kt`;
20
+ const filePath = `${dirPath}/${fileName}`;
21
+ console.log(` Generating API interface ${typeName} to ${fileName}...`);
22
+ const builder = new KotlinFileBuilder(packageName, ctx.config);
23
+ this.generateApiInterfaceFileContent(ctx, builder);
24
+ writeFileSync(filePath, builder.toString());
25
+ return { typeName: builder.toString(), packageName };
26
+ }
27
+ generateApiInterfaceFileContent(ctx, builder) {
28
+ builder
29
+ .apply((builder) => this.generateApiInterfaceAnnotations(ctx, builder))
30
+ .ensureCurrentLineEmpty()
31
+ .apply((builder) => this.generateApiInterfaceSignature(ctx, builder))
32
+ .append(' ')
33
+ .parenthesize('{}', (builder) => builder
34
+ .appendLine()
35
+ .apply((builder) => this.generateApiInterfaceContent(ctx, builder))
36
+ .ensureCurrentLineEmpty());
37
+ }
38
+ generateApiInterfaceAnnotations(ctx, builder) {
39
+ builder
40
+ .appendAnnotation('Validated', 'org.springframework.validation.annotation')
41
+ .appendAnnotation('RequestMapping', 'org.springframework.web.bind.annotation', [
42
+ this.getControllerRequestMapping(ctx, 'api'),
43
+ ]);
44
+ }
45
+ generateApiInterfaceSignature(ctx, builder) {
46
+ builder.append('interface ').append(this.getApiInterfaceName(ctx));
47
+ }
48
+ generateApiInterfaceContent(ctx, builder) {
49
+ builder
50
+ .apply((builder) => this.generateApiInterfaceDelegateAccessor(ctx, builder))
51
+ .ensurePreviousLineEmpty()
52
+ .apply((builder) => this.generateApiInterfaceMethods(ctx, builder));
53
+ }
54
+ generateApiInterfaceDelegateAccessor(ctx, builder) {
55
+ builder.appendLine(`fun getDelegate(): ${this.getApiDelegateInterfaceName(ctx)} = object : ${this.getApiDelegateInterfaceName(ctx)} {}`);
56
+ }
57
+ generateApiInterfaceMethods(ctx, builder) {
58
+ builder.forEach(ctx.service.endpoints, (builder, endpoint) => builder.ensurePreviousLineEmpty().apply((builder) => this.generateApiInterfaceMethod(ctx, builder, endpoint)));
59
+ }
60
+ generateApiInterfaceMethod(ctx, builder, endpoint) {
61
+ builder
62
+ .apply((builder) => this.generateApiInterfaceMethodAnnnotations(ctx, builder, endpoint))
63
+ .ensureCurrentLineEmpty()
64
+ .apply((builder) => this.generateApiInterfaceMethodSignature(ctx, builder, endpoint))
65
+ .append(' ')
66
+ .parenthesize('{}', (builder) => builder
67
+ .appendLine()
68
+ .apply((builder) => this.generateApiInterfaceMethodContent(ctx, builder, endpoint))
69
+ .appendLine());
70
+ }
71
+ generateApiInterfaceMethodAnnnotations(ctx, builder, endpoint) {
72
+ var _a, _b, _c;
73
+ builder
74
+ .appendAnnotation('Operation', 'io.swagger.v3.oas.annotations', [
75
+ ['summary', this.toStringLiteral(ctx, (_a = endpoint.summary) === null || _a === void 0 ? void 0 : _a.trim())],
76
+ ['operationId', this.toStringLiteral(ctx, endpoint.name)],
77
+ ['description', this.toStringLiteral(ctx, (_b = endpoint.description) === null || _b === void 0 ? void 0 : _b.trim())],
78
+ [
79
+ 'responses',
80
+ (builder) => builder.parenthesize('[]', (builder) => builder
81
+ .appendLine()
82
+ .forEachSeparated(endpoint.responses, ',\n', (builder, response) => builder
83
+ .append('ApiResponse')
84
+ .addImport('ApiResponse', 'io.swagger.v3.oas.annotations.responses')
85
+ .parenthesize('()', (builder) => {
86
+ var _a, _b;
87
+ return builder
88
+ .append(`responseCode = ${this.toStringLiteral(ctx, (_a = response.statusCode) === null || _a === void 0 ? void 0 : _a.toString())}, `)
89
+ .append(`description = ${this.toStringLiteral(ctx, (_b = response.description) === null || _b === void 0 ? void 0 : _b.trim())}`);
90
+ }))
91
+ .appendLine()),
92
+ endpoint.responses.length > 0,
93
+ ],
94
+ ])
95
+ .appendAnnotation('RequestMapping', 'org.springframework.web.bind.annotation', [
96
+ ['method', '[RequestMethod.' + endpoint.method.toUpperCase() + ']'],
97
+ ['value', '[' + this.toStringLiteral(ctx, endpoint.path) + ']'],
98
+ [
99
+ 'consumes',
100
+ '[' + ((_c = endpoint.requestBody) === null || _c === void 0 ? void 0 : _c.content.map((x) => this.toStringLiteral(ctx, x.type)).join(', ')) + ']',
101
+ !!endpoint.requestBody && endpoint.requestBody.content.length > 0,
102
+ ],
103
+ ])
104
+ .addImport('RequestMethod', 'org.springframework.web.bind.annotation');
105
+ }
106
+ generateApiInterfaceMethodSignature(ctx, builder, endpoint) {
107
+ builder
108
+ .append(`suspend fun ${toCasing(endpoint.name, 'camel')}`)
109
+ .parenthesize('()', (builder) => builder
110
+ .appendLine()
111
+ .apply((builder) => this.generateApiInterfaceMethodParameters(ctx, builder, endpoint))
112
+ .appendLine())
113
+ .append(': ')
114
+ .apply((builder) => this.generateApiInterfaceMethodReturnType(ctx, builder, endpoint));
115
+ }
116
+ generateApiInterfaceMethodParameters(ctx, builder, endpoint) {
117
+ const parameters = this.getAllParameters(ctx, endpoint);
118
+ builder.forEachSeparated(parameters, ',\n', (builder, parameter) => this.generateApiInterfaceMethodParameter(ctx, builder, endpoint, parameter));
119
+ }
120
+ generateApiInterfaceMethodReturnType(ctx, builder, endpoint) {
121
+ var _a, _b;
122
+ this.generateResponseEntityType(ctx, builder, (_b = (_a = endpoint.responses[0]) === null || _a === void 0 ? void 0 : _a.contentOptions[0]) === null || _b === void 0 ? void 0 : _b.schema);
123
+ }
124
+ generateApiInterfaceMethodParameter(ctx, builder, endpoint, parameter) {
125
+ builder
126
+ .apply((builder) => this.generateApiInterfaceMethodParameterAnnotations(ctx, builder, endpoint, parameter))
127
+ .ensureCurrentLineEmpty()
128
+ .apply((builder) => this.generateApiInterfaceMethodParameterSignature(ctx, builder, endpoint, parameter));
129
+ }
130
+ generateApiInterfaceMethodParameterAnnotations(ctx, builder, endpoint, parameter) {
131
+ var _a, _b, _c, _d, _e, _f, _g, _h;
132
+ const parameterSchemaInfo = this.getSchemaInfo(ctx, parameter.schema);
133
+ if (((_a = parameter.schema) === null || _a === void 0 ? void 0 : _a.default) !== undefined) {
134
+ builder.addImport('Schema', 'io.swagger.v3.oas.annotations.media');
135
+ }
136
+ builder.appendAnnotation('Parameter', 'io.swagger.v3.oas.annotations', [
137
+ ['description', this.toStringLiteral(ctx, (_b = parameter.description) === null || _b === void 0 ? void 0 : _b.trim())],
138
+ ['required', (_c = parameter.required) === null || _c === void 0 ? void 0 : _c.toString()],
139
+ [
140
+ 'schema',
141
+ `Schema(defaultValue = ${this.toStringLiteral(ctx, String((_d = parameter.schema) === null || _d === void 0 ? void 0 : _d.default))})`,
142
+ ((_e = parameter.schema) === null || _e === void 0 ? void 0 : _e.default) !== undefined,
143
+ ],
144
+ ]);
145
+ if (parameterSchemaInfo.packageName) {
146
+ builder.appendAnnotation('Valid', 'jakarta.validation');
147
+ }
148
+ if (parameter.target === 'body') {
149
+ builder.appendAnnotation('RequestBody', 'org.springframework.web.bind.annotation');
150
+ }
151
+ if (parameter.target === 'query') {
152
+ builder.appendAnnotation('RequestParam', 'org.springframework.web.bind.annotation', [
153
+ ['value', this.toStringLiteral(ctx, parameter.name)],
154
+ ['required', (_f = parameter.required) === null || _f === void 0 ? void 0 : _f.toString()],
155
+ [
156
+ 'defaultValue',
157
+ this.toStringLiteral(ctx, String((_g = parameter.schema) === null || _g === void 0 ? void 0 : _g.default)),
158
+ ((_h = parameter.schema) === null || _h === void 0 ? void 0 : _h.default) !== undefined,
159
+ ],
160
+ ]);
161
+ }
162
+ if (parameter.target === 'path') {
163
+ builder.appendAnnotation('PathVariable', 'org.springframework.web.bind.annotation', [
164
+ this.toStringLiteral(ctx, parameter.name),
165
+ ]);
166
+ }
167
+ }
168
+ generateApiInterfaceMethodParameterSignature(ctx, builder, endpoint, parameter) {
169
+ builder
170
+ .append(toCasing(parameter.name, 'camel'))
171
+ .append(': ')
172
+ .apply((builder) => this.generateTypeUsage(ctx, builder, parameter.schema));
173
+ }
174
+ generateApiInterfaceMethodContent(ctx, builder, endpoint) {
175
+ const parameters = this.getAllParameters(ctx, endpoint);
176
+ builder
177
+ .append(`return getDelegate().${toCasing(endpoint.name, 'camel')}(`)
178
+ .forEachSeparated(parameters, ', ', (builder, parameter) => builder.append(toCasing(parameter.name, 'camel')))
179
+ .append(')');
180
+ }
181
+ generateApiControllerFile(ctx, dirPath, packageName) {
182
+ const typeName = this.getApiControllerName(ctx);
183
+ const fileName = `${typeName}.kt`;
184
+ const filePath = `${dirPath}/${fileName}`;
185
+ console.log(` Generating API controller ${typeName} to ${fileName}...`);
186
+ const builder = new KotlinFileBuilder(packageName, ctx.config);
187
+ this.generateApiControllerFileContent(ctx, builder);
188
+ writeFileSync(filePath, builder.toString());
189
+ return { typeName: builder.toString(), packageName };
190
+ }
191
+ generateApiControllerFileContent(ctx, builder) {
192
+ builder
193
+ .apply((builder) => this.generateApiControllerAnnotations(ctx, builder))
194
+ .ensureCurrentLineEmpty()
195
+ .apply((builder) => this.generateApiControllerSignature(ctx, builder))
196
+ .append(' ')
197
+ .parenthesize('{}', (builder) => builder
198
+ .appendLine()
199
+ .apply((builder) => this.generateApiControllerContent(ctx, builder))
200
+ .ensureCurrentLineEmpty());
201
+ }
202
+ generateApiControllerAnnotations(ctx, builder) {
203
+ builder
204
+ .appendAnnotation('Generated', 'jakarta.annotation', [
205
+ ['value', '[' + this.toStringLiteral(ctx, 'com.goast.kotlin.spring-service-generator') + ']'],
206
+ ])
207
+ .appendAnnotation('Controller', 'org.springframework.stereotype')
208
+ .appendAnnotation('RequestMapping', 'org.springframework.web.bind.annotation', [
209
+ this.getControllerRequestMapping(ctx),
210
+ ]);
211
+ }
212
+ generateApiControllerSignature(ctx, builder) {
213
+ builder
214
+ .append('class ')
215
+ .append(this.getApiControllerName(ctx))
216
+ .parenthesize('()', (builder) => builder
217
+ .appendLine()
218
+ .apply((builder) => this.generateApiControllerParameters(ctx, builder))
219
+ .ensureCurrentLineEmpty())
220
+ .append(' : ')
221
+ .append(this.getApiInterfaceName(ctx));
222
+ }
223
+ generateApiControllerParameters(ctx, builder) {
224
+ builder
225
+ .appendAnnotation('Autowired', 'org.springframework.beans.factory.annotation', [['required', 'false']])
226
+ .append('delegate: ')
227
+ .append(this.getApiDelegateInterfaceName(ctx))
228
+ .append('?');
229
+ }
230
+ generateApiControllerContent(ctx, builder) {
231
+ builder
232
+ .append('private val delegate: ')
233
+ .appendLine(this.getApiDelegateInterfaceName(ctx))
234
+ .appendLine()
235
+ .append('init ')
236
+ .parenthesize('{}', (builder) => builder
237
+ .appendLine()
238
+ .append('this.delegate = Optional.ofNullable(delegate).orElse')
239
+ .addImport('Optional', 'java.util')
240
+ .parenthesize('()', (builder) => builder.append('object : ').append(this.getApiDelegateInterfaceName(ctx)).append(' {}'))
241
+ .appendLine())
242
+ .appendLine()
243
+ .appendLine()
244
+ .appendLine(`override fun getDelegate(): ${this.getApiDelegateInterfaceName(ctx)} = delegate`);
245
+ }
246
+ generateApiDelegateInterfaceFile(ctx, dirPath, packageName) {
247
+ const typeName = this.getApiDelegateInterfaceName(ctx);
248
+ const fileName = `${typeName}.kt`;
249
+ const filePath = `${dirPath}/${fileName}`;
250
+ console.log(` Generating API delegate ${typeName} to ${fileName}...`);
251
+ const builder = new KotlinFileBuilder(packageName, ctx.config);
252
+ this.generateApiDelegateInterfaceFileContent(ctx, builder);
253
+ writeFileSync(filePath, builder.toString());
254
+ return { typeName: builder.toString(), packageName };
255
+ }
256
+ generateApiDelegateInterfaceFileContent(ctx, builder) {
257
+ builder
258
+ .apply((builder) => this.generateApiDelegateInterfaceAnnotations(ctx, builder))
259
+ .ensureCurrentLineEmpty()
260
+ .apply((builder) => this.generateApiDelegateInterfaceSignature(ctx, builder))
261
+ .append(' ')
262
+ .parenthesize('{}', (builder) => builder
263
+ .appendLine()
264
+ .apply((builder) => this.generateApiDelegateInterfaceContent(ctx, builder))
265
+ .ensureCurrentLineEmpty());
266
+ }
267
+ generateApiDelegateInterfaceAnnotations(ctx, builder) {
268
+ builder.appendAnnotation('Generated', 'jakarta.annotation', [
269
+ ['value', '[' + this.toStringLiteral(ctx, 'com.goast.kotlin.spring-service-generator') + ']'],
270
+ ]);
271
+ }
272
+ generateApiDelegateInterfaceSignature(ctx, builder) {
273
+ builder.append('interface ').append(this.getApiDelegateInterfaceName(ctx));
274
+ }
275
+ generateApiDelegateInterfaceContent(ctx, builder) {
276
+ builder
277
+ .appendLine(`fun getRequest(): Optional<NativeWebRequest> = Optional.empty()`)
278
+ .addImport('Optional', 'java.util')
279
+ .addImport('NativeWebRequest', 'org.springframework.web.context.request')
280
+ .forEach(ctx.service.endpoints, (builder, endpoint) => builder
281
+ .ensurePreviousLineEmpty()
282
+ .apply((builder) => this.generateApiDelegateInterfaceMethod(ctx, builder, endpoint)));
283
+ }
284
+ generateApiDelegateInterfaceMethod(ctx, builder, endpoint) {
285
+ builder
286
+ .apply((builder) => this.generateApiDelegateInterfaceMethodAnnnotations(ctx, builder, endpoint))
287
+ .ensureCurrentLineEmpty()
288
+ .apply((builder) => this.generateApiDelegateInterfaceMethodSignature(ctx, builder, endpoint))
289
+ .append(' ')
290
+ .parenthesize('{}', (builder) => builder
291
+ .appendLine()
292
+ .apply((builder) => this.generateApiDelegateInterfaceMethodContent(ctx, builder, endpoint))
293
+ .ensureCurrentLineEmpty());
294
+ }
295
+ generateApiDelegateInterfaceMethodAnnnotations(ctx, builder, endpoint) {
296
+ // None for now.
297
+ }
298
+ generateApiDelegateInterfaceMethodSignature(ctx, builder, endpoint) {
299
+ builder
300
+ .append(`suspend fun ${toCasing(endpoint.name, 'camel')}`)
301
+ .parenthesize('()', (builder) => builder
302
+ .appendLine()
303
+ .apply((builder) => this.generateApiDelegateInterfaceMethodParameters(ctx, builder, endpoint))
304
+ .appendLine())
305
+ .append(': ')
306
+ .apply((builder) => this.generateApiDelegateInterfaceMethodReturnType(ctx, builder, endpoint));
307
+ }
308
+ generateApiDelegateInterfaceMethodParameters(ctx, builder, endpoint) {
309
+ const parameters = this.getAllParameters(ctx, endpoint);
310
+ builder.forEachSeparated(parameters, ',\n', (builder, parameter) => this.generateApiDelegateInterfaceMethodParameter(ctx, builder, endpoint, parameter));
311
+ }
312
+ generateApiDelegateInterfaceMethodReturnType(ctx, builder, endpoint) {
313
+ var _a, _b;
314
+ this.generateResponseEntityType(ctx, builder, (_b = (_a = endpoint.responses[0]) === null || _a === void 0 ? void 0 : _a.contentOptions[0]) === null || _b === void 0 ? void 0 : _b.schema);
315
+ }
316
+ generateApiDelegateInterfaceMethodParameter(ctx, builder, endpoint, parameter) {
317
+ builder
318
+ .apply((builder) => this.generateApiDelegateInterfaceMethodParameterAnnotations(ctx, builder, endpoint, parameter))
319
+ .ensureCurrentLineEmpty()
320
+ .apply((builder) => this.generateApiDelegateInterfaceMethodParameterSignature(ctx, builder, endpoint, parameter));
321
+ }
322
+ generateApiDelegateInterfaceMethodParameterAnnotations(ctx, builder, endpoint, parameter) {
323
+ // None for now.
324
+ }
325
+ generateApiDelegateInterfaceMethodParameterSignature(ctx, builder, endpoint, parameter) {
326
+ builder
327
+ .append(toCasing(parameter.name, 'camel'))
328
+ .append(': ')
329
+ .apply((builder) => this.generateTypeUsage(ctx, builder, parameter.schema));
330
+ }
331
+ generateApiDelegateInterfaceMethodContent(ctx, builder, endpoint) {
332
+ builder
333
+ .appendLine('return ResponseEntity(HttpStatus.NOT_IMPLEMENTED)')
334
+ .addImport('ResponseEntity', 'org.springframework.http')
335
+ .addImport('HttpStatus', 'org.springframework.http');
336
+ }
337
+ generateResponseEntityType(ctx, builder, schema) {
338
+ builder
339
+ .append('ResponseEntity')
340
+ .addImport('ResponseEntity', 'org.springframework.http')
341
+ .parenthesize('<>', (builder) => this.generateTypeUsage(ctx, builder, schema, 'Unit'));
342
+ }
343
+ generateTypeUsage(ctx, builder, schema, fallback) {
344
+ if (schema && schema.kind === 'array') {
345
+ const schemaInfo = this.getSchemaInfo(ctx, schema.items);
346
+ builder.append(`Flux<${schemaInfo.typeName}>`).addImport('Flux', 'reactor.core.publisher');
347
+ builder.imports.addImports([schemaInfo, ...schemaInfo.additionalImports]);
348
+ }
349
+ else if (schema || !fallback) {
350
+ const schemaInfo = this.getSchemaInfo(ctx, schema);
351
+ builder.append(schemaInfo.typeName);
352
+ builder.imports.addImports([schemaInfo, ...schemaInfo.additionalImports]);
353
+ }
354
+ else {
355
+ builder.append(fallback);
356
+ }
357
+ }
358
+ getControllerRequestMapping(ctx, prefix) {
359
+ var _a, _b, _c, _d, _e, _f;
360
+ const basePath = (_f = (_e = (_d = (_c = ((_a = ctx.service.$src) !== null && _a !== void 0 ? _a : (_b = ctx.service.endpoints[0]) === null || _b === void 0 ? void 0 : _b.$src)) === null || _c === void 0 ? void 0 : _c.document.servers) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.url) !== null && _f !== void 0 ? _f : '/';
361
+ prefix !== null && prefix !== void 0 ? prefix : (prefix = `openapi.${toCasing(ctx.service.name, 'camel')}`);
362
+ return this.toStringLiteral(ctx, `\${${prefix}.base-path:${basePath}}`);
363
+ }
364
+ getDirectoryPath(ctx, packageName) {
365
+ return `${ctx.config.outputDir}/${packageName.replace(/\./g, '/')}`;
366
+ }
367
+ getPackageName(ctx) {
368
+ return ctx.config.packageName + ctx.config.packageSuffix;
369
+ }
370
+ getApiInterfaceName(ctx) {
371
+ return toCasing(ctx.service.name, 'pascal') + 'Api';
372
+ }
373
+ getApiControllerName(ctx) {
374
+ return toCasing(ctx.service.name, 'pascal') + 'ApiController';
375
+ }
376
+ getApiDelegateInterfaceName(ctx) {
377
+ return toCasing(ctx.service.name, 'pascal') + 'ApiDelegate';
378
+ }
379
+ getSchemaInfo(ctx, schema) {
380
+ var _a;
381
+ return ((_a = (schema && ctx.input.models[schema.id])) !== null && _a !== void 0 ? _a : { typeName: 'Any?', packageName: undefined, additionalImports: [] });
382
+ }
383
+ getAllParameters(ctx, endpoint) {
384
+ const parameters = endpoint.parameters.filter((parameter) => parameter.target === 'query' || parameter.target === 'path');
385
+ if (endpoint.requestBody) {
386
+ const schema = endpoint.requestBody.content[0].schema;
387
+ parameters.push({
388
+ $src: undefined,
389
+ $ref: undefined,
390
+ id: 'body',
391
+ name: schema ? this.getSchemaInfo(ctx, schema).typeName : 'body',
392
+ target: 'body',
393
+ schema,
394
+ required: endpoint.requestBody.required,
395
+ description: endpoint.requestBody.description,
396
+ allowEmptyValue: undefined,
397
+ allowReserved: undefined,
398
+ deprecated: false,
399
+ explode: undefined,
400
+ style: undefined,
401
+ });
402
+ }
403
+ return parameters;
404
+ }
405
+ sortParameters(ctx, parameters) {
406
+ return [...parameters].sort((a, b) => {
407
+ const aRequired = a.required ? 1 : 0;
408
+ const bRequired = b.required ? 1 : 0;
409
+ return aRequired - bRequired;
410
+ });
411
+ }
412
+ }
@@ -0,0 +1,26 @@
1
+ import { Factory, OpenApiServicesGenerationProviderBase } from '@goast/core';
2
+ import { defaultKotlinServicesGeneratorConfig, } from './models';
3
+ import { DefaultKotlinSpringControllerGenerator } from './spring-controller-generator';
4
+ export class KotlinSpringControllersGenerator extends OpenApiServicesGenerationProviderBase {
5
+ constructor(serviceGeneratorFactory) {
6
+ super();
7
+ this._serviceGeneratorFactory =
8
+ serviceGeneratorFactory !== null && serviceGeneratorFactory !== void 0 ? serviceGeneratorFactory : Factory.fromValue(new DefaultKotlinSpringControllerGenerator());
9
+ }
10
+ initResult() {
11
+ return {
12
+ services: {},
13
+ };
14
+ }
15
+ generateService(ctx, service) {
16
+ const serviceGenerator = this._serviceGeneratorFactory.create();
17
+ return serviceGenerator.generate(Object.assign(Object.assign({}, ctx), { service }));
18
+ }
19
+ addServiceResult(ctx, service, result) {
20
+ ctx.output.services[service.id] = result;
21
+ }
22
+ buildContext(context, config) {
23
+ context.data.services = context.data.services.filter((x) => x.name !== 'exclude-from-generation');
24
+ return this.getProviderContext(context, config, defaultKotlinServicesGeneratorConfig);
25
+ }
26
+ }
@@ -0,0 +1,66 @@
1
+ import { StringBuilder } from '@goast/core';
2
+ export class ImportCollection {
3
+ constructor() {
4
+ this._imports = new Map();
5
+ }
6
+ get hasImports() {
7
+ return this._imports.size > 0;
8
+ }
9
+ get imports() {
10
+ return Array.from(this._imports.entries())
11
+ .map(([fromPackage, importNames]) => Array.from(importNames).map((importName) => ({
12
+ packageName: fromPackage,
13
+ typeName: importName,
14
+ })))
15
+ .flat();
16
+ }
17
+ addImport(importOrImportName, fromModule) {
18
+ const importName = typeof importOrImportName === 'string' ? importOrImportName : importOrImportName.typeName;
19
+ fromModule = typeof importOrImportName === 'string' ? fromModule : importOrImportName.packageName;
20
+ if (!fromModule) {
21
+ return;
22
+ }
23
+ const existingImport = this._imports.get(fromModule);
24
+ if (existingImport) {
25
+ existingImport.add(importName);
26
+ }
27
+ else {
28
+ this._imports.set(fromModule, new Set([importName]));
29
+ }
30
+ }
31
+ addImports(imports) {
32
+ for (const importObj of imports) {
33
+ this.addImport(importObj);
34
+ }
35
+ }
36
+ clear() {
37
+ this._imports.clear();
38
+ }
39
+ toString(options) {
40
+ const builder = new StringBuilder(options);
41
+ this.writeTo(builder);
42
+ return builder.toString();
43
+ }
44
+ writeTo(builder) {
45
+ if (this._imports.size > 0) {
46
+ const sortedImports = Array.from(this._imports.entries()).sort(([fromModuleA], [fromModuleB]) => {
47
+ if (isCoreImport(fromModuleA) && !isCoreImport(fromModuleB)) {
48
+ return 1;
49
+ }
50
+ else if (!isCoreImport(fromModuleA) && isCoreImport(fromModuleB)) {
51
+ return -1;
52
+ }
53
+ return fromModuleA.localeCompare(fromModuleB);
54
+ });
55
+ for (const [fromPackage, importNames] of sortedImports) {
56
+ const sortedImportNames = Array.from(importNames).sort();
57
+ for (const importName of sortedImportNames) {
58
+ builder.appendLine(`import ${fromPackage}.${importName}`);
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ function isCoreImport(packageName) {
65
+ return packageName.startsWith('kotlin.') || packageName.startsWith('java.') || packageName.startsWith('javax.');
66
+ }
@@ -0,0 +1,17 @@
1
+ export function toKotlinStringLiteral(value) {
2
+ if (!value) {
3
+ return '""';
4
+ }
5
+ const escaped = value
6
+ .replace(/(["\\$])/g, '\\$1')
7
+ .replace(/\n/g, '\\n')
8
+ .replace(/\r/g, '\\r')
9
+ .replace(/\t/g, '\\t');
10
+ return `"${escaped}"`;
11
+ }
12
+ export function toKotlinPropertyName(value) {
13
+ if (value.match(/^[a-zA-Z_$][a-zA-Z_$0-9]*$/)) {
14
+ return value;
15
+ }
16
+ return `\`${value}\``;
17
+ }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@goast/kotlin",
3
+ "version": "0.0.1",
4
+ "repository": "https://github.com/MaSch0212/goast.git",
5
+ "author": {
6
+ "name": "Marc Schmidt (MaSch0212)",
7
+ "url": "https://github.com/MaSch0212"
8
+ },
9
+ "license": "MIT",
10
+ "type": "module",
11
+ "dependencies": {
12
+ "fs-extra": "^11.1.1",
13
+ "node-fetch": "^2.6.11",
14
+ "yaml": "^2.3.1",
15
+ "@goast/core": "0.0.5",
16
+ "tslib": "^2.3.0"
17
+ },
18
+ "module": "./esm/index.js",
19
+ "main": "./cjs/index.js",
20
+ "types": "./types/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "import": "./esm/index.js",
24
+ "require": "./cjs/index.js",
25
+ "types": "./types/index.d.ts"
26
+ },
27
+ "./package.json": "./package.json"
28
+ }
29
+ }
@@ -0,0 +1,6 @@
1
+ export * from './lib/common-results';
2
+ export * from './lib/config';
3
+ export * from './lib/generators';
4
+ export * from './lib/file-builder';
5
+ export * from './lib/import-collection';
6
+ export * from './lib/utils';
@@ -0,0 +1,4 @@
1
+ export type KotlinImport = {
2
+ packageName?: string;
3
+ typeName: string;
4
+ };
@@ -0,0 +1,6 @@
1
+ import { DefaultGenerationProviderConfig, OpenApiGeneratorConfig, StringCasing, StringCasingWithOptions } from '@goast/core';
2
+ export type KotlinGeneratorConfig = OpenApiGeneratorConfig & {
3
+ propertyNameCasing: StringCasing | StringCasingWithOptions;
4
+ enumValueNameCasing: StringCasing | StringCasingWithOptions;
5
+ };
6
+ export declare const defaultKotlinGeneratorConfig: DefaultGenerationProviderConfig<KotlinGeneratorConfig>;
@@ -0,0 +1,14 @@
1
+ import { SourceBuilder, SourceBuilderOptions } from '@goast/core';
2
+ import { ImportCollection } from './import-collection';
3
+ type AnnotationArgumentValue<T extends SourceBuilder> = string | ((builder: T) => void);
4
+ type AnnotationArgument<T extends SourceBuilder> = AnnotationArgumentValue<T> | [key: string, value: AnnotationArgumentValue<T>] | [value: AnnotationArgumentValue<T>, condition: boolean] | [key: string, value: AnnotationArgumentValue<T>, condition: boolean];
5
+ export declare class KotlinFileBuilder extends SourceBuilder {
6
+ readonly packageName: string | undefined;
7
+ readonly imports: ImportCollection;
8
+ constructor(packageName: string | undefined, options: SourceBuilderOptions);
9
+ addImport(name: string, packageName?: string): this;
10
+ appendAnnotation(name: string, packageName: string | undefined, args?: AnnotationArgument<this>[]): this;
11
+ clear(): void;
12
+ toString(addPadding?: boolean): string;
13
+ }
14
+ export {};
@@ -0,0 +1,8 @@
1
+ import { Nullable, OpenApiGenerationProviderContext, OpenApiGeneratorInput } from '@goast/core';
2
+ import { KotlinGeneratorConfig } from '../config';
3
+ export declare abstract class KotlinFileGenerator<TContext extends OpenApiGenerationProviderContext<OpenApiGeneratorInput, KotlinGeneratorConfig>, TOutput> {
4
+ abstract generate(context: TContext): TOutput;
5
+ protected toPropertyName(context: TContext, name: string): string;
6
+ protected toEnumValueName(context: TContext, name: string): string;
7
+ protected toStringLiteral(context: TContext, text: Nullable<string>): string;
8
+ }
@@ -0,0 +1,3 @@
1
+ export * from './file-generator';
2
+ export * from './models';
3
+ export * from './services/spring-controllers';
@@ -0,0 +1,3 @@
1
+ export * from './model-generator';
2
+ export * from './models';
3
+ export * from './models-generator';
@@ -0,0 +1,31 @@
1
+ import { ApiSchema, ApiSchemaProperty } from '@goast/core';
2
+ import { KotlinModelGeneratorContext, KotlinModelGeneratorOutput } from './models';
3
+ import { KotlinFileBuilder } from '../../file-builder';
4
+ import { KotlinFileGenerator } from '../file-generator';
5
+ type Context = KotlinModelGeneratorContext;
6
+ type Output = KotlinModelGeneratorOutput;
7
+ type Builder = KotlinFileBuilder;
8
+ export interface KotlinModelGenerator<TOutput extends Output = Output> {
9
+ generate(ctx: Context): TOutput;
10
+ }
11
+ export declare class DefaultKotlinModelGenerator extends KotlinFileGenerator<Context, Output> implements KotlinModelGenerator {
12
+ generate(ctx: KotlinModelGeneratorContext): KotlinModelGeneratorOutput;
13
+ protected generateFileContent(ctx: Context, builder: Builder): void;
14
+ protected generateObjectType(ctx: Context, builder: Builder, schema: ApiSchema<'object'>): void;
15
+ protected generateDataClass(ctx: Context, builder: Builder, schema: ApiSchema<'object'>): void;
16
+ protected generatePropertyAnnotations(ctx: Context, builder: Builder, schema: ApiSchema, property: ApiSchemaProperty): void;
17
+ protected generatePropertyValidationAnnotations(ctx: Context, builder: Builder, schema: ApiSchema, property: ApiSchemaProperty): void;
18
+ protected generatePropertySchemaAnnotation(ctx: Context, builder: Builder, schema: ApiSchema, property: ApiSchemaProperty): void;
19
+ protected generateJsonPropertyAnnotation(ctx: Context, builder: Builder, schema: ApiSchema, property: ApiSchemaProperty): void;
20
+ protected generateMapType(ctx: Context, builder: Builder, schema: ApiSchema<'object'>): void;
21
+ protected generateType(ctx: Context, builder: Builder, schema: ApiSchema): void;
22
+ protected generateNumberType(ctx: Context, builder: Builder, schema: ApiSchema<'number' | 'integer'>): void;
23
+ protected generateStringType(ctx: Context, builder: Builder, schema: ApiSchema<'string'>): void;
24
+ protected generateEnum(ctx: Context, builder: Builder, schema: ApiSchema): void;
25
+ protected generateArrayType(ctx: Context, builder: Builder, schema: ApiSchema<'array'>): void;
26
+ protected generateDocumentation(ctx: Context, builder: Builder, schema: ApiSchema): void;
27
+ protected shouldGenerateTypeDeclaration(ctx: Context, schema: ApiSchema): boolean;
28
+ protected getDeclarationTypeName(ctx: Context): string;
29
+ protected sortProperties(ctx: Context, schema: ApiSchema, properties: Iterable<ApiSchemaProperty>): ApiSchemaProperty[];
30
+ }
31
+ export {};
@@ -0,0 +1,17 @@
1
+ import { ApiSchema, Factory, OpenApiGeneratorContext, OpenApiSchemasGenerationProviderBase } from '@goast/core';
2
+ import { KotlinModelGenerator } from './model-generator';
3
+ import { KotlinModelsGeneratorInput, KotlinModelsGeneratorOutput, KotlinModelsGeneratorConfig, KotlinModelGeneratorOutput, KotlinModelsGeneratorContext } from './models';
4
+ type Input = KotlinModelsGeneratorInput;
5
+ type Output = KotlinModelsGeneratorOutput;
6
+ type Config = KotlinModelsGeneratorConfig;
7
+ type SchemaOutput = KotlinModelGeneratorOutput;
8
+ type Context = KotlinModelsGeneratorContext;
9
+ export declare class KotlinModelsGenerator extends OpenApiSchemasGenerationProviderBase<Input, Output, Config, SchemaOutput, Context> {
10
+ private readonly _modelGeneratorFactory;
11
+ constructor(modelGeneratorFactory?: Factory<KotlinModelGenerator, []>);
12
+ protected initResult(): Output;
13
+ protected buildContext(context: OpenApiGeneratorContext<KotlinModelsGeneratorInput>, config?: Partial<Config> | undefined): Context;
14
+ protected generateSchema(ctx: Context, schema: ApiSchema): SchemaOutput;
15
+ protected addSchemaResult(ctx: Context, schema: ApiSchema, result: SchemaOutput): void;
16
+ }
17
+ export {};