Skip to content

Commit

Permalink
Update readme (#406)
Browse files Browse the repository at this point in the history
* Move API reference out of README

* Add examples

* Remove plugins from README
  • Loading branch information
alexdebrie authored Mar 30, 2018
1 parent ff09341 commit 3ffe17c
Show file tree
Hide file tree
Showing 13 changed files with 975 additions and 516 deletions.
550 changes: 34 additions & 516 deletions README.md

Large diffs are not rendered by default.

476 changes: 476 additions & 0 deletions docs/api.md

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions docs/plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Plugin System

The Event Gateway is built with extensibility in mind. Built-in plugin system allows reacting on system events and
manipulate how an event is processed through the Event Gateway.

_Current implementation supports plugins written only in Golang. We plan to support other languages in the future._

Plugin system is based on [go-plugin](https://github.com/hashicorp/go-plugin). A plugin needs to implement the following
interface:

```go
type Reacter interface {
Subscriptions() []Subscription
React(event event.Event) error
}
```

`Subscription` model indicates the event that plugin subscribes to and the subscription type. A subscription can be either
sync or async. Sync (blocking) subscription means that in case of error returned from `React` method the event won't be
further processed by the Event Gateway.

`React` method is called for every system event that plugin subscribed to.

For more details, see [the example plugin](../plugin/example).
156 changes: 156 additions & 0 deletions examples/crm-service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Using Custom Events

## Background

In a microservices architecture, you'll often have events in one service that are relevant to other services. In this example, we'll show you how to emit events from one service and subscribe to event from other services.

We'll use a common example -- the user creation flow. Your users may be created in the Users service, but the event when a user is created is important to multiple services. Perhaps you want to trigger a welcome email in your email service, post a celebratory message to Slack, or insert the user into your marketing team's CRM.

Let's focus on the last use case here. We'll emulate a CRM service that listens to events across our architecture and updates our CRM accordingly. We'll set up a subscription on the `user.created` event that's emitted from our Users service and insert the new user into our CRM.

## Deploying & Testing the CRM Service

First, let's set up our CRM service. I like to start with my function logic first, as my business logic is where I should be focusing. Take a look at `index.js`, which includes the logic:

```javascript
module.exports.handler = (event, context, cb) => {
const user = event.data;
console.log('Saving user to CRM: ');
console.dir(user);

// Add your CRM logic here
// saveUserToCRM(user)

cb(null, { message: 'success' })
}
```

This function is pretty basic as you'll need to implement the logic specific to your CRM. It accepts the event and creates a user object from the event data. Then it saves that user to my CRM. We'll just log the user object here rather than actually save it to a CRM.

Next, let's see how we configure that function to be triggered by our event. In the `serverless.yml`, look at the `functions` block:

```yml
functions:
addUserToCRM:
handler: index.handler
events:
- eventgateway:
event: user.created
```
We've registered our function logic from `index.js`, and we've created an `eventgateway` subscription to the `user.created` event. Whenever that event is emitted into our Event Gateway space, this function will be triggered.

Finally, there are a few other relevant configuration sections of `serverless.yml`:

```yml
custom:
eventgateway:
space: <your space>
apiKey: <your API key>
plugins:
- "@serverless/serverless-event-gateway-plugin"
```

This `serverless-event-gateway-plugin` adds additional functionality to register functions and subscriptions with the Event Gateway. The `eventgateway` section of the `custom` block configures the plugin with your space and api key. You may choose any space you want, as long as it hasn't already been claimed, and it will be available at `https://<space>.slsgateway.com`.

Once you've entered your space and API key, install your dependencies and deploy:

```bash
$ npm install
$ sls deploy
```

With your function deployed and subscription configured, it's time to test it out. You can use the plugin command `sls gateway emit` to emit a test event into your Event Gateway space. There's a sample event body in `event.json` to represent a test user.created event:

```bash
$ sls gateway emit --event "user.created" --data "$(cat event.json)"
Event emitted: user.created
Run `serverless logs -f <functionName>` to verify your subscribed function was triggered.
```

Note that we only get a message that an event was emitted. Custom events are processed asynchronously, and multiple functions can be subscribed to a custom event. This decouples producers and consumers, and it means a producer won't be able to tell who receives an event when it publishes it.

You can check your function logs to make sure everything is working properly:

```bash
$ sls logs -f addUserToCRM -t
START RequestId: 7b8800f5-2d34-11e8-8165-171c6f09ec0e Version: $LATEST
2018-03-21 18:20:07.365 (+00:00) 7b8800f5-2d34-11e8-8165-171c6f09ec0e Saving user to CRM:
{ company: 'Test Corp, Inc.',
email: '[email protected]',
firstname: 'Test',
lastname: 'Williams',
username: 'test-user' }
END RequestId: 7b8800f5-2d34-11e8-8165-171c6f09ec0e
REPORT RequestId: 7b8800f5-2d34-11e8-8165-171c6f09ec0e Duration: 5.77 ms Billed Duration: 100 ms Memory Size: 1024 MB Max Memory Used: 19 MB
```
As you can see, our function was triggered from our event and logged out the message as desired. Success!
## Emitting Events in your Services
In the example above, we saw how to set up a consumer of events as well as how to publish test events via the command line. However, most events won't come from the command line -- they'll come from your application code, or webhooks, or database events.
In this example, let's complete the story by emitting `user.created` events from our Users service. The easiest way to emit an event is to use the [`event-gateway-sdk`](https://github.com/serverless/event-gateway-sdk) for Node.
To use the SDK, you'll create an Event Gateway client configured for your space, then emit an event with the event name and payload to send.
The `emit.js` file shows how you could use the SDK in your application:
```javascript
// emit.js

const SDK = require('@serverless/event-gateway-sdk');
const SPACE = 'examplestest';

const eventGateway = new SDK({
url: `https://${SPACE}.slsgateway.com`,
space: `${SPACE}`
})

