Skip to content

Commit

Permalink
fix: parameter unwrapping (#128)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonaslagoni authored Oct 13, 2024
1 parent 234f32f commit f003eb0
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 84 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-nested-ternary */
import {SingleFunctionRenderType} from '../../../../../types';
import {pascalCase, unwrap} from '../../../utils';
import {findRegexFromChannel, pascalCase} from '../../../utils';
import {ConstrainedMetaModel, ConstrainedObjectModel} from '@asyncapi/modelina';

export function renderCoreReply({
Expand Down Expand Up @@ -114,7 +114,7 @@ ${functionName}: (
(async () => {
for await (const msg of subscription) {
${channelParameters ? unwrap(requestTopic, channelParameters) : ''}
${channelParameters ? `const parameters = ${channelParameters.type}.createFromChannel(msg.subject, ${findRegexFromChannel(requestTopic)})` : ''}
${receivingOperation}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-nested-ternary */
import {SingleFunctionRenderType} from '../../../../../types';
import {pascalCase, unwrap} from '../../../utils';
import {findRegexFromChannel, pascalCase} from '../../../utils';
import {ConstrainedMetaModel, ConstrainedObjectModel} from '@asyncapi/modelina';

export function renderCoreSubscribe({
Expand Down Expand Up @@ -109,7 +109,7 @@ ${functionName}: (
(async () => {
for await (const msg of subscription) {
${channelParameters ? unwrap(topic, channelParameters) : ''}
${channelParameters ? `const parameters = ${channelParameters.type}.createFromChannel(msg.subject, ${findRegexFromChannel(topic)})` : ''}
${whenReceivingMessage}
}
})();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-nested-ternary */
import {SingleFunctionRenderType} from '../../../../../types';
import {pascalCase, unwrap} from '../../../utils';
import {findRegexFromChannel, pascalCase} from '../../../utils';
import {ConstrainedMetaModel, ConstrainedObjectModel} from '@asyncapi/modelina';

export function renderJetstreamPullSubscribe({
Expand Down Expand Up @@ -107,7 +107,7 @@ ${functionName}: (
(async () => {
for await (const msg of subscription) {
${channelParameters ? unwrap(topic, channelParameters) : ''}
${channelParameters ? `const parameters = ${channelParameters.type}.createFromChannel(msg.subject, ${findRegexFromChannel(topic)})` : ''}
${whenReceivingMessage}
}
})();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-nested-ternary */
import {SingleFunctionRenderType} from '../../../../../types';
import {pascalCase, unwrap} from '../../../utils';
import {findRegexFromChannel, pascalCase} from '../../../utils';
import {ConstrainedMetaModel, ConstrainedObjectModel} from '@asyncapi/modelina';

export function renderJetstreamPushSubscription({
Expand Down Expand Up @@ -110,7 +110,7 @@ ${functionName}: (
(async () => {
for await (const msg of subscription) {
${channelParameters ? unwrap(topic, channelParameters) : ''}
${channelParameters ? `const parameters = ${channelParameters.type}.createFromChannel(msg.subject, ${findRegexFromChannel(topic)})` : ''}
${whenReceivingMessage}
}
})();
Expand Down
85 changes: 85 additions & 0 deletions src/codegen/generators/typescript/parameters.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import {
ConstrainedEnumModel,
ConstrainedObjectModel,
ConstrainedReferenceModel,
OutputModel,
TS_DESCRIPTION_PRESET,
TypeScriptFileGenerator
Expand Down Expand Up @@ -34,6 +37,84 @@ export interface TypescriptParametersContext extends GenericCodegenContext {
generator: TypescriptParametersGeneratorInternal;
}

/**
* Component which contains the parameter unwrapping functionality.
*
*
* Example
const regex = /^adeo-([^.]*)-case-study-COSTING-REQUEST-([^.]*)$/;
const match = channel.match(regex);
const parameters = new CostingRequestChannelParameters({env: "dev", version: ''});
if (match) {
const envMatch = match.at(1)
if(envMatch && envMatch !== '') {
parameters.env = envMatch as any
} else {
throw new Error(`Parameter: 'env' is not valid. Abort! `)
}
const versionMatch = match.at(2)
if(versionMatch && versionMatch !== '') {
parameters.version = versionMatch as any
} else {
throw new Error(`Parameter: 'version' is not valid. Abort! `)
}
} else {
throw new Error(`Unable to find parameters in channe/topic, topic was ${channel}`)
}
return parameters;
*
*/
export function unwrap(
channelParameters: ConstrainedObjectModel
) {
// Nothing to unwrap if no parameters are used
if (Object.keys(channelParameters.properties).length === 0) {
return '';
}

// Use channel to iterate over matches as channelParameters.properties might be in incorrect order.

const parameterReplacement = Object.values(channelParameters.properties).map(
(parameter) => {
const variableName = `${parameter.propertyName}Match`;
return `const ${variableName} = match[sequentialParameters.indexOf('{${parameter.unconstrainedPropertyName}}')];
if(${variableName} && ${variableName} !== '') {
parameters.${parameter.propertyName} = ${variableName} as any
} else {
throw new Error(\`Parameter: '${parameter.propertyName}' is not valid. Abort! \`)
}`;
}
);

const parameterInitializer = Object.values(channelParameters.properties).map(
(parameter) => {
if (parameter.property.options.isNullable) {
return `${parameter.propertyName}: null`;
}
const property = parameter.property;
if (
property instanceof ConstrainedReferenceModel &&
property.ref instanceof ConstrainedEnumModel
) {
return `${parameter.propertyName}: ${property.ref.values[0].value}`;
}
return `${parameter.propertyName}: ''`;
}
);

return `const parameters = new ${channelParameters.name}({${parameterInitializer.join(', ')}});
const match = channel.match(regex);
const sequentialParameters = channel.match(/\\{(\\w+)\\}/g)?.map(param => param.slice(1, -1)) || [];
if (match) {
${parameterReplacement.join('\n')}
} else {
throw new Error(\`Unable to find parameters in channel/topic, topic was \${channel}\`)
}
return parameters;`;
}

export async function generateTypescriptParameters(
context: TypescriptParametersContext
): Promise<ParameterRenderType> {
Expand Down Expand Up @@ -62,6 +143,10 @@ export async function generateTypescriptParameters(
public getChannelWithParameters(channel: string) {
${renderer.renderBlock(parameters)};
return channel;
}
public static createFromChannel(channel: string, regex: RegExp): ${model.type} {
${unwrap(model)}
}`;
}
}
Expand Down
84 changes: 8 additions & 76 deletions src/codegen/generators/typescript/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {
checkForReservedKeyword,
ConstrainedEnumModel,
ConstrainedObjectModel,
ConstrainedReferenceModel,
FormatHelpers,
NO_RESERVED_KEYWORDS,
typeScriptDefaultModelNameConstraints,
Expand All @@ -11,79 +9,6 @@ import {
} from '@asyncapi/modelina';
import {DeepPartial} from '../../utils';

/**
* Component which contains the parameter unwrapping functionality.
*
*
* Example
const unmodifiedChannel = `streetlight.{streetlight_id}.command.turnon`;
const channel = msg.subject;
const streetlightIdSplit = unmodifiedChannel.split("{streetlight_id}");
const splits = [
streetlightIdSplit[0],
streetlightIdSplit[1]
];
channel = channel.substring(splits[0].length);
const streetlightIdEnd = channel.indexOf(splits[1]);
const streetlightIdParam = "" + channel.substring(0, streetlightIdEnd);
*
*/
export function unwrap(
channelName: string,
channelParameters: ConstrainedObjectModel
) {
// Nothing to unwrap if no parameters are used
if (Object.keys(channelParameters.properties).length === 0) {
return '';
}

const parameterReplacement = Object.values(channelParameters.properties).map(
(parameter, index) => {
const variableName = `${parameter.propertyName}Match`;
return `const ${variableName} = match.at(${index + 1})
if(${variableName} && ${variableName} !== '') {
parameters.${parameter.propertyName} = ${variableName} as any
} else {
throw new Error(\`Parameter: '${parameter.propertyName}' is not valid. Abort! \`)
}`;
}
);

const parameterInitializer = Object.values(channelParameters.properties).map(
(parameter, index) => {
if (parameter.property.options.isNullable) {
return `${parameter.propertyName}: null`;
}
const property = parameter.property;
if (
property instanceof ConstrainedReferenceModel &&
property.ref instanceof ConstrainedEnumModel
) {
return `${parameter.propertyName}: ${property.ref.values.at(0)?.value}`;
}
return `${parameter.propertyName}: ''`;
}
);

let topicWithWildcardGroup = channelName.replaceAll(/\//g, '\\/');
topicWithWildcardGroup = topicWithWildcardGroup.replaceAll(
/{[^}]+}/g,
'([^.]*)'
);
const regexMatch = `/^${topicWithWildcardGroup}$/`;

return `const regex = ${regexMatch};
const parameters = new ${channelParameters.name}({${parameterInitializer.join(', ')}});
const match = msg.subject.match(regex);
if (match) {
${parameterReplacement.join('\n')}
} else {
console.error(\`Was not able to retrieve parameters, ignoring message.\`);
return;
}`;
}

/**
* Cast JSON schema variable to typescript type
*
Expand Down Expand Up @@ -216,7 +141,14 @@ export function camelCase(value: string) {
export function pascalCase(value: string) {
return FormatHelpers.toPascalCase(value);
}

export function findRegexFromChannel(channel: string): string {
let topicWithWildcardGroup = channel.replaceAll(/\//g, '\\/');
topicWithWildcardGroup = topicWithWildcardGroup.replaceAll(
/{[^}]+}/g,
'([^.]*)'
);
return `/^${topicWithWildcardGroup}$/`;
}
export const RESERVED_TYPESCRIPT_KEYWORDS = [
'break',
'case',
Expand Down

0 comments on commit f003eb0

Please sign in to comment.