Skip to content

Commit 502a491

Browse files
committed
wip
1 parent 9b468de commit 502a491

File tree

25 files changed

+3619
-60
lines changed

25 files changed

+3619
-60
lines changed

assets/videos/remotion-test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 58d77ef55f0ecc86b70ccd2b149abd1e779f21c2

src/codegen/generators/typescript/channels/protocols/amqp/publishExchange.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@ export function renderPublishExchange({
2424
messageMarshalling = `${messageModule}.marshal(message)`;
2525
}
2626
messageType = messageModule ? `${messageModule}.${messageType}` : messageType;
27-
const publishOperation = `let dataToSend: any = ${messageType === 'null' ? 'null' : messageMarshalling};
28-
const channel = await amqp.createChannel();
29-
const routingKey = ${addressToUse};
30-
// Set up message properties (headers) if provided
27+
28+
const headersHandling = channelHeaders
29+
? `// Set up message properties (headers) if provided
3130
let publishOptions = { ...options };
3231
if (headers) {
3332
const headerData = headers.marshal();
@@ -38,7 +37,13 @@ if (headers) {
3837
publishOptions.headers[key] = value;
3938
}
4039
}
41-
}
40+
}`
41+
: `let publishOptions = { ...options };`;
42+
43+
const publishOperation = `let dataToSend: any = ${messageType === 'null' ? 'null' : messageMarshalling};
44+
const channel = await amqp.createChannel();
45+
const routingKey = ${addressToUse};
46+
${headersHandling}
4247
channel.publish(exchange, routingKey, Buffer.from(dataToSend), publishOptions);`;
4348

4449
const functionParameters = [

src/codegen/generators/typescript/channels/protocols/amqp/publishQueue.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@ export function renderPublishQueue({
2121
messageMarshalling = `${messageModule}.marshal(message)`;
2222
}
2323
messageType = messageModule ? `${messageModule}.${messageType}` : messageType;
24-
const publishOperation = `let dataToSend: any = ${messageType === 'null' ? 'null' : messageMarshalling};
25-
const channel = await amqp.createChannel();
26-
const queue = ${addressToUse};
27-
// Set up message properties (headers) if provided
24+
25+
const headersHandling = channelHeaders
26+
? `// Set up message properties (headers) if provided
2827
let publishOptions = { ...options };
2928
if (headers) {
3029
const headerData = headers.marshal();
@@ -35,7 +34,13 @@ if (headers) {
3534
publishOptions.headers[key] = value;
3635
}
3736
}
38-
}
37+
}`
38+
: `let publishOptions = { ...options };`;
39+
40+
const publishOperation = `let dataToSend: any = ${messageType === 'null' ? 'null' : messageMarshalling};
41+
const channel = await amqp.createChannel();
42+
const queue = ${addressToUse};
43+
${headersHandling}
3944
channel.sendToQueue(queue, Buffer.from(dataToSend), publishOptions);`;
4045

