@axinom/mosaic-cli 0.14.2-rc.8 → 0.15.0-rc.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/package.json +6 -5
- package/src/cli/README.md +60 -0
- package/src/cli/index.ts +47 -0
- package/src/commands/apply-templates/apply-templates.spec.ts +623 -0
- package/src/commands/apply-templates/apply-templates.ts +494 -0
- package/src/commands/apply-templates/bitwarden-vault.ts +130 -0
- package/src/commands/apply-templates/index.ts +1 -0
- package/src/commands/create-extension-config/create-extension-config.ts +92 -0
- package/src/commands/create-extension-config/index.ts +23 -0
- package/src/commands/get-access-token/get-access-token-options.ts +9 -0
- package/src/commands/get-access-token/get-dev-access-token.ts +32 -0
- package/src/commands/get-access-token/index.ts +66 -0
- package/src/commands/graphql-diff.ts +143 -0
- package/src/commands/msg-codegen/codegen.ts +891 -0
- package/src/commands/msg-codegen/index.ts +48 -0
- package/src/commands/msg-codegen/lint.ts +84 -0
- package/src/commands/msg-codegen/message-codegen-options.ts +7 -0
- package/src/commands/msg-diff/asyncapi-override.ts +31 -0
- package/src/commands/msg-diff/git-checkout-tmp.ts +73 -0
- package/src/commands/msg-diff/index.ts +53 -0
- package/src/commands/msg-diff/message-diff-options.ts +7 -0
- package/src/commands/msg-diff/msg-diff.spec.ts +412 -0
- package/src/commands/msg-diff/msg-diff.ts +364 -0
- package/src/commands/msg-diff/test-resources/0/1-asyncapi.yml +38 -0
- package/src/commands/msg-diff/test-resources/0/2-asyncapi.yml +36 -0
- package/src/commands/msg-diff/test-resources/0/command.json +74 -0
- package/src/commands/msg-diff/test-resources/0/event.json +25 -0
- package/src/commands/msg-diff/test-resources/1/1-asyncapi.yml +25 -0
- package/src/commands/msg-diff/test-resources/1/moved-event.json +25 -0
- package/src/commands/msg-diff/test-resources/common.json +20 -0
- package/src/commands/pg-dump/README.md +21 -0
- package/src/commands/pg-dump/generate.ts +146 -0
- package/src/commands/pg-dump/index.ts +39 -0
- package/src/commands/pg-dump/pg-dump-options.ts +6 -0
- package/src/commands/publish-schema-to-db/README.md +130 -0
- package/src/commands/publish-schema-to-db/abstractions/base-smart-tags.ts +6 -0
- package/src/commands/publish-schema-to-db/abstractions/index.ts +5 -0
- package/src/commands/publish-schema-to-db/abstractions/pg-column.ts +31 -0
- package/src/commands/publish-schema-to-db/abstractions/pg-fk-column.ts +6 -0
- package/src/commands/publish-schema-to-db/abstractions/pg-table.ts +55 -0
- package/src/commands/publish-schema-to-db/abstractions/pg-type.ts +8 -0
- package/src/commands/publish-schema-to-db/content-entity-model.ts +93 -0
- package/src/commands/publish-schema-to-db/generate.ts +82 -0
- package/src/commands/publish-schema-to-db/index.ts +49 -0
- package/src/commands/publish-schema-to-db/jest.config.js +9 -0
- package/src/commands/publish-schema-to-db/pg-models/columns/fk-column.spec.ts +42 -0
- package/src/commands/publish-schema-to-db/pg-models/columns/fk-column.ts +41 -0
- package/src/commands/publish-schema-to-db/pg-models/columns/index.ts +4 -0
- package/src/commands/publish-schema-to-db/pg-models/columns/pk-column.spec.ts +47 -0
- package/src/commands/publish-schema-to-db/pg-models/columns/pk-column.ts +34 -0
- package/src/commands/publish-schema-to-db/pg-models/columns/primitive-column.spec.ts +65 -0
- package/src/commands/publish-schema-to-db/pg-models/columns/primitive-column.ts +62 -0
- package/src/commands/publish-schema-to-db/pg-models/columns/virtual-fk-column.spec.ts +24 -0
- package/src/commands/publish-schema-to-db/pg-models/columns/virtual-fk-column.ts +34 -0
- package/src/commands/publish-schema-to-db/pg-models/json-schema-parse-utils.spec.ts +182 -0
- package/src/commands/publish-schema-to-db/pg-models/json-schema-parse-utils.ts +166 -0
- package/src/commands/publish-schema-to-db/pg-models/pg-sql-gen-utils.spec.ts +19 -0
- package/src/commands/publish-schema-to-db/pg-models/pg-sql-gen-utils.ts +237 -0
- package/src/commands/publish-schema-to-db/pg-models/pgl-utils.spec.ts +19 -0
- package/src/commands/publish-schema-to-db/pg-models/pgl-utils.ts +115 -0
- package/src/commands/publish-schema-to-db/pg-models/tables/content-entity-table.ts +104 -0
- package/src/commands/publish-schema-to-db/pg-models/tables/index.ts +3 -0
- package/src/commands/publish-schema-to-db/pg-models/tables/object-property-table.ts +113 -0
- package/src/commands/publish-schema-to-db/pg-models/tables/relations-table.ts +115 -0
- package/src/commands/publish-schema-to-db/postprocessors/collection-postprocessor.ts +33 -0
- package/src/commands/publish-schema-to-db/postprocessors/content-entity-model-postprocessor.ts +13 -0
- package/src/commands/publish-schema-to-db/postprocessors/episode-postprocessor.ts +37 -0
- package/src/commands/publish-schema-to-db/postprocessors/index.ts +6 -0
- package/src/commands/publish-schema-to-db/postprocessors/movie-postprocessor.ts +30 -0
- package/src/commands/publish-schema-to-db/postprocessors/postprocessing-utils.ts +21 -0
- package/src/commands/publish-schema-to-db/postprocessors/season-postprocessor.ts +37 -0
- package/src/commands/publish-schema-to-db/postprocessors/tvshow-postprocessor.ts +30 -0
- package/src/commands/publish-schema-to-db/publish-schema-to-db-options.ts +15 -0
- package/src/commands/publish-schema-to-db/types/sql-formatter.d.ts +10 -0
- package/src/exports.ts +2 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,891 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CommonNamingConvention,
|
|
5
|
+
FormatHelpers,
|
|
6
|
+
OutputModel,
|
|
7
|
+
PropertyType,
|
|
8
|
+
TypeScriptGenerator,
|
|
9
|
+
} from '@asyncapi/modelina';
|
|
10
|
+
import { AsyncAPIDocument, Message, parse } from '@asyncapi/parser';
|
|
11
|
+
import endent from 'endent';
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import { MessageCodegenOptions } from './message-codegen-options';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Processes AsyncAPI document and generates Typescript classes from it.
|
|
18
|
+
* Generated output:
|
|
19
|
+
* - message payloads will be transformed into Typescript types and bundled JSON schemas
|
|
20
|
+
* - channels information will be used to generate MessagingSettings, that can be used to configure RabbitMQ connection.
|
|
21
|
+
* @param inputDir - Path to input directory - AsyncAPI document root.
|
|
22
|
+
* @param filePattern - Regular expression that matches suitable input files.
|
|
23
|
+
* @param outputDir - Path to output directory - output root.
|
|
24
|
+
*/
|
|
25
|
+
export class Codegen {
|
|
26
|
+
private readonly schemaRoot: string;
|
|
27
|
+
private readonly filePattern: RegExp;
|
|
28
|
+
private readonly outputRoot: string;
|
|
29
|
+
private readonly typesOutputRoot: string;
|
|
30
|
+
private readonly schemasOutputRoot: string;
|
|
31
|
+
private readonly messagingSettingsOutputRoot: string;
|
|
32
|
+
|
|
33
|
+
constructor(options: MessageCodegenOptions) {
|
|
34
|
+
this.schemaRoot = path.resolve(options.inputDir);
|
|
35
|
+
try {
|
|
36
|
+
this.filePattern = new RegExp(options.filePattern);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(
|
|
39
|
+
`${options.filePattern} is not a valid regular expression.`,
|
|
40
|
+
);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.outputRoot = path.resolve(options.outputDir);
|
|
45
|
+
this.typesOutputRoot = path.join(this.outputRoot, 'types');
|
|
46
|
+
this.schemasOutputRoot = path.join(this.outputRoot, 'schemas');
|
|
47
|
+
this.messagingSettingsOutputRoot = path.join(this.outputRoot, 'config');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public async run(): Promise<void> {
|
|
51
|
+
console.log('Running message codegen.');
|
|
52
|
+
console.log(`* schema root: ${this.schemaRoot}`);
|
|
53
|
+
console.log(`* output root: ${this.outputRoot}`);
|
|
54
|
+
|
|
55
|
+
await this.walk(this.schemaRoot);
|
|
56
|
+
await this.barrelExportTs(
|
|
57
|
+
[],
|
|
58
|
+
[
|
|
59
|
+
this.typesOutputRoot,
|
|
60
|
+
this.schemasOutputRoot,
|
|
61
|
+
this.messagingSettingsOutputRoot,
|
|
62
|
+
],
|
|
63
|
+
path.join(this.outputRoot, 'index.ts'),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Recursively walks a schemas directory and generates TS + bundled JSON schema files.
|
|
69
|
+
* @param dir - Directory to walk.
|
|
70
|
+
*/
|
|
71
|
+
async walk(dir: string): Promise<void> {
|
|
72
|
+
const items = await fs.promises.readdir(dir);
|
|
73
|
+
|
|
74
|
+
const fullPathItems = items.map((i) => path.join(dir, i));
|
|
75
|
+
const dirs = fullPathItems.filter((i) => fs.statSync(i).isDirectory());
|
|
76
|
+
const files = fullPathItems.filter(
|
|
77
|
+
(i) => fs.statSync(i).isFile() && this.filePattern.test(i),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if ([...files, ...dirs].length === 0) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const asyncApiFile of files) {
|
|
85
|
+
console.log(`Processing ${asyncApiFile}.`);
|
|
86
|
+
await this.processAsyncAPIDocument(asyncApiFile);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const directory of dirs) {
|
|
90
|
+
await this.walk(directory);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (files.length === 0) {
|
|
94
|
+
//Calculate correct TS output related directories for barrel exports.
|
|
95
|
+
const tsDirs = dirs.map((d) =>
|
|
96
|
+
path.join(this.typesOutputRoot, path.relative(this.schemaRoot, d)),
|
|
97
|
+
);
|
|
98
|
+
const tsIndexOutPath = path.join(
|
|
99
|
+
this.typesOutputRoot,
|
|
100
|
+
getRelativeDir(this.schemaRoot, dir),
|
|
101
|
+
'index.ts',
|
|
102
|
+
);
|
|
103
|
+
await this.barrelExportTs([], tsDirs, tsIndexOutPath);
|
|
104
|
+
|
|
105
|
+
// Calculate correct JSON schema output related directories for barrel exports.
|
|
106
|
+
const schemaDirs = dirs.map((d) =>
|
|
107
|
+
path.join(this.schemasOutputRoot, path.relative(this.schemaRoot, d)),
|
|
108
|
+
);
|
|
109
|
+
const schemaIndexOutPath = path.join(
|
|
110
|
+
this.schemasOutputRoot,
|
|
111
|
+
getRelativeDir(this.schemaRoot, dir),
|
|
112
|
+
'index.ts',
|
|
113
|
+
);
|
|
114
|
+
await this.barrelExportSchema([], schemaDirs, schemaIndexOutPath);
|
|
115
|
+
|
|
116
|
+
// Calculate correct Message Settings output related directories for barrel exports.
|
|
117
|
+
const settingsDirs = dirs.map((d) =>
|
|
118
|
+
path.join(
|
|
119
|
+
this.messagingSettingsOutputRoot,
|
|
120
|
+
path.relative(this.schemaRoot, d),
|
|
121
|
+
),
|
|
122
|
+
);
|
|
123
|
+
const messagingIndexOutPath = path.join(
|
|
124
|
+
this.messagingSettingsOutputRoot,
|
|
125
|
+
getRelativeDir(this.schemaRoot, dir),
|
|
126
|
+
'index.ts',
|
|
127
|
+
);
|
|
128
|
+
await this.barrelExportSettings([], settingsDirs, messagingIndexOutPath);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Builds a path for the generated TS file from an input JSON schema path, retains directory structure.
|
|
134
|
+
* @param outputPath - output directory.
|
|
135
|
+
* @param typeName - Name of the type.
|
|
136
|
+
*/
|
|
137
|
+
buildTsOutPath(outputPath: string, typeName: string): string {
|
|
138
|
+
return path.join(outputPath, `${FormatHelpers.toParamCase(typeName)}.ts`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Builds a path for the bundled JSON schema file from an input JSON schema path, retains directory structure.
|
|
143
|
+
* @param outputPath - output directory.
|
|
144
|
+
* @param typeName - Name of the type.
|
|
145
|
+
*/
|
|
146
|
+
buildSchemaOutPath(outputPath: string, typeName: string): string {
|
|
147
|
+
return path.join(outputPath, `${FormatHelpers.toParamCase(typeName)}.json`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Generates TS interfaces and JSON schemas from AsyncAPI schema.
|
|
152
|
+
* @param asyncApiFile - path to AsyncAPI document
|
|
153
|
+
*/
|
|
154
|
+
async processAsyncAPIDocument(asyncApiFile: string): Promise<void> {
|
|
155
|
+
const schemaContent = fs.readFileSync(asyncApiFile, 'utf8');
|
|
156
|
+
|
|
157
|
+
const asyncApiDocument = await parse(schemaContent, { path: asyncApiFile });
|
|
158
|
+
|
|
159
|
+
//export Typescript models
|
|
160
|
+
const tsModelsOutputPath = path.join(
|
|
161
|
+
this.typesOutputRoot,
|
|
162
|
+
getRelativeDir(this.schemaRoot, asyncApiFile),
|
|
163
|
+
);
|
|
164
|
+
await this.exportTsModels(asyncApiDocument, tsModelsOutputPath);
|
|
165
|
+
|
|
166
|
+
//export JSON Schemas
|
|
167
|
+
const schemasOutputPath = path.join(
|
|
168
|
+
this.schemasOutputRoot,
|
|
169
|
+
getRelativeDir(this.schemaRoot, asyncApiFile),
|
|
170
|
+
);
|
|
171
|
+
await this.exportSchemas(asyncApiDocument, schemasOutputPath);
|
|
172
|
+
|
|
173
|
+
//export message settings
|
|
174
|
+
const messagingSettingsOutputPath = path.join(
|
|
175
|
+
this.messagingSettingsOutputRoot,
|
|
176
|
+
getRelativeDir(this.schemaRoot, asyncApiFile),
|
|
177
|
+
);
|
|
178
|
+
await this.exportSettings(asyncApiDocument, messagingSettingsOutputPath);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Export all TS models from AsyncAPI document
|
|
183
|
+
* @param asyncAPIDocument - AsyncAPI Document object
|
|
184
|
+
* @param outputPath - output path for generated files
|
|
185
|
+
*/
|
|
186
|
+
async exportTsModels(
|
|
187
|
+
asyncAPIDocument: AsyncAPIDocument,
|
|
188
|
+
outputPath: string,
|
|
189
|
+
): Promise<void> {
|
|
190
|
+
const generator = new TypeScriptGenerator({
|
|
191
|
+
modelType: 'interface',
|
|
192
|
+
namingConvention: CustomNamingConvention,
|
|
193
|
+
enumType: 'union',
|
|
194
|
+
presets: [
|
|
195
|
+
ADDITIONAL_PROPERTIES_PRESET,
|
|
196
|
+
NULLABLE_PROPERTY_TO_UNION_PRESET,
|
|
197
|
+
EXPORT_UNION_TYPE_PRESET,
|
|
198
|
+
EXPORT_INTERFACE_PRESET,
|
|
199
|
+
DESCRIPTION_PRESET,
|
|
200
|
+
IMPORTS_PRESET,
|
|
201
|
+
],
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
let outputModels = await await generator.generate(asyncAPIDocument as any);
|
|
205
|
+
for (const key in asyncAPIDocument.components().schemas()) {
|
|
206
|
+
const referencedModels = await generator.generate(
|
|
207
|
+
asyncAPIDocument.components().schemas()[key].json(),
|
|
208
|
+
);
|
|
209
|
+
outputModels = outputModels.map(
|
|
210
|
+
(om) =>
|
|
211
|
+
referencedModels.find((rm) => om.modelName === rm.modelName) || om,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const groupedModels: { [key: string]: OutputModel[] } = {};
|
|
216
|
+
outputModels.map((outModel) => {
|
|
217
|
+
const modelPathPrefix = getModelPathPrefix(outModel.modelName);
|
|
218
|
+
if (groupedModels[modelPathPrefix] === undefined) {
|
|
219
|
+
groupedModels[modelPathPrefix] = [];
|
|
220
|
+
}
|
|
221
|
+
groupedModels[modelPathPrefix].push(outModel);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const tsDirs: string[] = [];
|
|
225
|
+
for (const pathPrefix in groupedModels) {
|
|
226
|
+
const models = groupedModels[pathPrefix];
|
|
227
|
+
const groupOutPutPath = path.join(outputPath, pathPrefix);
|
|
228
|
+
if (pathPrefix) {
|
|
229
|
+
tsDirs.push(groupOutPutPath);
|
|
230
|
+
}
|
|
231
|
+
await fs.promises.mkdir(groupOutPutPath, {
|
|
232
|
+
recursive: true,
|
|
233
|
+
});
|
|
234
|
+
const tsFiles: string[] = [];
|
|
235
|
+
for (const outputModel of models) {
|
|
236
|
+
const outputFilePath = this.buildTsOutPath(
|
|
237
|
+
groupOutPutPath,
|
|
238
|
+
outputModel.modelName,
|
|
239
|
+
);
|
|
240
|
+
await fs.promises.writeFile(outputFilePath, outputModel.result);
|
|
241
|
+
tsFiles.push(outputFilePath);
|
|
242
|
+
}
|
|
243
|
+
//generate barrel export for all model in AsyncAPI document
|
|
244
|
+
const tsIndexOutPath = path.join(groupOutPutPath, 'index.ts');
|
|
245
|
+
await this.barrelExportTs(tsFiles, [], tsIndexOutPath);
|
|
246
|
+
}
|
|
247
|
+
if (tsDirs.length > 0) {
|
|
248
|
+
const tsIndexOutPath = path.join(outputPath, 'index.ts');
|
|
249
|
+
await this.barrelExportTs([], tsDirs, tsIndexOutPath);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Export all JSON Schemas from AsyncAPI document
|
|
255
|
+
* @param asyncAPIDocument - AsyncAPI Document object
|
|
256
|
+
* @param outputPath - output path for generated files
|
|
257
|
+
*/
|
|
258
|
+
async exportSchemas(
|
|
259
|
+
asyncAPIDocument: AsyncAPIDocument,
|
|
260
|
+
outputPath: string,
|
|
261
|
+
): Promise<void> {
|
|
262
|
+
const groupedSchemas: { [key: string]: Message[] } = {};
|
|
263
|
+
|
|
264
|
+
for (const [, message] of asyncAPIDocument.allMessages()) {
|
|
265
|
+
const schemaPathPrefix = getModelPathPrefix(getMessageTitle(message));
|
|
266
|
+
if (groupedSchemas[schemaPathPrefix] === undefined) {
|
|
267
|
+
groupedSchemas[schemaPathPrefix] = [];
|
|
268
|
+
}
|
|
269
|
+
groupedSchemas[schemaPathPrefix].push(message);
|
|
270
|
+
}
|
|
271
|
+
const schemaDirs: string[] = [];
|
|
272
|
+
for (const pathPrefix in groupedSchemas) {
|
|
273
|
+
const messages = groupedSchemas[pathPrefix];
|
|
274
|
+
const schemaFiles: string[] = [];
|
|
275
|
+
const groupOutPutPath = path.join(outputPath, pathPrefix);
|
|
276
|
+
if (pathPrefix) {
|
|
277
|
+
schemaDirs.push(groupOutPutPath);
|
|
278
|
+
}
|
|
279
|
+
await fs.promises.mkdir(groupOutPutPath, {
|
|
280
|
+
recursive: true,
|
|
281
|
+
});
|
|
282
|
+
for (const msg of messages) {
|
|
283
|
+
const outputSchemaPath = this.buildSchemaOutPath(
|
|
284
|
+
groupOutPutPath,
|
|
285
|
+
getMessageTitle(msg),
|
|
286
|
+
);
|
|
287
|
+
await this.bundleSchema(msg.originalPayload(), outputSchemaPath);
|
|
288
|
+
schemaFiles.push(outputSchemaPath);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
//generate barrel export for all model in AsyncAPI document
|
|
292
|
+
const schemasIndexOutPath = path.join(groupOutPutPath, 'index.ts');
|
|
293
|
+
await this.barrelExportSchema(schemaFiles, [], schemasIndexOutPath);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const schemaIndexOutPath = path.join(outputPath, 'index.ts');
|
|
297
|
+
await this.barrelExportSchema([], schemaDirs, schemaIndexOutPath);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Export all AsyncAPI document channels to Messaging Settings
|
|
302
|
+
* @param asyncAPIDocument - AsyncAPI Document object
|
|
303
|
+
* @param outputPath - output path for generated files
|
|
304
|
+
*/
|
|
305
|
+
async exportSettings(
|
|
306
|
+
asyncAPIDocument: AsyncAPIDocument,
|
|
307
|
+
outputPath: string,
|
|
308
|
+
): Promise<void> {
|
|
309
|
+
const serviceTitle = getServiceTitle(asyncAPIDocument);
|
|
310
|
+
const serviceId = getServiceId(asyncAPIDocument);
|
|
311
|
+
const channelsData: ChannelData[] = [];
|
|
312
|
+
|
|
313
|
+
for (const routingKey of asyncAPIDocument.channelNames()) {
|
|
314
|
+
const channel = asyncAPIDocument.channel(routingKey);
|
|
315
|
+
if (channel.hasBinding('amqp')) {
|
|
316
|
+
const queueName = channel.binding('amqp')['queue']['name'];
|
|
317
|
+
|
|
318
|
+
const payloadName = channel.hasSubscribe()
|
|
319
|
+
? channel.subscribe().message().payload().title()
|
|
320
|
+
: channel.hasPublish()
|
|
321
|
+
? channel.publish().message().payload().title()
|
|
322
|
+
: 'undefined';
|
|
323
|
+
|
|
324
|
+
channelsData.push({
|
|
325
|
+
routingKey: routingKey,
|
|
326
|
+
queueName: queueName,
|
|
327
|
+
payloadName: payloadName,
|
|
328
|
+
acceptedAction: channel.hasSubscribe() ? 'subscribe' : `publish`,
|
|
329
|
+
isMultiTenant: routingKey.includes('*.*'),
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
await this.generateMessagingSettings(
|
|
334
|
+
serviceId,
|
|
335
|
+
serviceTitle,
|
|
336
|
+
channelsData,
|
|
337
|
+
outputPath,
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Generate Messaging Settings based on Channels data.
|
|
343
|
+
* @param serviceId - service identifier
|
|
344
|
+
* @param serviceTitle - service title
|
|
345
|
+
* @param channelsData - AsyncAPI channels information
|
|
346
|
+
* @param outputPath - output path for generated files
|
|
347
|
+
*/
|
|
348
|
+
async generateMessagingSettings(
|
|
349
|
+
serviceId: string,
|
|
350
|
+
serviceTitle: string,
|
|
351
|
+
channelsData: ChannelData[],
|
|
352
|
+
outputPath: string,
|
|
353
|
+
): Promise<void> {
|
|
354
|
+
const messagingSettingFiles: string[] = [];
|
|
355
|
+
|
|
356
|
+
messagingSettingFiles.push(
|
|
357
|
+
await this.createMessagingSettingFile(
|
|
358
|
+
true,
|
|
359
|
+
serviceId,
|
|
360
|
+
serviceTitle,
|
|
361
|
+
channelsData.filter((data) => data.isMultiTenant === true),
|
|
362
|
+
outputPath,
|
|
363
|
+
),
|
|
364
|
+
);
|
|
365
|
+
messagingSettingFiles.push(
|
|
366
|
+
await this.createMessagingSettingFile(
|
|
367
|
+
false,
|
|
368
|
+
serviceId,
|
|
369
|
+
serviceTitle,
|
|
370
|
+
channelsData.filter((data) => data.isMultiTenant === false),
|
|
371
|
+
outputPath,
|
|
372
|
+
),
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
//barrel export for messaging settings
|
|
376
|
+
const schemaIndexOutPath = path.join(outputPath, 'index.ts');
|
|
377
|
+
await this.barrelExportSettings(
|
|
378
|
+
messagingSettingFiles.filter((f) => f),
|
|
379
|
+
[],
|
|
380
|
+
schemaIndexOutPath,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Create new Messaging Settings file.
|
|
386
|
+
* @param isMultiTenant - is file with multi tenant settings, or not
|
|
387
|
+
* @param serviceId - service Identifier
|
|
388
|
+
* @param serviceTitle - service Title
|
|
389
|
+
* @param channelsData - AsyncAPI channels information
|
|
390
|
+
* @param outputPath - output path for generated files
|
|
391
|
+
*/
|
|
392
|
+
async createMessagingSettingFile(
|
|
393
|
+
isMultiTenant: boolean,
|
|
394
|
+
serviceId: string,
|
|
395
|
+
serviceTitle: string,
|
|
396
|
+
channelsData: ChannelData[],
|
|
397
|
+
outputPath: string,
|
|
398
|
+
): Promise<string> {
|
|
399
|
+
if (channelsData.length === 0) {
|
|
400
|
+
return ``;
|
|
401
|
+
}
|
|
402
|
+
const baseClass = isMultiTenant
|
|
403
|
+
? `MultiTenantMessagingSettings`
|
|
404
|
+
: `MessagingSettings`;
|
|
405
|
+
const className = `${FormatHelpers.toPascalCase(serviceTitle)}${baseClass}`;
|
|
406
|
+
const action = isMultiTenant ? `extends` : `implements`;
|
|
407
|
+
const constructor = endent`private constructor(
|
|
408
|
+
public readonly messageType: string,
|
|
409
|
+
public readonly queue: string,
|
|
410
|
+
public readonly routingKey: string,
|
|
411
|
+
) {
|
|
412
|
+
${
|
|
413
|
+
isMultiTenant
|
|
414
|
+
? `super('${serviceId}', messageType, queue, routingKey);`
|
|
415
|
+
: ``
|
|
416
|
+
}
|
|
417
|
+
}`;
|
|
418
|
+
|
|
419
|
+
const properties = channelsData.map((data) =>
|
|
420
|
+
this.createMessagingSettingProperty(data, className),
|
|
421
|
+
);
|
|
422
|
+
const content = endent`import { ${baseClass} } from '@axinom/mosaic-message-bus-abstractions';
|
|
423
|
+
|
|
424
|
+
export class ${className} ${action} ${baseClass} {
|
|
425
|
+
${properties.join(`\n`)}
|
|
426
|
+
${isMultiTenant ? `` : `\npublic readonly serviceId = '${serviceId}';\n`}
|
|
427
|
+
${constructor}
|
|
428
|
+
|
|
429
|
+
public toString = (): string => {
|
|
430
|
+
return this.messageType;
|
|
431
|
+
};
|
|
432
|
+
}`;
|
|
433
|
+
|
|
434
|
+
const filePath = path.join(
|
|
435
|
+
outputPath,
|
|
436
|
+
`${FormatHelpers.toParamCase(className)}.ts`,
|
|
437
|
+
);
|
|
438
|
+
await fs.promises.mkdir(path.dirname(filePath), {
|
|
439
|
+
recursive: true,
|
|
440
|
+
});
|
|
441
|
+
await fs.promises.writeFile(filePath, content);
|
|
442
|
+
|
|
443
|
+
return filePath;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Create Messaging Settings property from Channel Data.
|
|
447
|
+
* @param channelData - AsyncAPI channel information
|
|
448
|
+
* @param settingsClassName - class name of messaging setting
|
|
449
|
+
*/
|
|
450
|
+
createMessagingSettingProperty(
|
|
451
|
+
channelData: ChannelData,
|
|
452
|
+
settingsClassName: string,
|
|
453
|
+
): string {
|
|
454
|
+
const messageType = FormatHelpers.toPascalCase(
|
|
455
|
+
channelData.payloadName.replace('command', '').replace('event', ''),
|
|
456
|
+
);
|
|
457
|
+
return endent`
|
|
458
|
+
public static ${messageType} = new ${settingsClassName}(
|
|
459
|
+
'${messageType}',
|
|
460
|
+
'${channelData.queueName}',
|
|
461
|
+
'${channelData.routingKey}'
|
|
462
|
+
);`;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Bundles a JSON schema into a self-contained file by including all external refs.
|
|
467
|
+
* @param jsonSchema - Schema object.
|
|
468
|
+
* @param outPath - Output JSON schema path.
|
|
469
|
+
*/
|
|
470
|
+
async bundleSchema(jsonSchema: unknown, outPath: string): Promise<void> {
|
|
471
|
+
await fs.promises.mkdir(path.dirname(outPath), { recursive: true });
|
|
472
|
+
await fs.promises.writeFile(outPath, JSON.stringify(jsonSchema, null, 2));
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// TODO: Consider doing named exports based on message groups to not just put all messages in one namespace.
|
|
476
|
+
// TODO: Maybe some templating engine would be better than just building strings.
|
|
477
|
+
/**
|
|
478
|
+
* Generates a barrel index.ts for all modules inside `outPath`.
|
|
479
|
+
* In addition it generates a schemas enum for the included modules to make it easier to map them to bundled JSON schemas.
|
|
480
|
+
* @param files - Files to roll up.
|
|
481
|
+
* @param dirs - Directories to roll up.
|
|
482
|
+
* @param outPath - Path where to write `index.ts`.
|
|
483
|
+
*/
|
|
484
|
+
async barrelExportTs(
|
|
485
|
+
files: string[],
|
|
486
|
+
dirs: string[],
|
|
487
|
+
outPath: string,
|
|
488
|
+
): Promise<void> {
|
|
489
|
+
console.log(`Rolling up TS exports to ${outPath}.`);
|
|
490
|
+
const items = [
|
|
491
|
+
...files.map((f) => path.basename(f, '.ts')),
|
|
492
|
+
...dirs.filter((d) => fs.existsSync(d)).map((d) => path.basename(d)),
|
|
493
|
+
];
|
|
494
|
+
|
|
495
|
+
if (items.length < 1) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const exports = `${items
|
|
500
|
+
.sort()
|
|
501
|
+
.map((p) => `export * from './${p}';`)
|
|
502
|
+
.join('\n')}`;
|
|
503
|
+
let schemaEnum = '';
|
|
504
|
+
let typeNamesEnum = '';
|
|
505
|
+
|
|
506
|
+
// TODO: Break this up into smaller pieces.
|
|
507
|
+
// TODO: Consider adding docstring to generated enums.
|
|
508
|
+
if (files.length > 0) {
|
|
509
|
+
const sortedFiles = files.sort();
|
|
510
|
+
// TODO: Message envelope is handled separately, we could remove this entirely.
|
|
511
|
+
// If message-envelope requires some special handling.
|
|
512
|
+
if (files.length === 1 && files[0].endsWith('message-envelope.ts')) {
|
|
513
|
+
const file = files[0];
|
|
514
|
+
schemaEnum = endent`
|
|
515
|
+
export enum MessageEnvelopeSchema {
|
|
516
|
+
MessageEnvelope = '${toPosixPath(this.tsPathToSchemaPath(file))}'
|
|
517
|
+
}
|
|
518
|
+
`;
|
|
519
|
+
} else {
|
|
520
|
+
let enumNameBase = FormatHelpers.toPascalCase(
|
|
521
|
+
getRelativeDir(this.typesOutputRoot, path.dirname(outPath)),
|
|
522
|
+
);
|
|
523
|
+
if (!enumNameBase.includes('Types')) {
|
|
524
|
+
enumNameBase = enumNameBase.replace('Payloads', ''); // Remove the Payloads prefix to reduce noise
|
|
525
|
+
schemaEnum = endent`
|
|
526
|
+
export enum ${enumNameBase}Schemas {
|
|
527
|
+
${sortedFiles
|
|
528
|
+
.map(
|
|
529
|
+
(f) =>
|
|
530
|
+
`${FormatHelpers.toPascalCase(
|
|
531
|
+
path.basename(f, '.ts'),
|
|
532
|
+
)} = '${toPosixPath(this.tsPathToSchemaPath(f))}'`,
|
|
533
|
+
)
|
|
534
|
+
.join(',\n')}
|
|
535
|
+
}`;
|
|
536
|
+
typeNamesEnum = endent`
|
|
537
|
+
export enum ${enumNameBase}Types {
|
|
538
|
+
${sortedFiles
|
|
539
|
+
.sort()
|
|
540
|
+
.map(
|
|
541
|
+
(f) =>
|
|
542
|
+
`${FormatHelpers.toPascalCase(
|
|
543
|
+
path.basename(f, '.ts'),
|
|
544
|
+
)} = '${FormatHelpers.toPascalCase(path.basename(f, '.ts'))}'`,
|
|
545
|
+
)
|
|
546
|
+
.join(',\n')}
|
|
547
|
+
}`;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const contents = endent`
|
|
553
|
+
${exports}
|
|
554
|
+
|
|
555
|
+
${schemaEnum}
|
|
556
|
+
|
|
557
|
+
${typeNamesEnum}
|
|
558
|
+
`;
|
|
559
|
+
|
|
560
|
+
await fs.promises.writeFile(outPath, contents);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Generates a barrel index.ts for all Messaging Settings inside `outPath`.
|
|
565
|
+
* @param files - Files to roll up.
|
|
566
|
+
* @param dirs - Directories to roll up.
|
|
567
|
+
* @param outPath - Path where to write `index.ts`.
|
|
568
|
+
*/
|
|
569
|
+
async barrelExportSettings(
|
|
570
|
+
files: string[],
|
|
571
|
+
dirs: string[],
|
|
572
|
+
outPath: string,
|
|
573
|
+
): Promise<void> {
|
|
574
|
+
console.log(`Rolling up Messaging Settings exports to ${outPath}.`);
|
|
575
|
+
const items = [
|
|
576
|
+
...files.map((f) => path.basename(f, '.ts')),
|
|
577
|
+
...dirs.filter((d) => fs.existsSync(d)).map((d) => path.basename(d)),
|
|
578
|
+
];
|
|
579
|
+
|
|
580
|
+
const dirExports = dirs
|
|
581
|
+
.sort()
|
|
582
|
+
.filter((d) => fs.existsSync(d))
|
|
583
|
+
.map((d) => path.basename(d));
|
|
584
|
+
|
|
585
|
+
if (items.length < 1) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
let contents = '';
|
|
590
|
+
|
|
591
|
+
if (files.length > 0) {
|
|
592
|
+
const baseNames = files.map((f) => path.basename(f, '.ts')).sort();
|
|
593
|
+
const messagingImports = baseNames.map((n) => `export * from './${n}';`);
|
|
594
|
+
contents = endent`
|
|
595
|
+
${messagingImports.join('\n')}
|
|
596
|
+
`;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
contents += endent`\n
|
|
600
|
+
${dirExports.map((d) => `export * from './${d}';`).join('\n')}`;
|
|
601
|
+
|
|
602
|
+
await fs.promises.writeFile(outPath, contents);
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Generates a barrel index.ts for all JSON schemas inside `outPath`.
|
|
606
|
+
* @param files - Files to roll up.
|
|
607
|
+
* @param dirs - Directories to roll up.
|
|
608
|
+
* @param outPath - Path where to write `index.ts`.
|
|
609
|
+
*/
|
|
610
|
+
async barrelExportSchema(
|
|
611
|
+
files: string[],
|
|
612
|
+
dirs: string[],
|
|
613
|
+
outPath: string,
|
|
614
|
+
): Promise<void> {
|
|
615
|
+
console.log(`Rolling up JSON exports to ${outPath}.`);
|
|
616
|
+
const items = [
|
|
617
|
+
...files.map((f) => path.basename(f, '.json')),
|
|
618
|
+
...dirs.filter((d) => fs.existsSync(d)).map((d) => path.basename(d)),
|
|
619
|
+
];
|
|
620
|
+
|
|
621
|
+
const dirExports = dirs
|
|
622
|
+
.sort()
|
|
623
|
+
.filter((d) => fs.existsSync(d))
|
|
624
|
+
.map((d) => path.basename(d));
|
|
625
|
+
|
|
626
|
+
if (items.length < 1) {
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
let contents = '';
|
|
631
|
+
|
|
632
|
+
if (files.length > 0) {
|
|
633
|
+
const baseNames = files.map((f) => path.basename(f, '.json')).sort();
|
|
634
|
+
const schemaImports = baseNames.map(
|
|
635
|
+
(n) =>
|
|
636
|
+
`import * as ${FormatHelpers.toPascalCase(n)} from './${n}.json';`,
|
|
637
|
+
);
|
|
638
|
+
const schemaExports = baseNames.map(
|
|
639
|
+
(n) =>
|
|
640
|
+
`export const ${FormatHelpers.toPascalCase(
|
|
641
|
+
n,
|
|
642
|
+
)}Schema = ${FormatHelpers.toPascalCase(n)};`,
|
|
643
|
+
);
|
|
644
|
+
contents = endent`
|
|
645
|
+
${schemaImports.join('\n')}
|
|
646
|
+
|
|
647
|
+
${schemaExports.join('\n')}
|
|
648
|
+
`;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
contents += endent`\n
|
|
652
|
+
${dirExports.map((d) => `export * from './${d}';`).join('\n')}`;
|
|
653
|
+
|
|
654
|
+
await fs.promises.writeFile(outPath, contents);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Converts a generated TS path to a corresponding JSON schema path.
|
|
659
|
+
* @param tsFile - Path to a generated TS file.
|
|
660
|
+
*/
|
|
661
|
+
tsPathToSchemaPath(tsFile: string): string {
|
|
662
|
+
return path.join(
|
|
663
|
+
getRelativeDir(this.typesOutputRoot, tsFile),
|
|
664
|
+
`${path.basename(tsFile, '.ts')}.json`,
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Metadata of channels for settings generation.
|
|
671
|
+
*/
|
|
672
|
+
interface ChannelData {
|
|
673
|
+
/** Channel routing key */
|
|
674
|
+
routingKey: string;
|
|
675
|
+
|
|
676
|
+
/** Channel queue */
|
|
677
|
+
queueName: string;
|
|
678
|
+
|
|
679
|
+
/** Channel model */
|
|
680
|
+
payloadName: string;
|
|
681
|
+
|
|
682
|
+
/** Accepted Action*/
|
|
683
|
+
acceptedAction: 'subscribe' | 'publish';
|
|
684
|
+
|
|
685
|
+
/** Channel multi tenancy */
|
|
686
|
+
isMultiTenant: boolean;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function getRelativeDir(from: string, to: string): string {
|
|
690
|
+
return fs.statSync(to).isFile()
|
|
691
|
+
? path.dirname(path.relative(from, to))
|
|
692
|
+
: path.relative(from, to);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function getMessageTitle(message: Message): string {
|
|
696
|
+
return (
|
|
697
|
+
message.payload().title() ??
|
|
698
|
+
message.extension('x-parser-message-name') ??
|
|
699
|
+
message.payload().extension('x-parser-schema-id')
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Retrieve Service Title from AsyncAPI document.
|
|
705
|
+
* As Title returned value set in info.title property of document.
|
|
706
|
+
* @param asyncAPIDocument - AsyncAPI document.
|
|
707
|
+
*/
|
|
708
|
+
function getServiceTitle(asyncAPIDocument: AsyncAPIDocument): string {
|
|
709
|
+
return asyncAPIDocument.info().title();
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Retrieve Service Id from AsyncAPI document.
|
|
714
|
+
* As Id returned value set in extension property `x-service-id`.
|
|
715
|
+
* If extension property is not set - returned Service Title in lower-kebab-case.
|
|
716
|
+
* @param asyncAPIDocument - AsyncAPI document.
|
|
717
|
+
*/
|
|
718
|
+
function getServiceId(asyncAPIDocument: AsyncAPIDocument): string {
|
|
719
|
+
return (
|
|
720
|
+
(asyncAPIDocument.extension('x-service-id') as string) ??
|
|
721
|
+
FormatHelpers.toParamCase(getServiceTitle(asyncAPIDocument))
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Converts a path into a POSIX path by replacing current path separator with `/`.
|
|
727
|
+
* @param p - Path to convert.
|
|
728
|
+
*/
|
|
729
|
+
function toPosixPath(p: string): string {
|
|
730
|
+
return path.posix.join(...p.split(path.sep));
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Generating the path prefix for model.
|
|
735
|
+
* model name ends with "command" - prefix "commands"
|
|
736
|
+
* model name ends with "event" - prefix "events"
|
|
737
|
+
* default prefix "types"
|
|
738
|
+
* @param modelName - name of the model, defined in json schema, or AsyncAPI document
|
|
739
|
+
*/
|
|
740
|
+
function getModelPathPrefix(modelName: string): string {
|
|
741
|
+
return modelName
|
|
742
|
+
? modelName.toLowerCase().endsWith('command')
|
|
743
|
+
? 'commands'
|
|
744
|
+
: modelName.toLowerCase().endsWith('event')
|
|
745
|
+
? 'events'
|
|
746
|
+
: 'types'
|
|
747
|
+
: 'types';
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Adding 'export' for interfaces
|
|
752
|
+
*/
|
|
753
|
+
const EXPORT_INTERFACE_PRESET = {
|
|
754
|
+
interface: {
|
|
755
|
+
self({ content }) {
|
|
756
|
+
return `export ${content}`;
|
|
757
|
+
},
|
|
758
|
+
property({ content }) {
|
|
759
|
+
return content;
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Adding 'export' for union enumeration types
|
|
766
|
+
*/
|
|
767
|
+
const EXPORT_UNION_TYPE_PRESET = {
|
|
768
|
+
type: {
|
|
769
|
+
async self({ renderer }) {
|
|
770
|
+
return `export ${await renderer.defaultSelf()}`;
|
|
771
|
+
},
|
|
772
|
+
},
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Preset for TypeScriptGenerator, adds descriptions to types and properties.
|
|
777
|
+
*/
|
|
778
|
+
const DESCRIPTION_PRESET = {
|
|
779
|
+
interface: {
|
|
780
|
+
self({ renderer, content, model }) {
|
|
781
|
+
const desc = model.getFromOriginalInput('description');
|
|
782
|
+
if (desc) {
|
|
783
|
+
const renderedDesc = renderer.renderComments(desc);
|
|
784
|
+
return `${renderedDesc}\n${content}`;
|
|
785
|
+
}
|
|
786
|
+
return content;
|
|
787
|
+
},
|
|
788
|
+
property({ renderer, model, content, propertyName }) {
|
|
789
|
+
if (model.getFromOriginalInput('properties') !== undefined) {
|
|
790
|
+
const property = model.getFromOriginalInput('properties')[propertyName];
|
|
791
|
+
if (property !== undefined) {
|
|
792
|
+
const desc = property['description'];
|
|
793
|
+
if (desc) {
|
|
794
|
+
const renderedDesc = renderer.renderComments(desc);
|
|
795
|
+
return `${renderedDesc}\n${content}`;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
return content;
|
|
800
|
+
},
|
|
801
|
+
},
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Preset for TypeScriptGenerator, additionalProperties added to models as "[k: string]: unknown;"
|
|
806
|
+
*/
|
|
807
|
+
const ADDITIONAL_PROPERTIES_PRESET = {
|
|
808
|
+
interface: {
|
|
809
|
+
self({ content }) {
|
|
810
|
+
return content;
|
|
811
|
+
},
|
|
812
|
+
property({ renderer, propertyName, property, type }) {
|
|
813
|
+
if (type === PropertyType.additionalProperty) {
|
|
814
|
+
return property.originalInput === true
|
|
815
|
+
? `\n[k: string]: unknown;`
|
|
816
|
+
: renderer
|
|
817
|
+
.renderProperty(propertyName, property)
|
|
818
|
+
.replace('additionalProperties?:', '[k: string]:');
|
|
819
|
+
}
|
|
820
|
+
return renderer.renderProperty(propertyName, property, type);
|
|
821
|
+
},
|
|
822
|
+
},
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* Preset for TypeScriptGenerator, adds imports to resulting model.
|
|
827
|
+
*/
|
|
828
|
+
const IMPORTS_PRESET = {
|
|
829
|
+
interface: {
|
|
830
|
+
self({ content, model }) {
|
|
831
|
+
const interfaceName = model.getFromOriginalInput('title');
|
|
832
|
+
const interfacePrefix = getModelPathPrefix(interfaceName);
|
|
833
|
+
const dependencies = model.getNearestDependencies(model);
|
|
834
|
+
const imports = dependencies.map((dep) => ({
|
|
835
|
+
class: FormatHelpers.toPascalCase(dep),
|
|
836
|
+
location:
|
|
837
|
+
interfacePrefix !== getModelPathPrefix(dep)
|
|
838
|
+
? `../${getModelPathPrefix(dep)}/${FormatHelpers.toParamCase(dep)}`
|
|
839
|
+
: `./${FormatHelpers.toParamCase(dep)}`,
|
|
840
|
+
}));
|
|
841
|
+
return endent`
|
|
842
|
+
${imports
|
|
843
|
+
.sort((a, b) => a.location.localeCompare(b.location))
|
|
844
|
+
.map((i) => `import { ${i.class} } from '${i.location}';`)
|
|
845
|
+
.join('\n')}
|
|
846
|
+
${content}`;
|
|
847
|
+
},
|
|
848
|
+
property({ content }) {
|
|
849
|
+
return content;
|
|
850
|
+
},
|
|
851
|
+
},
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Preset for TypeScriptGenerator, sets nullable properties as unions in resulted models(compatibility with zapatos generated models).
|
|
856
|
+
*/
|
|
857
|
+
const NULLABLE_PROPERTY_TO_UNION_PRESET = {
|
|
858
|
+
interface: {
|
|
859
|
+
self({ content }) {
|
|
860
|
+
return content;
|
|
861
|
+
},
|
|
862
|
+
property({ renderer, model, content, propertyName }) {
|
|
863
|
+
if (model.getFromOriginalInput('properties') !== undefined) {
|
|
864
|
+
const property = model.getFromOriginalInput('properties')[propertyName];
|
|
865
|
+
if (property !== undefined) {
|
|
866
|
+
if (Array.isArray(property['type'])) {
|
|
867
|
+
const renderProp = renderer.renderProperty(propertyName, property);
|
|
868
|
+
return renderProp.replace(
|
|
869
|
+
'object',
|
|
870
|
+
content.split(':')[1].replace(';', ''),
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
return content;
|
|
877
|
+
},
|
|
878
|
+
},
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* Extension for @asyncapi/modelina, to make sure that the type name is in pascal.
|
|
883
|
+
*/
|
|
884
|
+
const CustomNamingConvention: CommonNamingConvention = {
|
|
885
|
+
type: (name) => {
|
|
886
|
+
if (!name) {
|
|
887
|
+
return '';
|
|
888
|
+
}
|
|
889
|
+
return FormatHelpers.toPascalCase(name);
|
|
890
|
+
},
|
|
891
|
+
};
|