function createUser(user) {
// Save your user to database
// saveUserToDB(user)

// Then emit your event
eventGateway
.emit({
event: 'user.created',
data: user
})
.then(() => console.log('Emitted user.created event!'))
}

const user = {
"username": "sls-fan",
"firstname": "Bill",
"lastname": "Jones",
"company": "Big Corp, Inc.",
"email": "[email protected]"
}

createUser(user);
```
The `createUser` function is similar to one you would have in your application -- save the new user to your database, then emit a `user.created` event that other services can subscribe to.
You can run this file as a test if you like. You'll need to edit the `SPACE` variable to match the space you created in your `serverless.yml`. Then just execute the file and check your function logs:
```bash
$ node emit.js
Emitted user.created event!
$ sls logs -f addUserToCRM -t
START RequestId: 5a11a5d8-2d36-11e8-ac4f-eb85ef6fadda Version: $LATEST
2018-03-21 18:33:29.796 (+00:00) 5a11a5d8-2d36-11e8-ac4f-eb85ef6fadda Saving user to CRM:
{ company: 'Big Corp, Inc.',
email: '[email protected]',
firstname: 'Bill',
lastname: 'Jones',
username: 'sls-fan' }
END RequestId: 5a11a5d8-2d36-11e8-ac4f-eb85ef6fadda
REPORT RequestId: 5a11a5d8-2d36-11e8-ac4f-eb85ef6fadda Duration: 1.64 ms Billed Duration: 100 ms Memory Size: 1024 MB Max Memory Used: 21 MB
```
Success!
30 changes: 30 additions & 0 deletions examples/crm-service/emit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const SDK = require('@serverless/event-gateway-sdk')
const SPACE = 'examplestest'

const eventGateway = new SDK({
url: `https://${SPACE}.slsgateway.com`,
space: `${SPACE}`
})

function createUser (user) {
// Save your user to database
// saveUserToDB(user)

// Then emit your event
eventGateway
.emit({
event: 'user.created',
data: user
})
.then(() => console.log('Emitted user.created event!'))
}

const user = {
'username': 'sls-fan',
'firstname': 'Bill',
'lastname': 'Jones',
'company': 'Big Corp, Inc.',
'email': '[email protected]'
}

createUser(user)
1 change: 1 addition & 0 deletions examples/crm-service/event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"username": "test-user", "firstname": "Test", "lastname": "Williams", "company": "Test Corp, Inc.", "email": "[email protected]"}
12 changes: 12 additions & 0 deletions examples/crm-service/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use script'