4146
const functionParameters = [

src/codegen/generators/typescript/channels/protocols/kafka/publish.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,23 @@ export function renderPublish({
5858
}
5959
];
6060

61+
const headersHandling = channelHeaders
62+
? `// Set up headers if provided
63+
let messageHeaders: Record<string, string> | undefined = undefined;
64+
if (headers) {
65+
const headerData = headers.marshal();
66+
const parsedHeaders = typeof headerData === 'string' ? JSON.parse(headerData) : headerData;
67+
messageHeaders = {};
68+
for (const [key, value] of Object.entries(parsedHeaders)) {
69+
if (value !== undefined) {
70+
messageHeaders[key] = String(value);
71+
}
72+
}
73+
}`
74+
: '';
75+
76+
const headersInMessage = channelHeaders ? 'headers: messageHeaders' : '';
77+
6178
const code = `/**
6279
* Kafka publish operation for \`${topic}\`
6380
*
@@ -73,25 +90,13 @@ function ${functionName}({
7390
${publishOperation}
7491
const producer = kafka.producer();
7592
await producer.connect();
76-
// Set up headers if provided
77-
let messageHeaders: Record<string, string> | undefined = undefined;
78-
if (headers) {
79-
const headerData = headers.marshal();
80-
const parsedHeaders = typeof headerData === 'string' ? JSON.parse(headerData) : headerData;
81-
messageHeaders = {};
82-
for (const [key, value] of Object.entries(parsedHeaders)) {
83-
if (value !== undefined) {
84-
messageHeaders[key] = String(value);
85-
}
86-
}
87-
}
93+
${headersHandling}
8894
8995
await producer.send({
9096
topic: ${addressToUse},
9197
messages: [
9298
{
93-
value: dataToSend,
94-
headers: messageHeaders
99+
value: dataToSend${channelHeaders ? ',\n ' + headersInMessage : ''}
95100
},
96101
],
97102
});

src/codegen/generators/typescript/channels/protocols/mqtt/publish.ts

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,35 +21,29 @@ export function renderPublish({
2121
messageMarshalling = `${messageModule}.marshal(message)`;
2222
}
2323
messageType = messageModule ? `${messageModule}.${messageType}` : messageType;
24-
const publishOperation =
25-
messageType === 'null'
26-
? `// Set up user properties (headers) if provided
27-
let publishOptions: Mqtt.IClientPublishOptions = {};
28-
if (headers) {
29-
const headerData = headers.marshal();
30-
const parsedHeaders = typeof headerData === 'string' ? JSON.parse(headerData) : headerData;
31-
publishOptions.properties = { userProperties: {} };
32-
for (const [key, value] of Object.entries(parsedHeaders)) {
33-
if (value !== undefined) {
34-
publishOptions.properties.userProperties[key] = String(value);
35-
}
36-
}
37-
}
38-
mqtt.publish(${addressToUse}, '', publishOptions);`
39-
: `let dataToSend: any = ${messageMarshalling};
40-
// Set up user properties (headers) if provided
24+
25+
const headersHandling = channelHeaders
26+
? `// Set up user properties (headers) if provided
4127
let publishOptions: Mqtt.IClientPublishOptions = {};
4228
if (headers) {
4329
const headerData = headers.marshal();
4430
const parsedHeaders = typeof headerData === 'string' ? JSON.parse(headerData) : headerData;
45-
const userProperties = {};
31+
const userProperties: Record<string, string> = {};
4632
for (const [key, value] of Object.entries(parsedHeaders)) {
4733
if (value !== undefined) {
4834
userProperties[key] = String(value);
4935
}
5036
}
5137
publishOptions.properties = { userProperties };
52-
}
38+
}`
39+
: `let publishOptions: Mqtt.IClientPublishOptions = {};`;
40+
41+
const publishOperation =
42+
messageType === 'null'
43+
? `${headersHandling}
44+
mqtt.publish(${addressToUse}, '', publishOptions);`
45+
: `let dataToSend: any = ${messageMarshalling};
46+
${headersHandling}
5347
mqtt.publish(${addressToUse}, dataToSend, publishOptions);`;
5448

5549
const functionParameters = [

src/codegen/generators/typescript/channels/protocols/websocket/subscribe.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function ${functionName}({
9999
100100
// Validate message if validation is enabled
101101
if (!skipMessageValidation) {
102-
const messageToValidate = parsedMessage.marshal();
102+
const messageToValidate = ${messageModule ? `${messageModule}.marshal(parsedMessage)` : `parsedMessage.marshal()`};
103103
const {valid, errors} = ${messageModule ? `${messageModule}.validate({data: messageToValidate, ajvValidatorFunction: validator})` : `${messageType}.validate({data: messageToValidate, ajvValidatorFunction: validator})`};
104104
if (!valid) {
105105
onDataCallback({

src/codegen/generators/typescript/payloads.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import {OpenAPIV2, OpenAPIV3, OpenAPIV3_1} from 'openapi-types';
1717
import {TS_COMMON_PRESET} from '@asyncapi/modelina';
1818
import {
1919
createValidationPreset,
20-
createUnionPreset
20+
createUnionPreset,
21+
createPrimitivesPreset
2122
} from '../../modelina/presets';
2223

2324
export const zodTypeScriptPayloadGenerator = z.object({
@@ -144,6 +145,12 @@ export async function generateTypescriptPayloadsCoreFromSchemas({
144145
includeValidation: generator.includeValidation
145146
},
146147
context
148+
),
149+
createPrimitivesPreset(
150+
{
151+
includeValidation: generator.includeValidation
152+
},
153+
context
147154
)
148155
],
149156
enumType: generator.enum,

src/codegen/modelina/presets/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ export {
88

99
// Core union marshalling/unmarshalling presets
1010
export {createUnionPreset, type UnionPresetOptions} from './union';
11+
12+
// Primitive and array type marshalling/unmarshalling presets
13+
export {
14+
createPrimitivesPreset,
15+
type PrimitivesPresetOptions
16+
} from './primitives';
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import {
2+
ConstrainedMetaModel,
3+
ConstrainedStringModel,
4+
ConstrainedIntegerModel,
5+
ConstrainedFloatModel,
6+
ConstrainedBooleanModel,
7+
ConstrainedArrayModel
8+
} from '@asyncapi/modelina';
9+
import {TypeScriptRenderer} from '@asyncapi/modelina/lib/types/generators/typescript/TypeScriptRenderer';
10+
import {
11+
BaseGeneratorContext,
12+
generateTypescriptValidationCode
13+
} from './validation';
14+
15+
/**
16+
* Configuration options for the primitives preset
17+
*/
18+
export interface PrimitivesPresetOptions {
19+
/** Whether to include validation methods in generated primitive types */
20+
includeValidation: boolean;
21+
}
22+
23+
/**
24+
* Check if the model is a primitive type (string, integer, float, boolean)
25+
*/
26+
function isPrimitiveModel(model: ConstrainedMetaModel): boolean {
27+
return (
28+
model instanceof ConstrainedStringModel ||
29+
model instanceof ConstrainedIntegerModel ||
30+
model instanceof ConstrainedFloatModel ||
31+
model instanceof ConstrainedBooleanModel
32+
);
33+
}
34+
35+
/**
36+
* Render marshal function for primitive types
37+
*/
38+
function renderPrimitiveMarshal(model: ConstrainedMetaModel): string {
39+
return `export function marshal(payload: ${model.name}): string {
40+
return JSON.stringify(payload);
41+
}`;
42+
}
43+
44+
/**
45+
* Render unmarshal function for primitive types
46+
*/
47+
function renderPrimitiveUnmarshal(model: ConstrainedMetaModel): string {
48+
// For string types, the input can be a JSON string (quoted) or the raw value
49+
// We use 'any' for the json parameter since JSON.parse returns 'any'
50+
return `export function unmarshal(json: string): ${model.name} {
51+
return JSON.parse(json) as ${model.name};
52+
}`;
53+
}
54+
55+
/**
56+
* Render marshal function for array types
57+
*/
58+
function renderArrayMarshal(model: ConstrainedArrayModel): string {
59+
const valueModel = model.valueModel;
60+
61+
// Check if array items have a marshal method (object types)
62+
const hasItemMarshal =
63+
valueModel.type !== 'string' &&
64+
valueModel.type !== 'number' &&
65+
valueModel.type !== 'boolean';
66+
67+
if (hasItemMarshal) {
68+
return `export function marshal(payload: ${model.name}): string {
69+
return JSON.stringify(payload.map((item) => {
70+
if (item && typeof item === 'object' && 'marshal' in item && typeof item.marshal === 'function') {
71+
return JSON.parse(item.marshal());
72+
}
73+
return item;
74+
}));
75+
}`;
76+
}
77+
78+
return `export function marshal(payload: ${model.name}): string {
79+
return JSON.stringify(payload);
80+
}`;
81+
}
82+
83+
/**
84+
* Render unmarshal function for array types
85+
*/
86+
function renderArrayUnmarshal(model: ConstrainedArrayModel): string {
87+
const valueModel = model.valueModel;
88+
89+
// Check if array items have an unmarshal method (object types)
90+
const hasItemUnmarshal =
91+
valueModel.type !== 'string' &&
92+
valueModel.type !== 'number' &&
93+
valueModel.type !== 'boolean';
94+
95+
if (hasItemUnmarshal) {
96+
return `export function unmarshal(json: string | any[]): ${model.name} {
97+
const arr = typeof json === 'string' ? JSON.parse(json) : json;
98+
return arr.map((item: any) => {
99+
if (item && typeof item === 'object') {
100+
// Try to use unmarshal if available on the type
101+
return item;
102+
}
103+
return item;
104+
}) as ${model.name};
105+
}`;
106+
}
107+
108+
return `export function unmarshal(json: string | any[]): ${model.name} {
109+
if (typeof json === 'string') {
110+
return JSON.parse(json) as ${model.name};
111+
}
112+
return json as ${model.name};
113+
}`;
114+
}
115+
116+
/**
117+
* Creates a preset that adds marshalling/unmarshalling and validation methods
118+
* to primitive types (string, number, boolean) and array types
119+
*
120+
* @param options Configuration for primitive generation
121+
* @param context Generator context containing input type information
122+
* @returns Modelina preset object with primitive marshalling functionality
123+
*
124+
* @example
125+
* ```typescript
126+
* const preset = createPrimitivesPreset({
127+
* includeValidation: true
128+
* }, context);
129+
* ```
130+
*/
131+
export function createPrimitivesPreset(
132+
options: PrimitivesPresetOptions,
133+
context: BaseGeneratorContext
134+
) {
135+
return {
136+
type: {
137+
self({
138+
model,
139+
content,
140+
renderer
141+
}: {
142+
model: ConstrainedMetaModel;
143+
content: string;
144+
renderer: TypeScriptRenderer;
145+
}) {
146+
// Handle primitive types (string, integer, float, boolean)
147+
if (isPrimitiveModel(model)) {
148+
return `${content}
149+
150+
${renderPrimitiveUnmarshal(model)}
151+
${renderPrimitiveMarshal(model)}
152+
${options.includeValidation ? generateTypescriptValidationCode({model, renderer, asClassMethods: false, context: context as any}) : ''}
153+
`;
154+
}
155+
156+
// Handle array types
157+
if (model instanceof ConstrainedArrayModel) {
158+
return `${content}
159+
160+
${renderArrayUnmarshal(model)}
161+
${renderArrayMarshal(model)}
162+
${options.includeValidation ? generateTypescriptValidationCode({model, renderer, asClassMethods: false, context: context as any}) : ''}
163+
`;
164+
}
165+
166+
return content;
167+
}
168+
}
169+
};
170+
}

0 commit comments

Comments
 (0)