|
| 1 | +/* eslint-disable security/detect-object-injection */ |
1 | 2 | import { |
2 | | - ConstrainedEnumModel, |
3 | | - ConstrainedObjectModel, |
4 | | - ConstrainedReferenceModel, |
5 | 3 | OutputModel, |
6 | | - TS_DESCRIPTION_PRESET, |
7 | 4 | TypeScriptFileGenerator |
8 | 5 | } from '@asyncapi/modelina'; |
9 | 6 | import {AsyncAPIDocumentInterface} from '@asyncapi/parser'; |
10 | 7 | import {GenericCodegenContext, ParameterRenderType} from '../../types'; |
11 | 8 | import {z} from 'zod'; |
12 | | -import {findNameFromChannel} from '../../utils'; |
13 | | -import {defaultCodegenTypescriptModelinaOptions, pascalCase} from './utils'; |
14 | 9 | import {OpenAPIV2, OpenAPIV3, OpenAPIV3_1} from 'openapi-types'; |
| 10 | +import { |
| 11 | + createAsyncAPIGenerator, |
| 12 | + processAsyncAPIParameters, |
| 13 | + ProcessedParameterSchemaData |
| 14 | +} from '../../inputs/asyncapi/generators/parameters'; |
| 15 | +import {createOpenAPIGenerator, processOpenAPIParameters} from '../../inputs/openapi/generators/parameters'; |
15 | 16 |
|
16 | 17 | export const zodTypescriptParametersGenerator = z.object({ |
17 | 18 | id: z.string().optional().default('parameters-typescript'), |
@@ -42,154 +43,62 @@ export interface TypescriptParametersContext extends GenericCodegenContext { |
42 | 43 | generator: TypescriptParametersGeneratorInternal; |
43 | 44 | } |
44 | 45 |
|
45 | | -/** |
46 | | - * Component which contains the parameter unwrapping functionality. |
47 | | - * |
48 | | - * |
49 | | - * Example |
50 | | -const regex = /^adeo-([^.]*)-case-study-COSTING-REQUEST-([^.]*)$/; |
51 | | -const match = channel.match(regex); |
52 | | -
|
53 | | -const parameters = new CostingRequestChannelParameters({env: "dev", version: ''}); |
54 | | -if (match) { |
55 | | - const envMatch = match.at(1) |
56 | | - if(envMatch && envMatch !== '') { |
57 | | - parameters.env = envMatch as any |
58 | | - } else { |
59 | | - throw new Error(`Parameter: 'env' is not valid. Abort! `) |
60 | | - } |
61 | | - const versionMatch = match.at(2) |
62 | | - if(versionMatch && versionMatch !== '') { |
63 | | - parameters.version = versionMatch as any |
64 | | - } else { |
65 | | - throw new Error(`Parameter: 'version' is not valid. Abort! `) |
66 | | - } |
67 | | -} else { |
68 | | - throw new Error(`Unable to find parameters in channe/topic, topic was ${channel}`) |
69 | | -} |
70 | | -return parameters; |
71 | | - * |
72 | | - */ |
73 | | -export function unwrap(channelParameters: ConstrainedObjectModel) { |
74 | | - // Nothing to unwrap if no parameters are used |
75 | | - if (Object.keys(channelParameters.properties).length === 0) { |
76 | | - return ''; |
77 | | - } |
78 | | - |
79 | | - // Use channel to iterate over matches as channelParameters.properties might be in incorrect order. |
80 | | - |
81 | | - const parameterReplacement = Object.values(channelParameters.properties).map( |
82 | | - (parameter) => { |
83 | | - const variableName = `${parameter.propertyName}Match`; |
84 | | - return `const ${variableName} = match[sequentialParameters.indexOf('{${parameter.unconstrainedPropertyName}}')+1]; |
85 | | - if(${variableName} && ${variableName} !== '') { |
86 | | - parameters.${parameter.propertyName} = ${variableName} as any |
87 | | - } else { |
88 | | - throw new Error(\`Parameter: '${parameter.propertyName}' is not valid. Abort! \`) |
89 | | - }`; |
90 | | - } |
91 | | - ); |
92 | | - |
93 | | - const parameterInitializer = Object.values(channelParameters.properties).map( |
94 | | - (parameter) => { |
95 | | - if (parameter.property.options.isNullable) { |
96 | | - return `${parameter.propertyName}: null`; |
97 | | - } |
98 | | - const property = parameter.property; |
99 | | - if ( |
100 | | - property instanceof ConstrainedReferenceModel && |
101 | | - property.ref instanceof ConstrainedEnumModel |
102 | | - ) { |
103 | | - return `${parameter.propertyName}: ${property.ref.values[0].value}`; |
104 | | - } |
105 | | - return `${parameter.propertyName}: ''`; |
106 | | - } |
107 | | - ); |
108 | | - |
109 | | - return `const parameters = new ${channelParameters.name}({${parameterInitializer.join(', ')}}); |
110 | | -const match = msgSubject.match(regex); |
111 | | -const sequentialParameters: string[] = channel.match(/\\{(\\w+)\\}/g) || []; |
112 | | -
|
113 | | -if (match) { |
114 | | - ${parameterReplacement.join('\n')} |
115 | | -} else { |
116 | | - throw new Error(\`Unable to find parameters in channel/topic, topic was \${channel}\`) |
117 | | -} |
118 | | -return parameters;`; |
119 | | -} |
120 | | - |
121 | 46 | export type TypeScriptParameterRenderType = |
122 | 47 | ParameterRenderType<TypescriptParametersGeneratorInternal>; |
123 | 48 |
|
| 49 | +// Main generator function that orchestrates input processing and generation |
124 | 50 | export async function generateTypescriptParameters( |
125 | 51 | context: TypescriptParametersContext |
126 | 52 | ): Promise<TypeScriptParameterRenderType> { |
127 | | - const {asyncapiDocument, inputType, generator} = context; |
128 | | - if (inputType === 'asyncapi' && asyncapiDocument === undefined) { |
129 | | - throw new Error('Expected AsyncAPI input, was not given'); |
130 | | - } |
131 | | - const modelinaGenerator = new TypeScriptFileGenerator({ |
132 | | - ...defaultCodegenTypescriptModelinaOptions, |
133 | | - enumType: 'union', |
134 | | - useJavascriptReservedKeywords: false, |
135 | | - presets: [ |
136 | | - TS_DESCRIPTION_PRESET, |
137 | | - { |
138 | | - class: { |
139 | | - additionalContent: ({content, model, renderer}) => { |
140 | | - const parameters = Object.entries(model.properties).map( |
141 | | - ([, parameter]) => { |
142 | | - return `channel = channel.replace(/\\{${parameter.unconstrainedPropertyName}\\}/g, this.${parameter.propertyName})`; |
143 | | - } |
144 | | - ); |
145 | | - return `${content} |
146 | | -/** |
147 | | - * Realize the channel/topic with the parameters added to this class. |
148 | | - */ |
149 | | -public getChannelWithParameters(channel: string) { |
150 | | - ${renderer.renderBlock(parameters)}; |
151 | | - return channel; |
152 | | -} |
153 | | - |
154 | | -public static createFromChannel(msgSubject: string, channel: string, regex: RegExp): ${model.type} { |
155 | | - ${unwrap(model)} |
156 | | -}`; |
157 | | - } |
158 | | - } |
| 53 | + const {asyncapiDocument, openapiDocument, inputType, generator} = context; |
| 54 | + |
| 55 | + const channelModels: Record<string, OutputModel | undefined> = {}; |
| 56 | + let processedSchemaData: ProcessedParameterSchemaData; |
| 57 | + let parameterGenerator: TypeScriptFileGenerator; |
| 58 | + |
| 59 | + // Process input based on type |
| 60 | + switch (inputType) { |
| 61 | + case 'asyncapi': { |
| 62 | + if (!asyncapiDocument) { |
| 63 | + throw new Error('Expected AsyncAPI input, was not given'); |
159 | 64 | } |
160 | | - ] |
161 | | - }); |
162 | | - const returnType: Record<string, OutputModel | undefined> = {}; |
163 | | - for (const channel of asyncapiDocument!.allChannels().all()) { |
164 | | - const parameters = channel.parameters().all(); |
165 | | - if (parameters.length > 0) { |
166 | | - const schemaObj: any = { |
167 | | - type: 'object', |
168 | | - $id: pascalCase(`${findNameFromChannel(channel)}_parameters`), |
169 | | - $schema: 'http://json-schema.org/draft-07/schema', |
170 | | - required: [], |
171 | | - properties: {}, |
172 | | - additionalProperties: false, |
173 | | - 'x-channel-address': channel.address() |
174 | | - }; |
175 | | - for (const parameter of channel.parameters().all()) { |
176 | | - schemaObj.properties[parameter.id()] = parameter.schema()?.json(); |
177 | | - schemaObj.required.push(parameter.id()); |
| 65 | + |
| 66 | + processedSchemaData = await processAsyncAPIParameters(asyncapiDocument); |
| 67 | + parameterGenerator = createAsyncAPIGenerator(); |
| 68 | + break; |
| 69 | + } |
| 70 | + case 'openapi': { |
| 71 | + if (!openapiDocument) { |
| 72 | + throw new Error('Expected OpenAPI input, was not given'); |
178 | 73 | } |
179 | | - const models = await modelinaGenerator.generateToFiles( |
180 | | - schemaObj, |
| 74 | + |
| 75 | + processedSchemaData = processOpenAPIParameters(openapiDocument); |
| 76 | + parameterGenerator = createOpenAPIGenerator(); |
| 77 | + break; |
| 78 | + } |
| 79 | + default: |
| 80 | + throw new Error(`Unsupported input type: ${inputType}`); |
| 81 | + } |
| 82 | + |
| 83 | + // Generate models for channel parameters |
| 84 | + for (const [channelId, schemaData] of Object.entries( |
| 85 | + processedSchemaData.channelParameters |
| 86 | + )) { |
| 87 | + if (schemaData) { |
| 88 | + const models = await parameterGenerator.generateToFiles( |
| 89 | + schemaData.schema, |
181 | 90 | generator.outputPath, |
182 | 91 | {exportType: 'named'}, |
183 | 92 | true |
184 | 93 | ); |
185 | | - returnType[channel.id()] = models[0]; |
| 94 | + channelModels[channelId] = models.length > 0 ? models[0] : undefined; |
186 | 95 | } else { |
187 | | - returnType[channel.id()] = undefined; |
| 96 | + channelModels[channelId] = undefined; |
188 | 97 | } |
189 | 98 | } |
190 | 99 |
|
191 | 100 | return { |
192 | | - channelModels: returnType, |
| 101 | + channelModels, |
193 | 102 | generator |
194 | 103 | }; |
195 | 104 | } |
0 commit comments