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!: enable v3 support #293

Closed
wants to merge 8 commits into from
Closed
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
14 changes: 4 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
- [Specification requirements](#specification-requirements)
- [Supported protocols](#supported-protocols)
- [How to use the template](#how-to-use-the-template)
* [CLI](#cli)
* [Adding custom code / handlers](#adding-custom-code--handlers)
- [CLI](#cli)
- [Adding custom code / handlers](#adding-custom-code--handlers)
- [Template configuration](#template-configuration)
- [Development](#development)
- [Contributors](#contributors)
Expand Down Expand Up @@ -80,15 +80,9 @@ $ cd output
# Build generated application
$ npm i

# Start server
# To enable production settings start the server with "NODE_ENV=production npm start"
$ npm start
# Import the library and add your custom code and handlers

##
## Start the client
##

#for testing your server you can use mqtt client. open a new terminal and install it using:
# for testing your server you can use mqtt client. open a new terminal and install it using:
$ npm install mqtt -g

#publish an invalid message.
Expand Down
8 changes: 3 additions & 5 deletions helpers/channels-topics.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,8 @@ export function toHermesTopic(str) {
return str.replace(/\{([^}]+)\}/g, ':$1');
}

export function channelNamesWithPublish(asyncapi) {
const result = [];
asyncapi.channelNames().forEach((name) => {
if (asyncapi.channel(name).hasPublish()) result.push(name);
});
export function channelNamesWithReceive(asyncapi) {
const result = asyncapi.channels().filterByReceive().map(channel => channel.id());
return result;
}

Expand All @@ -69,6 +66,7 @@ export function port(url, defaultPort) {
}

export function stripProtocol(url) {
console.log(url);
if (!url.includes('://')) {
return url;
}
Expand Down
10 changes: 5 additions & 5 deletions hooks/beautify-output.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ module.exports = {
generator.targetDir,
'src/api/index.js'
);
const handlersPath = path.resolve(generator.targetDir, 'src/api/handlers');
const routesPath = path.resolve(generator.targetDir, 'src/api/routes');
// const handlersPath = path.resolve(generator.targetDir, 'src/api/handlers');
// const routesPath = path.resolve(generator.targetDir, 'src/api/routes');

beautifyAllOutputFiles(handlersPath);
beautifyAllOutputFiles(routesPath);
beautifySingleFile(entryPointFilePath);
// beautifyAllOutputFiles(handlersPath);
// beautifyAllOutputFiles(routesPath);
// beautifySingleFile(entryPointFilePath);
},
};
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,22 @@
"@asyncapi/generator-hooks": "^0.1.0",
"@asyncapi/generator-react-sdk": "^1.0.14",
"eslint-plugin-react": "^7.34.1",
"filenamify": "^4.1.0",
"filenamify": "^4.3.0",
"js-beautify": "^1.15.1",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"markdown-toc": "^1.2.0"
},
"devDependencies": {
"@asyncapi/generator": "^1.17.13",
"eslint": "^8.7.0",
"@asyncapi/generator": "^1.17.12",
"eslint": "^8.57.0",
"eslint-plugin-jest": "^25.7.0",
"eslint-plugin-sonarjs": "^0.11.0",
"jest": "^27.3.1",
"node-fetch": "^2.6.1",
"rimraf": "^5.0.1"
"jest": "^27.5.1",
"node-fetch": "^2.7.0",
"rimraf": "^5.0.5"
},
"generator": {
"apiVersion": "v3",
"supportedProtocols": [
"amqp",
"mqtt",
Expand Down
124 changes: 113 additions & 11 deletions template/README.md.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,129 @@
import { File } from '@asyncapi/generator-react-sdk';

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

let hasSecuritySchemeX509 = false;
let securitySchemeType;
if (params.securityScheme && security && security.length > 0) {
const securityReq = security[0].all();
if (securityReq && securityReq.length > 0) {
securitySchemeType = securityReq[0].scheme().type();
}
}

hasSecuritySchemeX509 = (params.securityScheme && (protocol === 'kafka' || protocol === 'kafka-secure') && securitySchemeType === 'X509');

return <File name={'README.md'}>
{`# ${ asyncapi.info().title() }

${ asyncapi.info().description() || '' }

## Running the server
## Set up your template

1. Install dependencies
\`\`\`sh
npm i
\`\`\`
${(params.securityScheme && (asyncapi.server(params.server).protocol() === 'kafka' || asyncapi.server(params.server).protocol() === 'kafka-secure') && asyncapi.components().securityScheme(params.securityScheme).type() === 'X509') ? '1. (Optional) For X509 security provide files with all data required to establish secure connection using certificates. Place files like `ca.pem`, `service.cert`, `service.key` in the root of the project or the location that you explicitly specified during generation.' : ''}
1. Start the server with default configuration
\`\`\`sh
npm start
\`\`\`
1. (Optional) Start server with secure production configuration
\`\`\`sh
NODE_ENV=production npm start
\`\`\`
${hasSecuritySchemeX509 ? '1. (Optional) For X509 security provide files with all data required to establish secure connection using certificates. Place files like `ca.pem`, `service.cert`, `service.key` in the root of the project or the location that you explicitly specified during generation.' : ''}

## Use the generated template by adding custom code / handlers

- use the \`client.register<OperationId>Middleware\` method as a bridge between the user-written handlers and the generated code. This can be used to register middlewares for specific methods on specific channels.

> The AsyncAPI file used for the example is [here](https://bit.ly/asyncapi)

\`\`\`js
// output refers to the generated template folder
// You require the generated server. Running this code starts the server
// App exposes API to send messages
const { client } = require("./output");

// to start the app
client.init();

// Generated handlers that we use to react on consumer / produced messages are attached to the client
// through which we can register middleware functions

/**
*
*
* Example of how to process a message before it is sent to the broker
*
*
*/
function testPublish() {
// mosquitto_sub -h test.mosquitto.org -p 1883 -t "smartylighting/streetlights/1/0/action/12/turn/on"

// Registering your custom logic in a channel-specific handler
// the passed handler function is called once the app sends a message to the channel
// For example \`client.app.send\` sends a message to some channel using and before it is sent, you want to perform some other actions
// in such a case, you can register middlewares like below
client.registerTurnOnMiddleware((message) => { // \`turnOn\` is the respective operationId
console.log("hitting the middleware before publishing the message");
console.log(
\`sending turn on message to streetlight \${message.params.streetlightId}\`,
message.payload
);
});

client.app.send(
{ command: "off" },
{},
"smartylighting/streetlights/1/0/action/12/turn/on"
);
}


/**
*
*
* Example of how to work with generated code as a consumer
*
*
*/
function testSubscribe() {
// mosquitto_pub -h test.mosquitto.org -p 1883 -t "smartylighting/streetlights/1/0/event/101/lighting/measured" -m '{"lumens": 10}'

// Writing your custom logic that should be triggered when your app receives as message from a given channel
// Registering your custom logic in a channel-specific handler
// the passed handler functions are called once the app gets message sent to the channel

client.registerReceiveLightMeasurementMiddleware((message) => { // \`recieveLightMeasurement\` is the respective operationId
console.log("recieved in middleware 1", message.payload);
});

client.registerReceiveLightMeasurementMiddleware((message) => {
console.log("recieved in middleware 2", message.payload);
});
}

testPublish();
testSubscribe();

/**
*
*
* Example of how to produce a message using API of generated app independently from the handlers
*
*
*/

(function myLoop (i) {
setTimeout(() => {
console.log('producing custom message');
client.app.send({percentage: 1}, {}, 'smartylighting/streetlights/1/0/action/1/turn/on');
if (--i) myLoop(i);
}, 1000);
}(3));
\`\`\`

> NODE_ENV=production relates to \`config/common.yml\` that contains different configurations for different environments. Starting server without \`NODE_ENV\` applies default configuration while starting the server as \`NODE_ENV=production npm start\` applies default configuration supplemented by configuration settings called \`production\`.`}
You can run the above code and test the working of the handlers by sending a message using the mqtt cli / mosquitto broker software to the \`smartylighting/streetlights/1/0/event/123/lighting/measured\` channel using this command
\`mosquitto_pub -h test.mosquitto.org -p 1883 -t "smartylighting/streetlights/1/0/event/101/lighting/measured" -m '{"lumens": 10, "sentAt": "2017-06-07T12:34:32.000Z"}'\`
or
\`mqtt pub -t 'smartylighting/streetlights/1/0/event/123/lighting/measured' -h 'test.mosquitto.org' -m '{"id": 1, "lumens": 3, }'\` (if you are using the mqtt cli)
`}
</File>;
}
20 changes: 11 additions & 9 deletions template/config/common.yml.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { File } from '@asyncapi/generator-react-sdk';
import { camelCase, channelNamesWithPublish, dump, host, port, queueName, stripProtocol, toAmqpTopic, toKafkaTopic, toMqttTopic } from '../../helpers/index';
import { camelCase, channelNamesWithReceive, dump, host, port, queueName, stripProtocol, toAmqpTopic, toKafkaTopic, toMqttTopic } from '../../helpers/index';
import { replaceServerVariablesWithValues } from '@asyncapi/generator-filters/src/customFilters';

export default function CommonConfigYAMLRender({ asyncapi, params }) {
const serverProtocol = asyncapi.server(params.server).protocol();
const serverVariables = asyncapi.server(params.server).variables();
const resolvedBrokerUrlWithReplacedVariables = replaceServerVariablesWithValues(asyncapi.server(params.server).url(), serverVariables);
const server = asyncapi.allServers().get(params.server);
const serverProtocol = server.protocol();
const serverVariables = server.variables();
const resolvedBrokerUrlWithReplacedVariables = replaceServerVariablesWithValues(server.url(), serverVariables);

return (
<File name={'common.yml'}>
Expand Down Expand Up @@ -47,7 +48,7 @@ function amqpBlock(url, asyncapi) {
password:
host: ${host(url)}
port:
topics: ${dump(toAmqpTopic(channelNamesWithPublish(asyncapi)))}
topics: ${dump(toAmqpTopic(channelNamesWithReceive(asyncapi)))}
queue: ${queueName(asyncapi.info().title(), asyncapi.info().version())}
queueOptions:
exclusive: false
Expand All @@ -57,9 +58,10 @@ function amqpBlock(url, asyncapi) {
}

function mqttBlock(url, asyncapi, params) {
const server = asyncapi.allServers().get(params.server);
return ` mqtt:
url: ${asyncapi.server(params.server).protocol()}://${stripProtocol(url)}
topics: ${dump(toMqttTopic(channelNamesWithPublish(asyncapi)))}
url: ${server.protocol()}://${stripProtocol(url)}
topics: ${dump(toMqttTopic(channelNamesWithReceive(asyncapi)))}
qos:
protocol: mqtt
retain:
Expand All @@ -75,7 +77,7 @@ function kafkaBlock(url, asyncapi) {
consumerOptions:
groupId: ${camelCase(asyncapi.info().title())}
topics:
${channelNamesWithPublish(asyncapi).map(topic => `- ${toKafkaTopic(topic)}`).join('\n')}
${channelNamesWithReceive(asyncapi).map(topic => `- ${toKafkaTopic(topic)}`).join('\n')}
topicSeparator: '__'
topicPrefix:
`;
Expand All @@ -87,7 +89,7 @@ function kafkaProductionBlock(params, asyncapi) {
ssl:
rejectUnauthorized: true
`;
if (params.securityScheme && asyncapi.components().securityScheme(params.securityScheme).type() !== 'X509') {
if (params.securityScheme && asyncapi.components().securitySchemes().get(params.securityScheme).type() !== 'X509') {
productionBlock += ` sasl:
mechanism: 'plain'
username:
Expand Down
6 changes: 3 additions & 3 deletions template/package.json.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function packageFile({ asyncapi, params }) {
'node-yaml-config': '0.0.4',
};

const serverProtocol = asyncapi.server(params.server).protocol();
const serverProtocol = asyncapi.allServers().get(params.server).protocol();
if (serverProtocol === 'mqtt' || serverProtocol === 'mqtts') {
dependencies['hermesjs-mqtt'] = '2.x';
} else if (serverProtocol === 'kafka' || serverProtocol === 'kafka-secure') {
Expand All @@ -37,10 +37,10 @@ export default function packageFile({ asyncapi, params }) {

packageJSON = {
...packageJSON,
main: './src/api',
scripts: {
start: 'node src/api/index.js',
test: 'node custom-handler-example/script.js'
},
main: './src/api',
dependencies,
};

Expand Down
Loading
Loading