Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for AsyncAPI v3 #186

Merged
merged 6 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 11 additions & 20 deletions components/Common.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ public void close() {
}

export function EnvJson({ asyncapi, params }) {
const url = asyncapi.server(params.server).url();
const protocol = asyncapi.server(params.server).protocol();
const server = asyncapi.allServers().get(params.server);
const url = server.url();
const protocol = server.protocol();
let user = params.user;
let password = params.password;

Expand All @@ -126,12 +127,13 @@ export function EnvJson({ asyncapi, params }) {
const host = URLtoHost(url);
const domain = host.split(':', 1);
let cipher = protocol === 'ibmmq-secure' ? 'ANY' : '';
const server = asyncapi.allServers().get(params.server);

if (
protocol === 'ibmmq-secure' &&
asyncapi.server(params.server).bindings().ibmmq.cipherSpec
) {
cipher = MQCipherToJava(asyncapi.server(params.server).bindings().ibmmq.cipherSpec);
protocol === 'ibmmq-secure' &&
server.bindings().get('ibmmq').value().cipherSpec
) {
cipher = MQCipherToJava(server.bindings().get('ibmmq').value().cipherSpec);
}

return `
Expand Down Expand Up @@ -176,20 +178,9 @@ import ${params.package}.models.${messageName};`;

/* Used to resolve a channel object to message name */
export function ChannelToMessage(channel, asyncapi) {
// Get payload from either publish or subscribe
const targetPayloadProperties = Object.prototype.hasOwnProperty.call(channel, 'publish') ?
channel.publish().message().payload().properties() :
channel.subscribe().message().payload().properties();

// Find message name from messages array
const messages = asyncapi.components().messages();
let targetMessageName;

for (const message in messages) {
if (messages[message].payload().properties().toString() === targetPayloadProperties.toString()) {
targetMessageName = message;
}
}
const message = channel.messages().all()[0];
const targetPayloadProperties = message.payload().properties();
const targetMessageName = message.name();

const messageNameTitleCase = targetMessageName.charAt(0).toUpperCase() + targetMessageName.slice(1);

Expand Down
84 changes: 42 additions & 42 deletions components/Connection/MQTLS.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
export function MQCipherToJava(cipher) {
// List in line with Oracle JRE mappings from https://ibm.biz/mq-cipher-mappings
const ciphers = {
ECDHE_ECDSA_3DES_EDE_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
ECDHE_ECDSA_AES_128_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
ECDHE_ECDSA_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
ECDHE_ECDSA_AES_256_CBC_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
ECDHE_ECDSA_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
ECDHE_ECDSA_NULL_SHA256: "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
ECDHE_ECDSA_RC4_128_SHA256: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
ECDHE_RSA_3DES_EDE_CBC_SHA256: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
ECDHE_RSA_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
ECDHE_RSA_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
ECDHE_RSA_AES_256_CBC_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
ECDHE_RSA_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
ECDHE_RSA_NULL_SHA256: "TLS_ECDHE_RSA_WITH_NULL_SHA",
ECDHE_RSA_RC4_128_SHA256: "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
TLS_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA",
TLS_RSA_WITH_AES_128_CBC_SHA256: "TLS_RSA_WITH_AES_128_CBC_SHA256",
TLS_RSA_WITH_AES_128_GCM_SHA256: "TLS_RSA_WITH_AES_128_GCM_SHA256",
TLS_RSA_WITH_AES_256_CBC_SHA: "TLS_RSA_WITH_AES_256_CBC_SHA",
TLS_RSA_WITH_AES_256_CBC_SHA256: "TLS_RSA_WITH_AES_256_CBC_SHA256",
TLS_RSA_WITH_AES_256_GCM_SHA384: "TLS_RSA_WITH_AES_256_GCM_SHA384",
TLS_RSA_WITH_DES_CBC_SHA: "SSL_RSA_WITH_DES_CBC_SHA",
TLS_RSA_WITH_NULL_SHA256: "TLS_RSA_WITH_NULL_SHA256",
TLS_RSA_WITH_RC4_128_SHA256: "SSL_RSA_WITH_RC4_128_SHA",
ANY_TLS12: "*TLS12",
TLS_AES_128_GCM_SHA256: "TLS_AES_128_GCM_SHA256",
TLS_AES_256_GCM_SHA384: "TLS_AES_256_GCM_SHA384",
TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256",
TLS_AES_128_CCM_SHA256: "TLS_AES_128_CCM_SHA256",
TLS_AES_128_CCM_8_SHA256: "TLS_AES_128_CCM_8_SHA256",
ANY: "*ANY",
ANY_TLS13: "*TLS13",
ANY_TLS12_OR_HIGHER: "*TLS12ORHIGHER",
ANY_TLS13_OR_HIGHER: "*TLS13ORHIGHER"
}
// List in line with Oracle JRE mappings from https://ibm.biz/mq-cipher-mappings
const ciphers = {
ECDHE_ECDSA_3DES_EDE_CBC_SHA256: 'TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA',
ECDHE_ECDSA_AES_128_CBC_SHA256: 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256',
ECDHE_ECDSA_AES_128_GCM_SHA256: 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
ECDHE_ECDSA_AES_256_CBC_SHA384: 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384',
ECDHE_ECDSA_AES_256_GCM_SHA384: 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384',
ECDHE_ECDSA_NULL_SHA256: 'TLS_ECDHE_ECDSA_WITH_NULL_SHA',
ECDHE_ECDSA_RC4_128_SHA256: 'TLS_ECDHE_ECDSA_WITH_RC4_128_SHA',
ECDHE_RSA_3DES_EDE_CBC_SHA256: 'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA',
ECDHE_RSA_AES_128_CBC_SHA256: 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256',
ECDHE_RSA_AES_128_GCM_SHA256: 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256',
ECDHE_RSA_AES_256_CBC_SHA384: 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384',
ECDHE_RSA_AES_256_GCM_SHA384: 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384',
ECDHE_RSA_NULL_SHA256: 'TLS_ECDHE_RSA_WITH_NULL_SHA',
ECDHE_RSA_RC4_128_SHA256: 'TLS_ECDHE_RSA_WITH_RC4_128_SHA',
TLS_RSA_WITH_3DES_EDE_CBC_SHA: 'TLS_RSA_WITH_3DES_EDE_CBC_SHA',
TLS_RSA_WITH_AES_128_CBC_SHA: 'TLS_RSA_WITH_AES_128_CBC_SHA',
TLS_RSA_WITH_AES_128_CBC_SHA256: 'TLS_RSA_WITH_AES_128_CBC_SHA256',
TLS_RSA_WITH_AES_128_GCM_SHA256: 'TLS_RSA_WITH_AES_128_GCM_SHA256',
TLS_RSA_WITH_AES_256_CBC_SHA: 'TLS_RSA_WITH_AES_256_CBC_SHA',
TLS_RSA_WITH_AES_256_CBC_SHA256: 'TLS_RSA_WITH_AES_256_CBC_SHA256',
TLS_RSA_WITH_AES_256_GCM_SHA384: 'TLS_RSA_WITH_AES_256_GCM_SHA384',
TLS_RSA_WITH_DES_CBC_SHA: 'SSL_RSA_WITH_DES_CBC_SHA',
TLS_RSA_WITH_NULL_SHA256: 'TLS_RSA_WITH_NULL_SHA256',
TLS_RSA_WITH_RC4_128_SHA256: 'SSL_RSA_WITH_RC4_128_SHA',
ANY_TLS12: '*TLS12',
TLS_AES_128_GCM_SHA256: 'TLS_AES_128_GCM_SHA256',
TLS_AES_256_GCM_SHA384: 'TLS_AES_256_GCM_SHA384',
TLS_CHACHA20_POLY1305_SHA256: 'TLS_CHACHA20_POLY1305_SHA256',
TLS_AES_128_CCM_SHA256: 'TLS_AES_128_CCM_SHA256',
TLS_AES_128_CCM_8_SHA256: 'TLS_AES_128_CCM_8_SHA256',
ANY: '*ANY',
ANY_TLS13: '*TLS13',
ANY_TLS12_OR_HIGHER: '*TLS12ORHIGHER',
ANY_TLS13_OR_HIGHER: '*TLS13ORHIGHER'
};

if (ciphers[cipher] === undefined) {
throw new Error('An invalid cipher spec was provided. Please see https://ibm.biz/mq-cipher-mappings');
}
if (ciphers[cipher] === undefined) {
throw new Error('An invalid cipher spec was provided. Please see https://ibm.biz/mq-cipher-mappings');
}

return ciphers[cipher];
}
return ciphers[cipher];
}
3 changes: 2 additions & 1 deletion components/Connection/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ const connectionModuleMap = [
];

export default function({ asyncapi, params }) {
const protocol = asyncapi.server(params.server).protocol();
const server = asyncapi.allServers().get(params.server);
const protocol = server.protocol();
const foundModule = connectionModuleMap.find(item => item.protocols.includes(protocol));
if (!foundModule) {
throw new Error(`This template does not currently support the protocol ${protocol}`);
Expand Down
42 changes: 22 additions & 20 deletions components/ConnectionHelper/KafkaConnectionHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,36 @@
* limitations under the License.
*/

function getSecurityProtocol(protocol, securitySchemeType) {
if (protocol === 'kafka') {
if (securitySchemeType) {
return 'SASL_PLAINTEXT';
}
return 'PLAINTEXT';
} else if (protocol === 'kafka-secure') {
if (securitySchemeType) {
return 'SASL_SSL';
}
return 'SSL';
}
}

function getSecurityConfig({ asyncapi, params }) {
const server = asyncapi.server(params.server);
const server = asyncapi.allServers().get(params.server);
const protocol = server.protocol();
const security = server.security();

let securitySchemeType;
if (security && security.length > 0 && asyncapi.hasComponents()) {
const securitySchemeName = Object.keys(security[0].json())[0];
const securityScheme = asyncapi.components().securityScheme(securitySchemeName);
if (securityScheme) {
securitySchemeType = securityScheme.json().type;
if (security && security.length > 0) {
const securityReq = security[0].all();
if (securityReq && securityReq.length > 0) {
securitySchemeType = securityReq[0].scheme().type();
}
}

let securityProtocol, saslMechanism, authModule;
if (protocol === 'kafka') {
if (securitySchemeType) {
securityProtocol = 'SASL_PLAINTEXT';
} else {
securityProtocol = 'PLAINTEXT';
}
} else if (protocol === 'kafka-secure') {
if (securitySchemeType) {
securityProtocol = 'SASL_SSL';
} else {
securityProtocol = 'SSL';
}
}
let securityProtocol = getSecurityProtocol(protocol, securitySchemeType);

let saslMechanism, authModule;
if (securitySchemeType) {
switch (securitySchemeType) {
case 'plain':
Expand Down
3 changes: 2 additions & 1 deletion components/ConnectionHelper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ const connectionModuleMap = [
];

export default function({ asyncapi, params }) {
const protocol = asyncapi.server(params.server).protocol();
const server = asyncapi.allServers().get(params.server);
const protocol = server.protocol();
const foundModule = connectionModuleMap.find(item => item.protocols.includes(protocol));
if (!foundModule) {
throw new Error(`This template does not currently support the protocol ${protocol}`);
Expand Down
6 changes: 4 additions & 2 deletions components/Consumer/KafkaConsumer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export function ConsumerDeclaration() {
}

export function ConsumerImports({ params, message }) {
const id = message.id() || message.name();
return `
import java.time.Duration;
import java.util.logging.*;
Expand All @@ -38,7 +39,7 @@ import ${params.package}.Connection;
import ${params.package}.PubSubBase;

import ${params.package}.models.ModelContract;
import ${params.package}.models.${toJavaClassName(message.uid())};
import ${params.package}.models.${toJavaClassName(id)};

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
Expand All @@ -47,6 +48,7 @@ import org.apache.kafka.clients.consumer.KafkaConsumer;
}

export function ReceiveMessage({ message }) {
const id = message.id() || message.name();
return `
public void receive(int requestTimeout) {
boolean continueProcessing = true;
Expand All @@ -59,7 +61,7 @@ export function ReceiveMessage({ message }) {
for (ConsumerRecord<String, String> record : records) {

logger.info("Received message: " + record.value());
${toJavaClassName(message.uid())} receivedObject = new ObjectMapper().readValue(record.value(), ${toJavaClassName(message.uid())}.class);
${toJavaClassName(id)} receivedObject = new ObjectMapper().readValue(record.value(), ${toJavaClassName(id)}.class);
logger.info("Received message type: " + receivedObject.getClass().getName());

/*
Expand Down
5 changes: 3 additions & 2 deletions components/Consumer/MQConsumer.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@ import ${params.package}.Connection;
import ${params.package}.PubSubBase;

import ${params.package}.models.ModelContract;
import ${params.package}.models.${toJavaClassName(message.uid())};
import ${params.package}.models.${toJavaClassName(message.id())};
`;
}

export function ReceiveMessage({ message }) {
const id = message.id() || message.name();
return `
public void receive(int requestTimeout) {
boolean continueProcessing = true;
Expand All @@ -73,7 +74,7 @@ export function ReceiveMessage({ message }) {
TextMessage textMessage = (TextMessage) receivedMessage;
try {
logger.info("Received message: " + textMessage.getText());
${toJavaClassName(message.uid())} receivedObject = new ObjectMapper().readValue(textMessage.getText(), ${toJavaClassName(message.uid())}.class);
${toJavaClassName(id)} receivedObject = new ObjectMapper().readValue(textMessage.getText(), ${toJavaClassName(id)}.class);
logger.info("Received message type: " + receivedObject.getClass().getName());

/*
Expand Down
3 changes: 2 additions & 1 deletion components/Consumer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ const consumerModuleMap = [
];

function getModule({ asyncapi, params }) {
const protocol = asyncapi.server(params.server).protocol();
const server = asyncapi.allServers().get(params.server);
const protocol = server.protocol();
const foundModule = consumerModuleMap.find(item => item.protocols.includes(protocol));
if (!foundModule) {
throw new Error(`This template does not currently support the protocol ${protocol}`);
Expand Down
23 changes: 12 additions & 11 deletions components/Demo/Demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,24 @@ import { createJavaConstructorArgs } from '../../utils/Types.utils';
import { PackageDeclaration } from '../Common';

export function Demo(asyncapi, params) {
const channels = Object.entries(asyncapi.channels()).map(([key, value]) => ({key,value}));

const foundPubAndSub = channels.filter((el) => {
return el.value.hasPublish() && el.value.hasSubscribe();
const foundPubAndSub = asyncapi.allChannels().filterBy((chan) => {
return chan.operations().filterBySend().length > 0 &&
chan.operations().filterByReceive().length > 0;
});

const foundPubOrSub = channels.filter((el) => {
return el.value.hasPublish() || el.value.hasSubscribe();
const foundPubOrSub = asyncapi.allChannels().filterBy((chan) => {
return chan.operations().filterBySend().length > 0 ||
chan.operations().filterByReceive().length > 0;
});

// Prioritise channel with both, fallback to an OR
const channel = foundPubAndSub.length ? foundPubAndSub[0] : foundPubOrSub[0];
const channelName = channel.key;
const channelName = channel.id();

// Get payload from either publish or subscribe
const targetMessageName = channel.value.hasPublish() ? channel.value.publish().message().uid() : channel.value.subscribe().message().uid();
const targetPayloadProperties = channel.value.hasPublish() ? channel.value.publish().message().payload().properties() : channel.value.subscribe().message().payload().properties();
const message = channel.messages().all()[0];
const targetMessageName = message.id() || message.name();
const targetPayloadProperties = message.payload().properties();

const messageNameTitleCase = toJavaClassName(targetMessageName);

Expand All @@ -49,15 +50,15 @@ export function Demo(asyncapi, params) {

const constructorArgs = createJavaConstructorArgs(targetPayloadProperties).join(', ');
const generatedClasses = [];
if (channel.value.hasPublish()) {
if (channel.operations().filterBySend().length > 0) {
generatedClasses.push(
<File name={producerPath}>
<PackageDeclaration path={params.package} />
<DemoProducer params={params} messageName={messageNameTitleCase} className={className} constructorArgs={constructorArgs}></DemoProducer>
</File>
);
}
if (channel.value.hasSubscribe()) {
if (channel.operations().filterByReceive().length > 0) {
generatedClasses.push(
<File name={subscriberPath}>
<PackageDeclaration path={params.package} />
Expand Down
14 changes: 7 additions & 7 deletions components/Files/Consumers.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,22 @@ import { ConsumerDeclaration, ConsumerImports, ConsumerConstructor, ReceiveMessa
import { toJavaClassName, javaPackageToPath } from '../../utils/String.utils';

export function Consumers(asyncapi, channels, params) {
return Object.entries(channels).map(([channelName, channel]) => {
const name = channelName;
const className = `${toJavaClassName(channelName)}Subscriber`;
return channels.map((channel) => {
if (channel.operations().filterByReceive().length > 0) {
const name = channel.id();
const className = `${toJavaClassName(name)}Subscriber`;

const packagePath = javaPackageToPath(params.package);
const packagePath = javaPackageToPath(params.package);

if (channel.subscribe()) {
const message = channel.subscribe().message();
const message = channel.messages().all()[0];

return (
<File name={`${packagePath}${className}.java`}>
<PackageDeclaration path={params.package}></PackageDeclaration>
<ConsumerImports asyncapi={asyncapi} params={params} message={message}></ConsumerImports>

<Class name={className} extendsClass="PubSubBase">
<ConsumerDeclaration asyncapi={asyncapi} params={params} name={channelName} />
<ConsumerDeclaration asyncapi={asyncapi} params={params} name={name} />

<ClassConstructor name={className}>
<ConsumerConstructor asyncapi={asyncapi} params={params} name={name}/>
Expand Down
2 changes: 1 addition & 1 deletion components/Files/Models.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function Models(asyncapi, params) {
<File name={`${packagePath}models/${messageNameUpperCase}.java`}>
<PackageDeclaration path={`${params.package}.models`} />
<ImportDeclaration path={`${params.package}.models.ModelContract`} />
<ImportDeclaration path={`java.util.UUID`} />
<ImportDeclaration path={'java.util.UUID'} />

<Class name={messageNameUpperCase} extendsClass="ModelContract">
<Indent size={2} type={IndentationTypes.SPACES}>
Expand Down
10 changes: 5 additions & 5 deletions components/Files/Producers.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import { ProducerConstructor, SendMessage, ProducerImports, ProducerDeclaration,
import { toJavaClassName, javaPackageToPath } from '../../utils/String.utils';

export function Producers(asyncapi, channels, params) {
return Object.entries(channels).map(([channelName, channel]) => {
const name = channelName;
const className = `${toJavaClassName(channelName)}Producer`;
const packagePath = javaPackageToPath(params.package);
return channels.map((channel) => {
if (channel.operations().filterBySend().length > 0) {
const name = channel.id();
const className = `${toJavaClassName(name)}Producer`;
const packagePath = javaPackageToPath(params.package);

if (channel.publish()) {
return (
<File name={`${packagePath}${className}.java`}>

Expand Down
Loading
Loading