diff --git a/docs/pages/env-vars-config.md b/docs/pages/env-vars-config.md
index 9c8590e28..bb6d30d88 100644
--- a/docs/pages/env-vars-config.md
+++ b/docs/pages/env-vars-config.md
@@ -13,8 +13,22 @@ Glee provides several environment variables that allow you to tailor your applic
|GLEE_SERVER_CERTS|Indicates server-specific certificate files in a `${serverName}:${pathToCertificateFile}` format, separated by commas.|`GLEE_SERVER_CERTS=mosquitto:mosquitto.org.crt`|
|GLEE_SERVER_VARIABLES|Sets server variables in a `${serverName}:${serverVariable}:${value}` format, separated by commas.|`GLEE_SERVER_VARIABLES=websockets:namespace:public`|
+<<<<<<< HEAD
### Handling Multiple .env Files
Glee supports loading variables from `.env.local` directly into `process.env`. This feature is handy for keeping secrets out of your repository during development. You can also set environment-specific defaults in `.env.development` or `.env.production`.
+=======
+### Support for multiple .env files
+Glee has support for loading variables from `.env.local` into `process.env`.
+This is useful for storing secret environment variables needed in development while keeping them out of the repository.
+However, sometimes you might want to add some defaults for the `development` or `production` environment. You can do that by creating files with the following names:
+`.env.development` or `.env.production`
+
+`.env.local` always overrides any other existing `.env*` file.
+
+You can change the environment by setting the `NODE_ENV` variable to `development` or `production`.
+
+## Configuring Glee
+>>>>>>> 1f51e1f (feat: add support for multiple .env files. (#683))
`.env.local` takes precedence over other `.env*` files.
diff --git a/docs/pages/function-lifecycle-events.md b/docs/pages/function-lifecycle-events.md
index 3d10988d0..09b560d24 100644
--- a/docs/pages/function-lifecycle-events.md
+++ b/docs/pages/function-lifecycle-events.md
@@ -22,13 +22,13 @@ Functions take a single argument, which is the event received from a broker or a
|channel|The name of the channel/topic from which the event was read.|
|serverName|The name of the server/broker from which the event was received.|
-Functions may return an object to instruct Glee on what action to take next. For instance, the following example sends a greeting message to the `development` server:
+Functions may return an object to tell Glee what to do next. For instance, the following example sends a greeting message to `development` server:
```js
/* onHello.js */
export default async function (event) {
return {
send: [{
- server: 'development',
+ server: 'developement',
channel: 'greets',
payload: 'Greetings! How is your day going?'
}]
@@ -37,10 +37,10 @@ export default async function (event) {
```
|Attribute|Type|Description|
-|---------|----|-----------|
-|send|array<[OutboundMessage](#anatomy-of-an-outbound-message)>|A list of outbound messages to send after processing the inbound event. All clients subscribed to the given channel/topic will receive the message.
+|---|---|---|
+|send|array<[OutboundMessage](#anatomy-of-an-outbound-message)>|A list of outbound messages to send when the processing of the inbound event has finished. All clients subscribed to the given channel/topic will receive the message.
-##### Anatomy of an Outbound Message
+##### Anatomy of an outbound message
|Attribute|Type|Description|
|---------|----|-----------|
|payload|string|The payload/body of the message you want to send.|
diff --git a/docs/pages/glee-template.md b/docs/pages/glee-template.md
new file mode 100644
index 000000000..8fea93cfc
--- /dev/null
+++ b/docs/pages/glee-template.md
@@ -0,0 +1,98 @@
+---
+title: "Create AsyncAPI Glee template"
+weight: 30
+---
+This tutorial teaches you how to create a simple glee template. You'll use the AsyncAPI Glee template that you develop to generate Javascript code. Additionally, you'll create a template code with a reusable component to reuse the custom functionality you create and test your code using an WS server.
+
+
+{`asyncapi: 3.0.0
+info:
+ title: 'Hello, Glee!'
+ version: 1.0.0
+servers:
+ websockets:
+ host: 0.0.0.0:3000
+ protocol: ws
+channels:
+ hello:
+ address: hello
+ messages:
+ hello:
+ $ref: '#/components/messages/hello'
+operations:
+ onHello:
+ action: receive
+ channel:
+ $ref: '#/channels/hello'
+ SendHello:
+ action: send
+ channel:
+ $ref: "#/channels/hello"
+components:
+ messages:
+ hello:
+ payload:
+ type: string`}
+
+
+Let's break it down into pieces:
+
+
+{`info:
+ title: 'Hello, Glee!'
+ version: 1.0.0`}
+
+
+The `info` section provides general information about the API, including its title and version.
+
+Moving on, let's talk about the `servers` section.
+
+
+{`servers:
+ websockets:
+ host: 0.0.0.0:3000
+ protocol: ws`}
+
+
+The servers section defines the different servers where the API can be accessed. In this case, there is a single server named "websockets" that uses the WebSocket protocol (`ws`) and listens on the address `ws://0.0.0.0:3000`.
+
+Now lets move on to the `channels` section. This section is used to describe the event names your API will be publishing and/or subscribing to.
+
+
+{`channels:
+ hello:
+ address: hello
+ messages:
+ hello:
+ $ref: '#/components/messages/hello'
+operations:
+ onHello:
+ action: receive
+ channel:
+ $ref: '#/channels/hello'
+ sendHello:
+ action: send
+ channel:
+ $ref: '#/channels/hello'`}
+
+
+The channels section defines the communication channels available in the API. In this case, there's a channel named "hello". This channel supports both sending and receiving.
+
+The `receive` action indicates that messages received on the `hello` channel should follow the structure defined in the hello message component.
+The `send` action specifies that the operation with ID `sendHello` is used for sending messages to the `hello` channel. The message structure is referenced from the hello message component. The message payload is going to be validated against the `sendHello` operation message schemas.
+
+Next is the `payload` property under `hello` message component which is used to understand how the event should look like when publishing to that channel:
+
+
+{`components:
+ messages:
+ hello:
+ payload:
+ type: string`}
+
+
+The components section contains reusable elements, in this case, a definition for the "hello" message. It specifies that the payload of the "hello" message should be of type string.
+
+## Summary
+
+In this tutorial, you learned how to create an AsyncAPI specification document via a simple example with a glee template.
diff --git a/docs/pages/index.md b/docs/pages/index.md
index 0f401af13..6437a4079 100644
--- a/docs/pages/index.md
+++ b/docs/pages/index.md
@@ -40,3 +40,115 @@ Glee expects your project to have some files and folders with special names. Whe
|package.json|**Required.** The Node.js package definition file. Make sure you include `@asyncapi/glee` as a dependency and add two scripts: `dev` and `start`. They should be running `glee dev` and `glee start` respectively.
To understand the structure in a broader way, please refer to the associated page's links.
+
+### Let's create a glee project to simplify the app structure
+
+We will consider a simple WebSocket API using glee to understand its magic. We will create a simple WebSocket server that receives a current time from the client and then send a "good morning", "good evening" or "good night" respectively.
+
+To setup a project, you should follow our installation page on how to setup glee on your environment.
+
+We recommend creating a new Glee app using our official CLI which sets up everything automatically. (You don't need to create an empty directory. create-glee-app will make one for you.) To create a project, run: `asyncapi new glee`
+
+Once the process is completed, you should have a new Glee app ready for development and find the files that were made.
+
+#### Define our Spec for our API
+
+Glee being a spec-first framework, development starts with defining your API spec. To know more details into it, you can follow glee template to understand it step by step. For our case we will define our API:
+
+```yaml
+asyncapi: 3.0.0
+info:
+ title: Greet Bot
+ version: 1.0.0
+servers:
+ websockets:
+ host: 0.0.0.0:3000
+ protocol: ws
+channels:
+ greet:
+ address: greet
+ messages:
+ greet:
+ $ref: '#/components/messages/greet'
+ time:
+ $ref: '#/components/messages/time'
+ time:
+ address: time
+ messages:
+ time:
+ $ref: '#/components/messages/time'
+operations:
+ onGreet:
+ action: receive
+ channel:
+ $ref: '#/channels/greet'
+ sendGreet:
+ action: send
+ channel:
+ $ref: '#/channels/time'
+components:
+ messages:
+ time:
+ payload:
+ type: object
+ properties:
+ currentTime:
+ type: number
+ name:
+ type: string
+ greet:
+ payload:
+ type: string
+
+```
+
+This will be the Specification that defines our API, in our case, it is very simple, as we will be sending a name and the time of the day, and our API will greet us accordingly.
+
+One thing to note here is the `operations` item, this is needed and is a crucial part of glee, as this is how we will be connecting our business logic with our spec, `onGreet` is the name of the function that will be called every time a certain operation occurs. In our case whenever `/greet` channel receives a message, `onGreet` function is called.
+
+#### Define our operation function
+
+Now for our case, we will be adding a file `functions/onGreet.js` and writing up the logic for parsing our time and sending a response.
+
+```javascript
+export default async function (event) {
+ const { name, time } = event.payload
+ const t = new Date(time)
+ const curHr = t.getHours()
+ let response = ''
+ if (curHr < 12) {
+ response = `Good Morning ${name}`
+ } else if (curHr < 18) {
+ response = `Good Afternoon ${name}`
+ } else {
+ response = `Good Evening ${name}`
+ }
+ return {
+ send: [
+ {
+ server: "websockets",
+ channel: "greet"
+ payload: response,
+ },
+ ],
+ }
+}
+
+```
+
+Every file in the functions folder acts as a handler to develop business logic for glee, every file should export an async function that receives an event parameter, where you have access to payload and server details.
+
+#### Running and testing our application
+
+We will execute the application and carry out testing with Postman to ensure that it is functioning as intended.
+
+Now to execute our glee application, just run:
+
+```
+npm run dev
+# or
+npm run start
+```
+To send a WebSocket request with a payload e.g. `{"name":"john", "time": "1567906535"}` to `ws://localhost:3000/greet`, open Postman and checkout the endpoint:
+
+So, this is how easy it is to build a WebSocket API using Glee. You can also check out the example code [here](https://github.com/Souvikns/greet-bot).
diff --git a/docs/reference/classes/adapters_cluster_redis.default.md b/docs/reference/classes/adapters_cluster_redis.default.md
index 6ef867974..a3ef3c0f9 100644
--- a/docs/reference/classes/adapters_cluster_redis.default.md
+++ b/docs/reference/classes/adapters_cluster_redis.default.md
@@ -1429,4 +1429,4 @@ v15.4.0
#### Defined in
-node_modules/@types/node/events.d.ts:317
+node_modules/@types/node/events.d.ts:317
\ No newline at end of file
diff --git a/docs/reference/classes/adapters_http_client.default.md b/docs/reference/classes/adapters_http_client.default.md
index 974ccf020..ba00d2420 100644
--- a/docs/reference/classes/adapters_http_client.default.md
+++ b/docs/reference/classes/adapters_http_client.default.md
@@ -1650,4 +1650,4 @@ v15.4.0
#### Defined in
-node_modules/@types/node/events.d.ts:317
+node_modules/@types/node/events.d.ts:317
\ No newline at end of file
diff --git a/docs/reference/classes/adapters_kafka.default.md b/docs/reference/classes/adapters_kafka.default.md
index 48c64626c..345b03af1 100644
--- a/docs/reference/classes/adapters_kafka.default.md
+++ b/docs/reference/classes/adapters_kafka.default.md
@@ -1537,4 +1537,4 @@ v15.4.0
#### Defined in
-node_modules/@types/node/events.d.ts:317
+node_modules/@types/node/events.d.ts:317
\ No newline at end of file
diff --git a/docs/reference/classes/adapters_mqtt.default.md b/docs/reference/classes/adapters_mqtt.default.md
index dc2b10985..925aaf0a0 100644
--- a/docs/reference/classes/adapters_mqtt.default.md
+++ b/docs/reference/classes/adapters_mqtt.default.md
@@ -1678,4 +1678,4 @@ v15.4.0
#### Defined in
-node_modules/@types/node/events.d.ts:317
+node_modules/@types/node/events.d.ts:317
\ No newline at end of file
diff --git a/docs/reference/classes/adapters_ws_client.default.md b/docs/reference/classes/adapters_ws_client.default.md
index 138c374c3..2896c12b2 100644
--- a/docs/reference/classes/adapters_ws_client.default.md
+++ b/docs/reference/classes/adapters_ws_client.default.md
@@ -1561,4 +1561,4 @@ v15.4.0
#### Defined in
-node_modules/@types/node/events.d.ts:317
+node_modules/@types/node/events.d.ts:317
\ No newline at end of file
diff --git a/docs/reference/classes/adapters_ws_server.default.md b/docs/reference/classes/adapters_ws_server.default.md
index b798b1bc0..084d91c4c 100644
--- a/docs/reference/classes/adapters_ws_server.default.md
+++ b/docs/reference/classes/adapters_ws_server.default.md
@@ -1998,4 +1998,4 @@ v15.4.0
#### Defined in
-node_modules/@types/node/events.d.ts:317
+node_modules/@types/node/events.d.ts:317
\ No newline at end of file
diff --git a/docs/reference/classes/errors_glee_error.default.md b/docs/reference/classes/errors_glee_error.default.md
index 473ce41a5..92d27a9b4 100644
--- a/docs/reference/classes/errors_glee_error.default.md
+++ b/docs/reference/classes/errors_glee_error.default.md
@@ -219,4 +219,4 @@ Error.captureStackTrace
#### Defined in
-node_modules/@types/node/globals.d.ts:4
+node_modules/@types/node/globals.d.ts:4
\ No newline at end of file
diff --git a/docs/reference/classes/lib_adapter.default.md b/docs/reference/classes/lib_adapter.default.md
index d713b8677..cc1903832 100644
--- a/docs/reference/classes/lib_adapter.default.md
+++ b/docs/reference/classes/lib_adapter.default.md
@@ -1519,4 +1519,4 @@ EventEmitter.setMaxListeners
#### Defined in
-node_modules/@types/node/events.d.ts:317
+node_modules/@types/node/events.d.ts:317
\ No newline at end of file
diff --git a/docs/reference/classes/lib_cluster.default.md b/docs/reference/classes/lib_cluster.default.md
index 32c386661..01d387eb1 100644
--- a/docs/reference/classes/lib_cluster.default.md
+++ b/docs/reference/classes/lib_cluster.default.md
@@ -1370,4 +1370,4 @@ EventEmitter.setMaxListeners
#### Defined in
-node_modules/@types/node/events.d.ts:317
+node_modules/@types/node/events.d.ts:317
\ No newline at end of file
diff --git a/docs/reference/classes/lib_glee.default.md b/docs/reference/classes/lib_glee.default.md
index fd72eb9b0..9e566d2b1 100644
--- a/docs/reference/classes/lib_glee.default.md
+++ b/docs/reference/classes/lib_glee.default.md
@@ -1596,4 +1596,4 @@ EventEmitter.setMaxListeners
#### Defined in
-node_modules/@types/node/events.d.ts:317
+node_modules/@types/node/events.d.ts:317
\ No newline at end of file
diff --git a/docs/reference/classes/lib_message.default.md b/docs/reference/classes/lib_message.default.md
index 711444981..bae1e7b05 100644
--- a/docs/reference/classes/lib_message.default.md
+++ b/docs/reference/classes/lib_message.default.md
@@ -1784,4 +1784,4 @@ EventEmitter.setMaxListeners
#### Defined in
-node_modules/@types/node/events.d.ts:317
+node_modules/@types/node/events.d.ts:317
\ No newline at end of file
diff --git a/docs/reference/classes/lib_wsHttpAuth.default.md b/docs/reference/classes/lib_wsHttpAuth.default.md
index 3a7179f69..e7fb89879 100644
--- a/docs/reference/classes/lib_wsHttpAuth.default.md
+++ b/docs/reference/classes/lib_wsHttpAuth.default.md
@@ -1407,4 +1407,4 @@ EventEmitter.setMaxListeners
#### Defined in
-node_modules/@types/node/events.d.ts:317
+node_modules/@types/node/events.d.ts:317
\ No newline at end of file
diff --git a/examples/kafka-test/docs/asyncapi.md b/examples/kafka-test/docs/asyncapi.md
new file mode 100644
index 000000000..f7d02139a
--- /dev/null
+++ b/examples/kafka-test/docs/asyncapi.md
@@ -0,0 +1,99 @@
+# Kafka test 1 documentation
+
+
+## Table of Contents
+
+* [Servers](#servers)
+ * [mykafka](#mykafka-server)
+
+## Servers
+
+### `mykafka` Server
+
+* URL: `kafka-secure://kafka://pkc-6ojv2.us-west4.gcp.confluent.cloud:9092/`
+* Protocol: `kafka-secure`
+
+
+#### Security
+
+##### Security Requirement 1
+
+* Type: `ScramSha256`
+ * security.protocol: SASL_SSL
+ * sasl.mechanism: SCRAM-SHA-256
+
+
+
+
+##### Security Requirement 2
+
+* Type: `ScramSha256`
+ * security.protocol: SASL_SSL
+ * sasl.mechanism: SCRAM-SHA-256
+
+
+
+
+
+
+
+## Operations
+
+### REQUEST `undefined` Operation
+
+*Publish messages to Test channel.*
+
+* Operation ID: `onTest`
+
+#### Message `testMessage`
+
+##### Payload
+
+| Name | Type | Description | Value | Constraints | Notes |
+|---|---|---|---|---|---|
+| (root) | object | - | - | - | **additional properties are allowed** |
+| test | string | - | - | - | - |
+
+> Examples of payload _(generated)_
+
+```json
+{
+ "test": "string"
+}
+```
+
+
+#### Request information
+
+* should be done to channel: ``
+#### Operation reply address information
+
+* Operation reply address location: `$message.header#/replyTo`
+
+
+
+### RECEIVE `undefined` Operation
+
+*Recieve messages from Produce channel.*
+
+* Operation ID: `onProduce`
+
+#### Message `produceMessage`
+
+##### Payload
+
+| Name | Type | Description | Value | Constraints | Notes |
+|---|---|---|---|---|---|
+| (root) | object | - | - | - | **additional properties are allowed** |
+| test | string | - | - | - | - |
+
+> Examples of payload _(generated)_
+
+```json
+{
+ "test": "string"
+}
+```
+
+
+
diff --git a/src/adapters/kafka/index.ts b/src/adapters/kafka/index.ts
index 10f86f76d..426ca9d4b 100644
--- a/src/adapters/kafka/index.ts
+++ b/src/adapters/kafka/index.ts
@@ -3,6 +3,12 @@ import Adapter from '../../lib/adapter.js'
import GleeMessage from '../../lib/message.js'
import { KafkaAdapterConfig, KafkaAuthConfig } from '../../lib/index.js'
+enum SECURITY_TYPES {
+ USER_PASSWORD = 'userPassword',
+ SCRAM_SHA_256 = 'scramSha256',
+ SCRAM_SHA_512 = 'scramSha512',
+}
+
class KafkaAdapter extends Adapter {
private kafka: Kafka
private firstConnect = true
@@ -15,25 +21,23 @@ class KafkaAdapter extends Adapter {
}
async _connect() {
- const kafkaOptions: KafkaAdapterConfig = await this.resolveProtocolConfig(
- 'kafka'
- )
- const auth: KafkaAuthConfig = await this.getAuthConfig(kafkaOptions?.auth)
- const securityRequirements = this.AsyncAPIServer.security().map(
- (sec) => {
- const secName = Object.keys(sec.values())[0]
- return this.parsedAsyncAPI.components().securitySchemes().get(secName)
- }
- )
- const userAndPasswordSecurityReq = securityRequirements.find(
- (sec) => sec.type() === 'userPassword'
- )
- const scramSha256SecurityReq = securityRequirements.find(
- (sec) => sec.type() === 'scramSha256'
- )
- const scramSha512SecurityReq = securityRequirements.find(
- (sec) => sec.type() === 'scramSha512'
- )
+ const kafkaOptions: KafkaAdapterConfig = await this.resolveProtocolConfig(
+ 'kafka'
+ )
+ const auth: KafkaAuthConfig = await this.getAuthConfig(kafkaOptions?.auth)
+ const securityRequirements = this.AsyncAPIServer.security().map((sec) => {
+ const secName = Object.keys(sec.values())[0]
+ return this.parsedAsyncAPI.components().securitySchemes().get(secName)
+ })
+ const userAndPasswordSecurityReq = securityRequirements.find(
+ (sec) => sec.type() === SECURITY_TYPES.USER_PASSWORD
+ )
+ const scramSha256SecurityReq = securityRequirements.find(
+ (sec) => sec.type() === SECURITY_TYPES.SCRAM_SHA_256
+ )
+ const scramSha512SecurityReq = securityRequirements.find(
+ (sec) => sec.type() === SECURITY_TYPES.SCRAM_SHA_512
+ )
const brokerUrl = new URL(this.AsyncAPIServer.url())
this.kafka = new Kafka({
diff --git a/src/index.ts b/src/index.ts
index 80db5bbcc..68db9a014 100755
--- a/src/index.ts
+++ b/src/index.ts
@@ -34,7 +34,6 @@ import { getMessagesSchema } from './lib/util.js'
import pkg from '@next/env'
const { loadEnvConfig } = pkg
-
const isDev = process.env.NODE_ENV === 'development'
loadEnvConfig(process.cwd(), isDev)
@@ -100,31 +99,45 @@ export default async function GleeAppInitializer() {
app.use(errorLogger)
app.useOutbound(errorLogger)
await generateDocs(config)
- parsedAsyncAPI.operations().filterByReceive().forEach(operation => {
- const channel = operation.channels()[0] // operation can have only one channel.
- if (operation.reply()) {
- logWarningMessage(`Operation ${operation.id()} has a reply defined. Glee does not support replies yet.`)
- }
- const schema = getMessagesSchema(operation)
- if (schema.oneOf.length > 0) app.use(channel.id(), validate(schema))
- app.use(channel.id(), (event, next) => {
- triggerFunction({
- app,
- operation,
- message: event
- }).then(next).catch(next)
+ parsedAsyncAPI
+ .operations()
+ .filterByReceive()
+ .forEach((operation) => {
+ const channel = operation.channels()[0] // operation can have only one channel.
+ if (operation.reply()) {
+ logWarningMessage(
+ `Operation ${operation.id()} has a reply defined. Glee does not support replies yet.`
+ )
+ }
+ const schema = getMessagesSchema(operation)
+ if (schema.oneOf.length > 0) app.use(channel.id(), validate(schema))
+ app.use(channel.id(), (event, next) => {
+ triggerFunction({
+ app,
+ operation,
+ message: event,
+ })
+ .then(next)
+ .catch(next)
+ })
})
- })
- parsedAsyncAPI.operations().filterBySend().forEach(operation => {
- const channel = operation.channels()[0] // operation can have only one channel.
- if (operation.reply()) {
- logWarningMessage(`Operation ${operation.id()} has a reply defined. Glee does not support replies yet.`)
- }
- const schema = getMessagesSchema(operation)
- if (schema.oneOf.length > 0) app.useOutbound(channel.id(), validate(schema))
- app.useOutbound(channel.id(), json2string)
- })
+ parsedAsyncAPI
+ .operations()
+ .filterBySend()
+ .forEach((operation) => {
+ const channel = operation.channels()[0] // operation can have only one channel.
+ if (operation.reply()) {
+ logWarningMessage(
+ `Operation ${operation.id()} has a reply defined. Glee does not support replies yet.`
+ )
+ }
+ const schema = getMessagesSchema(operation)
+ if (schema.oneOf.length > 0) {
+ app.useOutbound(channel.id(), validate(schema))
+ }
+ app.useOutbound(channel.id(), json2string)
+ })
app.on('adapter:auth', async (e: AuthEvent) => {
logLineWithIcon(