module.exports.handler = (event, context, cb) => {
const user = event.data
console.log('Saving user to CRM: ')
console.dir(user)

// Add your CRM logic here
// saveUserToCRM(user)

cb(null, { message: 'success' })
}
5 changes: 5 additions & 0 deletions examples/crm-service/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"devDependencies": {
"@serverless/serverless-event-gateway-plugin": ">=0.4.0"
}
}
21 changes: 21 additions & 0 deletions examples/crm-service/serverless.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
service: eg-crm-service

provider:
name: aws
runtime: nodejs6.10
region: us-east-1

custom:
eventgateway:
space: <your space>
apiKey: <your API key>

plugins:
- "@serverless/serverless-event-gateway-plugin"

functions:
addUserToCRM:
handler: index.handler
events:
- eventgateway:
event: user.created
128 changes: 128 additions & 0 deletions examples/users-service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Using Event Gateway as an API Gateway

## Background

With the explosion of FaaS and Serverless architectures, applications are getting split up into smaller and smaller pieces. Rather than have a single bundle of code that handles all of your HTTP endpoints, you can create individual functions that handle a very specific task -- create a User, update my Shopping Cart, delete my Tweet.

This separation makes it easier for teams to move independently and ship more quickly, but it also presents a management problem. How do you make it easy for clients to access these hundreds of isolated functions?

Using the Event Gateway as an API Gateway solves this problem. You can have collections of functions split into services via the [Serverless Framework](https://github.com/serverless/serverless). These services can be deployed independently to the same domain in a way that's easy for clients to access.

In this example, we'll deploy an example Users service with a few different HTTP endpoints. We'll see how we can interact with those endpoints in a familiar, RESTful way.


## Deploying & Testing the Users Service

For our example Users service, let's say we want endpoints to create a new user, to get a specific user, and to delete a single user. We'll implement each of these endpoints in separate functions. Let's take a look at `index.js` where they're implemented:

```javascript
const faker = require('faker')

module.exports.get = (event, context, cb) => {
console.log(event)

cb(null, {
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: event.data.params.id,
name: faker.name.findName(),
email: faker.internet.email()
})
})
}

module.exports.post = (event, context, cb) => {
console.log(event)
cb(null, {
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: faker.random.number(),
name: faker.name.findName(),
email: faker.internet.email()
})
})
}

module.exports.delete = (event, context, cb) => {
console.log(event)
cb(null, {
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: `Deleted user with id ${event.data.params.id}`
})
})
}
```

We're exporting three functions -- `get`, `post`, and `delete` -- to handle our three endpoints. The logic is using the `faker` library to send out pretend data, but you would use some sort of data store in your actual application.

Next, let's see how we configure the functions to be triggered by our Event Gateway. In the `serverless.yml`, look at the `functions` block:

```yml
functions:
getUser:
handler: index.get
events:
- eventgateway:
event: http
method: GET
path: /users/:id
createUser:
handler: index.post
events:
- eventgateway:
event: http
path: /users
method: POST
deleteUser:
handler: index.delete
events:
- eventgateway:
event: http
path: /users/:id
method: DELETE
```
For each function we've registered an `eventgateway` event of type `http`, indicating that it's an API Gateway event. We include the path and HTTP method to match on as well.

Finally, there are a few other relevant configuration sections of `serverless.yml`:

```yml
custom:
eventgateway:
space: <your space>
apiKey: <your API key>
plugins:
- "@serverless/serverless-event-gateway-plugin"
```

This `serverless-event-gateway-plugin` adds additional functionality to register functions and subscriptions with the Event Gateway. The `eventgateway` section of the `custom` block configures the plugin with your space and api key. You may choose any space you want, as long as it hasn't already been claimed, and it will be available at `https://<space>.slsgateway.com`.

Once you've entered your space and API key, install your dependencies and deploy:

```bash
$ npm install
$ sls deploy
```

Now let's give it a try! Using `curl` or in your browser, navigate to the `getUser` endpoint at `https://<space>.slsgateway.com/users/15`:

```bash
$ $ curl -X GET https://examplestest.slsgateway.com/users/15 | jq "."
{
"id": "10",
"name": "Ariel Dach",
"email": "[email protected]"
}
```

It worked! You can try your `POST` and `DELETE` endpoints as well.

You can reuse this same space across multiple services using different endpoint paths to allow for agility across your team.
Loading

0 comments on commit 3ffe17c

Please sign in to comment.