gateway Documentation

React to any event with serverless functions across clouds

RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy gateway on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name gateway -p 4000:4000 --restart always serverless/gateway:latest

Technical Documentation: serverless/gateway

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (`README.md`)

๐Ÿ“ฆ Archived - This repository is archived and preserved for reference only. No updates, issues, or pull requests will be accepted. If you have questions, please reach out to our support team.


We're Hiring! โ€ข Website โ€ข Slack โ€ข Newsletter โ€ข Forum โ€ข Meetups โ€ข Twitter

The Event Gateway combines both API Gateway and Pub/Sub functionality into a single event-driven experience. It's
dataflow for event-driven, serverless architectures. It routes Events (data) to Functions (serverless compute).
Everything it cares about is an event! Even calling a function. It makes it easy to share events across different
systems, teams and organizations!

Use the Event Gateway right now, by running the Event Gateway Getting Started Application with the Serverless Framework.

Features:

  • Platform agnostic - All your cloud services are now compatible with one another: share cross-cloud functions and events with AWS Lambda, Microsoft Azure, IBM Cloud and Google Cloud Platform.
  • Send events from any cloud - Data streams in your application become events. Centralize events from any cloud provider to get a birdโ€™s eye view of all the data flowing through your cloud.
  • React to cross-cloud events - You arenโ€™t locked in to events and functions being on the same provider: Any event, on any cloud, can trigger any function. Set events and functions up like dominoes and watch them fall.
  • First-class support for CloudEvents - Emit and react to events in CloudEvents standard.
  • Expose events to your team - Share events and functions to other parts of the application. Your teammates can find them and utilize them in their own services.
  • Extendable through middleware - Perform data transforms, authorizations, serializations, and other custom computes straight from the Event Gateway.

The Event Gateway is a L7 proxy and realtime dataflow engine, intended for use with Functions-as-a-Service on AWS,
Azure, Google & IBM.

The project is under heavy development. The APIs will continue to change until we release a 1.0.0 version. It's not
yet ready for production applications.

Contents

  1. Quick Start
  2. Running the Event Gateway
  3. Motivation
  4. Components
    1. Event Registry
    2. Function Discovery
    3. Subscriptions
    4. Spaces
  5. CloudEvents Support
  6. SDKs
  7. Versioning
  8. FAQ
  9. Background
  10. Community

Reference

  1. API
  2. Event Types
  3. Subscription Types
  4. Architecture
  5. Clustering
  6. System Events and Plugin System
  7. System Metrics
  8. Reliability Guarantees

Quick Start

Getting Started

Looking for an example to get started? The easiest way to use the Event Gateway is with the serverless-event-gateway-plugin with the Serverless Framework. Check out the Getting Started Example to deploy your first service to the Event Gateway.

Running the Event Gateway

Hosted version

If you don't want to run the Event Gateway yourself, you can use the hosted version provided by the Serverless team. Sign up here!

via Docker

There is an official Docker image.

bash
docker run -p 4000:4000 -p 4001:4001 serverless/event-gateway --dev

Binary

On macOS or Linux run the following to download the binary:

text
curl -sfL https://raw.githubusercontent.com/serverless/event-gateway/master/install.sh | sh

On Windows download binary.

Then run the binary in development mode with:

bash
$ event-gateway --dev

Kubernetes

The repo contains helm charts for a quick deploy to an existing cluster using native nginx Ingress. To deploy
a development cluster you can follow the minikube instructions.


If you want more detailed information on running and developing with the Event Gateway,
please check Running Locally and Developing guides.

Motivation

  • It is cumbersome to plug things into each other. This should be easy! Why do I need to set up a queue system to
    keep track of new user registrations or failed logins?
  • Introspection is terrible. There is no performant way to emit logs and metrics from a function. How do I know
    a new piece of code is actually working? How do I feed metrics to my existing monitoring system? How do I
    plug this function into to my existing analytics system?
  • Using new functions is risky without the ability to incrementally deploy them.
  • The AWS API Gateway is frequently cited as a performance and cost-prohibitive factor for using AWS Lambda.

Components

Event Registry

Event Registry is a single source of truth about events occuring in the space. Every event emitted to a space has to have event
type registered beforehand. Event Registry also provides a way to authorize incoming events. Please check
Event Types reference for more information.

Function Discovery

Discover and call serverless functions from anything that can reach the Event Gateway. Function Discovery supports the
following function types:

  • FaaS functions (AWS Lambda, Google Cloud Functions, Azure Functions, IBM Cloud Functions)
  • Connectors (AWS Kinesis, AWS Kinesis Firehose, AWS SQS)
  • HTTP endpoints/Webhook (e.g. POST http://example.com/function)

Function Discovery stores information about functions allowing the Event Gateway to call them as a reaction to received
event.

Example: Register An AWS Lambda Function

curl example

bash
curl --request POST \
  --url http://localhost:4001/v1/spaces/default/functions \
  --header 'content-type: application/json' \
  --data '{
    "functionId": "hello",
    "type": "awslambda",
    "provider":{
      "arn": "arn:aws:lambda:us-east-1:377024778620:function:bluegreen-dev-hello",
      "region": "us-east-1"
    }
}'

Node.js SDK example

javascript
const eventGateway = new EventGateway({ url: 'http://localhost' })
eventGateway.registerFunction({
  functionId: 'sendEmail',
  type: 'awslambda',
  provider: {
    arn: 'xxx',
    region: 'us-west-2'
  }
})

Subscriptions

Lightweight pub/sub system. Allows functions to asynchronously receive custom events. Instead of rewriting your
functions every time you want to send data to another place, this can be handled entirely in configuration
using the Event Gateway. This completely decouples functions from one another, reducing communication costs across
teams, eliminates effort spent redeploying functions, and allows you to easily share events across functions,
HTTP services, even different cloud providers. Functions may be registered as subscribers to a custom event.
When an event occurs, all subscribers are called asynchronously with the event as its argument.

Creating a subscription requires providing ID of registered function, an event type, an HTTP method (POST by default),
and a path (/ by default). The method and path properties defines HTTP endpoint which Events API will be listening on.

Event Gateway supports two subscription types: async and sync. Please check Subscription Types
reference for more information.

Example: Subscribe to an Event

curl example

bash
curl --request POST \
  --url http://localhost:4001/v1/spaces/default/subscriptions \
  --header 'content-type: application/json' \
  --data '{
    "type": "async",
    "eventType": "user.created",
    "functionId": "sendEmail",
    "path": "/myteam"
  }'

Node.js SDK example

javascript
const eventGateway = new EventGateway({ url: 'http://localhost' })
eventGateway.subscribe({
  type: 'async',
  eventType: 'user.created',
  functionId: 'sendEmail',
  path: '/myteam'
})

sendEmail function will be invoked for every user.created event to <Events API>/myteam endpoint.

Example: Emit an Event

curl example

bash
curl --request POST \
  --url http://localhost:4000/ \
  --header 'content-type: application/json' \
  --data '{
    "eventType": "myapp.user.created",
    "eventID": "66dfc31d-6844-42fd-b1a7-a489a49f65f3",
    "cloudEventsVersion": "0.1",
    "source": "/myapp/services/users",
    "eventTime": "1990-12-31T23:59:60Z",
    "data": { "userID": "123" },
    "contentType": "application/json"
  }'

Node.js SDK example

javascript
const eventGateway = new EventGateway({ url: 'http://localhost' })
eventGateway.emit({
  "eventType": "myapp.user.created",
  "eventID": "66dfc31d-6844-42fd-b1a7-a489a49f65f3",
  "cloudEventsVersion": "0.1",
  "source": "/myapp/services/users",
  "eventTime": "1990-12-31T23:59:60Z",
  "data": { "userID": "123" },
  "contentType": "application/json"
})

Example: Subscribe to an `http.request` Event

Not all data are events that's why Event Gateway has a special, built-in http.request event type that enables subscribing to
raw HTTP requests.

curl example

bash
curl --request POST \
  --url http://localhost:4001/v1/spaces/default/subscriptions \
  --header 'content-type: application/json' \
  --data '{
    "type": "sync",
    "eventType": "http.request",
    "functionId": "listUsers",
    "method": "GET",
    "path": "/users"
  }'

Node.js SDK example

javascript
const eventGateway = new EventGateway({ url: 'http://localhost' })
eventGateway.subscribe({
  type: 'sync',
  eventType: 'http.request',
  functionId: 'listUsers',
  method: 'GET',
  path: '/users'
})

listUsers function will be invoked for every HTTP GET request to <Events API>/users endpoint.

Spaces

One additional concept in the Event Gateway are Spaces. Spaces provide isolation between resources. Space is a
coarse-grained sandbox in which entities (Functions and Subscriptions) can interact freely. All actions are
possible within a space: publishing, subscribing and invoking.

Space is not about access control/authentication/authorization. It's only about isolation. It doesn't enforce any
specific subscription path.

This is how Spaces fit different needs depending on use-case:

  • single user - single user uses default space for registering function and creating subscriptions.
  • multiple teams/departments - different teams/departments use different spaces for isolation and for hiding internal
    implementation and architecture.

Technically speaking Space is a mandatory field ("default" by default) on Function or Subscription object that user has
to provide during function registration or subscription creation. Space is a first class concept in Config API. Config
API can register function in specific space or list all functions or subscriptions from a space.

CloudEvents Support

Event Gateway has the first-class support for CloudEvents. It means few things.

First of all, if the event emitted to the Event Gateway is in CloudEvents format, the Event Gateway is able to recognize it and trigger proper subscriptions based on event type specified in the event. Event Gateway supports both Structured Content and Binary Content modes described in HTTP Transport Binding spec.

Secondly, there is a special, built-in HTTP Request Event type allowing reacting to raw HTTP requests that are not formatted according to CloudEvents spec. This event type can be especially helpful for building REST APIs.

Currently, Event Gateway supports CloudEvents v0.1 schema specification.

SDKs

Versioning

This project uses Semantic Versioning 2.0.0. We are in initial development phase
right now (v0.X.Y). The public APIs should not be considered stable. Every breaking change will be listed in the
release changelog.

FAQ

What The Event Gateway is NOT

  • it's not a replacement for message queues (no message ordering, currently weak durability guarantees only)
  • it's not a replacement for streaming platforms (no processing capability and consumers group)
  • it's not a replacement for existing service discovery solutions from the microservices world

Event Gateway vs FaaS Providers

The Event Gateway is NOT a FaaS platform. It integrates with existing FaaS providers (AWS Lambda, Google Cloud Functions,
Azure Functions, OpenWhisk Actions). The Event Gateway enables building large serverless architectures in a unified way
across different providers.

Background

SOA came along with a new set of challenges. In monolithic architectures, it was simple to call a built-in library or
rarely-changing external service. In SOA it involves much more network communication which is not reliable. The main problems to solve include:

  1. Where is the service deployed? How many instances are there? Which instance is the closest to me? (service discovery)
  2. Requests to the service should be balanced between all service instances (load balancing)
  3. If a remote service call failed I want to retry it (retries)
  4. If the service instance failed I want to stop sending requests there (circuit breaking)
  5. Services are written in multiple languages, I want to communicate between them using the best language for the particular task (sidecar)
  6. Calling remote service should not require setting up new connection every time as it increases request time (persistent connections)

The following systems are solutions those problems:

The main goal of those tools is to manage the inconveniences of network communication.

Microservices Challenges & FaaS

The greatest benefit of serverless/FaaS is that it solves almost all of above problems:

  1. service discovery: I don't care! I have a function name, that's all I need.
  2. load balancing: I don't care! I know that there will be a function to handle my request (blue/green deployments still an issue though)
  3. retries: It's highly unusual that my request will not proceed as function instances are ephemeral and failing function is immediately replaced with a new instance. If it happens I can easily send another request. In case of failure, it's easy to understand what is the cause.
  4. circuit breaking: Functions are ephemeral and auto-scaled, low possibility of flooding/DoS & cascading failures.
  5. sidecar: calling function is as simple as calling method from cloud provider SDK.
  6. in FaaS setting up persistent connection between two functions defeats the purpose as functions instances are ephemeral.

Tools like Envoy/Linkerd solve different domain of technical problems that doesn't occur in serverless space. They have a lot of features that are unnecessary in the context of serverless computing.

Service Discovery in FaaS = Function Discovery

Service discovery problems may be relevant to serverless architectures, especially when we have a multi-cloud setup or we want to call a serverless function from a legacy system (microservices, etc...). There is a need for some proxy that will know where the function is actually deployed and have retry logic built-in. Mapping from function name to serverless function calling metadata is a different problem from tracking the availability of a changing number of service instances. That's why there is a room for new tools that solves function discovery problem rather than the service discovery problem. Those problems are fundamentally different.


Overview (`contrib/README.md`)

Event Gateway Contributions

This folder contains services and contributions which complement the Event Gateway.

  • Install Event Gateway on Kubernetes using Helm
  • Provision the Event Gateway on AWS ECS Fargate using Terraform
  • Grafana Prometheus Dashboard template for Event Gateway
  • Grafana InfluxDB Dashboard template for Event Gateway

Overview (`libkv/README.md`)

`libkv` package

This package implements function.Service and subscription.Service using libkv (etcd) as a backend store.


Overview (`tests/README.md`)

`tests` package

This package contains high-level integration tests. Unit tests are placed in the respective packages.


Overview (`contrib/helm/README.md`)

Event Gateway on Kubernetes

This chart deploys the Event Gateway with etcd onto a Kubernetes cluster. Please note, the default instructions expect
an existing kubernetes cluster that supports ingress, such as GKE. If your environment doesn't have ingress support
set up, please follow the minikube instructions to set this up for your development environment.

Contents

  1. Quickstart
  2. Examples
    1. Register a function
    2. Query all function
    3. Register an event type
    4. Query all event types
    5. Register a subscription
    6. Query all subscriptions
    7. Trigger an event
  3. Configuration
  4. Cleanup

Quickstart

Make sure you have helm installed on your machine and run helm init on your k8s cluster. This will set up the
helm and tiller functions required for easy deployment of config files to your cluster. You can follow
instructions here if you have not set this up previously.

Once installed, navigate to the event-gateway/contrib/helm folder and install the following components:

etcd-operator

bash
helm install stable/etcd-operator --name ego [--namespace <namespace>]

event-gateway

bash
helm install event-gateway --name eg [--namespace <namespace>]

This will install each of the etcd-operator and event-gateway into the default namespace in kubernetes. Please note,
this namespace has no bearing on your Event Gateway spaces as outlined in the docs. If you'd like
to install etcd-operator and event-gateway in another namespace, add the --namespace <namespace> option to
both helm install commands above.

Next we'll need to collect the Event Gateway IP to use on the CLI. Since the Ingress was configured to receive all
connections from the eventgateway.minikube host, you'll need to pass this as a header value to the request.

bash
export EVENT_GATEWAY_URL=$(kubectl get ingress event-gateway-ingress -o jsonpath={.status.loadBalancer.ingress[0].ip})
curl --request GET \
     --url http://{EVENT_GATEWAY_URL}/v1/metrics \
     --header 'content-type: application/json' \
     --header 'host: eventgateway.minikube'

With your environment set up, you can now jump to the examples section to put your event-gateway to use!

Examples

Once you've set the EVENT_GATEWAY_URL environment variable, you're set to start interacting with the event-gateway!

NOTE: the events and configuration API ports are abstracted away from us via the kubernetes Ingress. The path-based
routing will ensure the request goes to the proper service managed by the cluster.

DOUBLENOTE: the examples below all assume the default namespace for the event-gateway. If you've updated or changed
this on your end, please don't forget to update the queries accordingly.

Register a function

Define the function registration payload, using AWS as an example:

bash
cat > function.json <<EOF
{
    "functionId": "echo",
    "type": "awslambda",
    "provider": {
        "arn": "arn:aws:lambda:us-east-1:123456789012:function:event-gateway-tests-dev-echo",
        "region": "us-east-1",
        "awsAccessKeyID": "AAAAAAAAAAAAAAAAAAAA",
        "awsSecretAccessKey": "AAAAaBcDeFgHiJqLmNoPqRsTuVwXyz0123456789"
    }
}
EOF

Then call the registration endpoint with your json payload:

bash
curl --request POST \
  --url http://${EVENT_GATEWAY_URL}/v1/spaces/default/functions \
  --header 'content-type: application/json' \
  --header 'host: eventgateway.minikube' \
  --data @function.json

And the corresponsing reply (if successful) should read something like the following:

bash
{
	"space": "default",
	"functionId": "echo",
	"type": "awslambda",
	"provider": {
		"arn": "arn:aws:lambda:us-east-1:123456789012:function:event-gateway-tests-dev-echo",
		"region": "us-east-1",
		"awsAccessKeyId": "AAAAAAAAAAAAAAAAAAAA",
		"awsSecretAccessKey": "AAAAaBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789"
	}
}

NOTE: if you try to overwrite an existing function, you will receive an error! To replace an existing function
you will have to delete it first, then register the function once more. For example, trying to re-register the echo
function will yield:

bash
curl --request POST \
   --url http://${EVENT_GATEWAY_URL}/v1/spaces/default/functions \
   --header 'content-type: application/json' \
   --header 'host: eventgateway.minikube' \
   --data @function.json

{
    "errors": [{
        "message": "Function \"echo\" already registered."
    }]
}

Query all functions

To check for registered functions, query the config API with the GET request:

bash
curl --request GET \
  --url http://${EVENT_GATEWAY_URL}/v1/spaces/default/functions \
  --header 'content-type: application/json' \
  --header 'host: eventgateway.minikube'  | jq

You should see the functions list return your defined set of functions across all vendors.

bash
{
  "functions": [
    {
      "space": "default",
      "functionId": "echo",
      "type": "awslambda",
      "provider": {
        "arn": "arn:aws:lambda:us-east-1:123456789012:function:event-gateway-tests-dev-echo",
        "region": "us-east-1",
        "awsAccessKeyId": "AAAAAAAAAAAAAAAAAAAA",
        "awsSecretAccessKey": "AAAAaBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789"
      }
    }
  ]
}

Register an event type

To register an event, make sure to POST the event name to the event-gateway.

bash
curl --request POST \
  --url http://${EVENT_GATEWAY_URL}/v1/spaces/default/eventtypes \
  --header 'content-type: application/json' \
  --header 'host: eventgateway.minikube' \
  --data '{ "name": "eventgateway.function.invoked" }'

The reply should look something like the following:

bash
{ 
  "space": "default",
  "name": "eventgateway.function.invoked"
}

Query all event types

bash
curl --request GET \
  --url http://${EVENT_GATEWAY_URL}/v1/spaces/default/eventtypes \
  --header 'content-type: application/json' \
  --header 'host: eventgateway.minikube'

Your registered events reply should look as follows:

bash
{
  "eventTypes": [
    {
      "space": "default",
      "name": "eventgateway.function.invoked"
    }
  ]
}

Register a subscription

To register subscriptions to one of your registered event types, make sure to specify the eventType in
the JSON POST payload.

bash
curl --request POST \
  --url http://${EVENT_GATEWAY_URL}/v1/spaces/default/subscriptions \
  --header 'content-type: application/json' \
  --header 'host: eventgateway.minikube' \
  --data '{
    "type": "async",
    "eventType": "eventgateway.function.invoked",
    "functionId": "echo",
    "path": "/",
    "method": "POST"
}'

Your reply payload should include the subscriptionId for your new subscription:

bash
{
  "space": "default",
  "subscriptionId": "YXN5bmMsZXZlbnRnYXRld2F5LmZ1bmN0aW9uLmludm9rZWQsZWNobywlMkYsUE9TVA",
  "type": "async",
  "eventType": "eventgateway.function.invoked",
  "functionId": "echo",
  "path": "/",
  "method": "POST"
}

Query all subscriptions

To list our your current subscrptions, you can do the following:

bash
curl --request GET \
  --url http://${EVENT_GATEWAY_URL}/v1/spaces/default/subscriptions \
  --header 'content-type: application/json' \
  --header 'host: eventgateway.minikube'

The output should list each of the registered subscriptions:

bash
{
  "subscriptions": [
    {
      "space": "default",
      "subscriptionId": "YXN5bmMsZXZlbnRnYXRld2F5LmZ1bmN0aW9uLmludm9rZWQsZWNobywlMkYsUE9TVA",
      "type": "async",
      "eventType": "eventgateway.function.invoked",
      "functionId": "echo",
      "path": "/",
      "method": "POST"
    }
  ]
}

Trigger an event

In order to trigger a registered function, call the event-gateway URL with the proper functionId. Following
our example from earlier, we've registered a GET function with functionId set to echo. To trigger this function,
we would:

bash
curl --request GET \
  --url http://${EVENT_GATEWAY_URL}/echo \
  --header 'content-type: application/json' \
  --header 'host: eventgateway.minikube'

NOTE: as mentioned earlier, the events service is handled by the path-routing service of the kubernetes Ingress. Any path
that's prepended with /v1 will ultimately route to the config service, while other paths default to the events service.

Configuration

Parameter Description Default
images.repository Event Gateway image serverless/event-gateway
images.tag Event Gateway image tag 0.9.0
replicaCount Number of containers 3
service.type Type of Kubernetes service LoadBalancer
service.annotations Custom annotations for the service []
service.config.port Config API port number 4001
service.events.port Events API port number 4000
resources.limits.cpu CPU resource limits 200m
resources.limits.memory Memory resource limits 256Mi
resources.requests.cpu CPU resource requests 200m
resources.requests.memory Memory resource requests 256Mi
command Options to pass to event-gateway command [--db-hosts=eg-etcd-cluster-client:2379, --log-level=debug]
etcd_cluster_name Name of the etcd cluster. Passed to the --db-hosts option as <etcd-cluster-name>-client eg-etcd-cluster

The service annotations can be used to set any annotations required by your platform, for example, if
you update your values.yml with:

text
-  annotations: []
+  annotations:
+    - "service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0"
+    - "foo: bar"

then the service will be annotated as shown:

text
$ helm install event-gateway --debug --dry-run | grep "kind: Service" -A5
kind: Service
metadata:
  name: rafting-umbrellabird-event-gateway
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
    foo: bar

Cleanup

When you'd like to clean up the deployments, it's easy to remove services using helm:

bash
helm delete --purge eg
helm delete --purge ego

Overview (`contrib/terraform/README.md`)

Event Gateway Terraform module

This module creates Event Gateway running on ECS Fargate with a standalone etcd cluster.

The module is an extract form the Tectonic Installer repository.

Usage

hcl
module "event-gateway" {
  source = "github.com/serverless/event-gateway//contrib/terraform/modules/event-gateway"

  aws_region = "us-east-1"
  command_list = ["-db-hosts", "event-gateway-etcd-0.etcd:2379,event-gateway-etcd-1.etcd:2379,event-gateway-etcd-2.etcd:2379", "-log-level", "debug"]
  tags = {
    Application = "event-gateway"
  }
}

output "config_url" {
  value = "${module.event-gateway.config_url}"
}

output "events_url" {
  value = "${module.event-gateway.events_url}"
}

Debugging etcd

It's possible to enable SSH access via bastion instance, by adding parameters:

text
bastion_enabled = true
ssh_key      = "eg-key"

Bastion IP can be distplayed by adding output:

text
output "bastion_ip" {
  value = "${module.event-gateway.bastion_ip}"
}

To connect to one of the etcd cluster hosts, run:

bash
ssh -J ec2-user@<bastion_ip> core@<etcd_host_private_ip>

Inputs

Name Description Type Default Required
aws_region AWS region for the stack string - yes
bastion_enabled Set to true enables SSH access to etcd nodes in the private subnet string false no
command_list List of parameters for the event-gateway command list ["-log-level", "debug"] no
config_alb_name Config ALB name string alb-config no
config_port Port number of the Event Gateway Config API string 4001 no
eg_image Event Gateway docker image string serverless/event-gateway:latest no
eg_vpc_name Event Gateway VPC name string eg-vpc no
etcd_base_domain Name of the base domain for the etcd cluster string etcd no
etcd_image etcd Docker image string quay.io/coreos/etcd:v3.1.8 no
etcd_instance_count Number of nodes in the etcd cluster string 3 no
etcd_instance_type Etcd node type string t2.micro no
etcd_root_volume_iops Number of IOPS of the etcd cluster volumes string 100 no
etcd_root_volume_size Size of the etcd cluster volumes (in GiB) string 30 no
etcd_root_volume_type Type of the etcd cluster volumes string gp2 no
etcd_ssh_key (optional) Name of the preexisting SSH key string `` no
etcd_tls_enabled Enable TLS for the etcd cluster string false no
events_alb_name Events ALB name string alb-events no
events_port Event Gateway Events API port number string 4000 no
fargate_cpu Fargate instance CPU units string 256 no
fargate_memory Fargate instance memory string 512 no
tags Additional tags map <map> no
task_count Number of Event Gateway Fargate tasks string 3 no

Outputs

Name Description
bastion_ip Public IP of etcd bastion instance
config_url Event Gateway Config API URL
events_url Event Gateway Events API URL

Overview (`examples/aws-node-crm-service/README.md`)

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:
    url: <your URL> # e.g. tenant-myapp.slsgateway.com
    accessKey: <your Access 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 application URL and access key. Sign up for both here.

Once you've entered your Application URL and Access 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
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 for Node.

To use the SDK, you'll create an Event Gateway client configured for your application, 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 URL = 'tenant-myapp.slsgateway.com'

const eventGateway = new SDK({
  url: URL
})

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 APP variable to match the application you used 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
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!


Overview (`examples/aws-node-users-service/README.md`)

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. 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:
    url: <your URL> # e.g. tenant-myapp.slsgateway.com
    accessKey: <your Access 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 Application URL and Access Key. You may get this by signing up here.

Once you've entered your Application URL and Access 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://<applicationURL>/users/15:

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

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

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


Api (`docs/api.md`)

API

The Event Gateway has two APIs: the Configuration API for registering functions and subscriptions, and the runtime Events API for sending events into the Event Gateway.
This document contains the API documentation for both Events and Configuration APIs. You can also find links to OpenAPI specs for these APIs.

Contents

  1. Events API
    1. Event Definition
    2. How To Emit an Event
    3. HTTP Request Event
    4. CORS
    5. Legacy Mode
  2. Configuration API
    1. Event Types
      1. Create Event Type
      2. Update Event Type
      3. Delete Event Type
      4. List Event Types
      5. Get Event Type
    2. Functions
      1. Register Function
      2. Update Function
      3. Delete Function
      4. List Functions
      5. Get Function
    3. Subscriptions
      1. Create Subscription
      2. Update Subscription
      3. Delete Subscription
      4. List Subscriptions
      5. Get Subscription
    4. CORS
      1. Create CORS Configuration
      2. Update CORS Configuration
      3. Delete CORS Configuration
      4. List CORS Configurations
      5. Get CORS Configuration
    5. Prometheus Metrics
    6. Status

Events API

The Event Gateway exposes an API for emitting events. By default Events API runs on :4000 port.

Event Definition

All data that passes through the Event Gateway is formatted as a CloudEvent, based on CloudEvents v0.1 schema.

Example:

json
{
  "eventType": "myapp.user.created",
  "eventID": "66dfc31d-6844-42fd-b1a7-a489a49f65f3",
  "cloudEventsVersion": "0.1",
  "source": "https://serverless.com/event-gateway/#transformationVersion=0.1",
  "eventTime": "1990-12-31T23:59:60Z",
  "data": { "foo": "bar" },
  "contentType": "application/json"
}

How To Emit an Event

Creating a subscription requires path (default: /), method (default: POST) and eventType. path indicates path under which you can send the event.

Endpoint

POST <Events API URL>/<Subscription Path>

Request

CloudEvents payload

Response

Status code:

  • 202 Accepted - this status code is returned if there is no sync subscription defined. Otherwise, status code is controlled by function synchronously subscribed on this endpoint.

HTTP Request Event

Not all data are events that's why Event Gateway has a special, built-in http.request event type that enables subscribing to
raw HTTP requests. It's especially helpful for building REST APIs or supporting legacy payloads. http.request event is a
CloudEvent created by Event Gateway where data field has the following structure:

  • path - string - request path
  • method - string - request method
  • headers - object - request headers
  • host - string - request host
  • query - object - query parameters
  • params - object - matched path parameters
  • body - depends on Content-Type header - request payload

CORS

By default cross-origin resource sharing (CORS) is disabled. CORS is configured per-method/path basis using
CORS Configuration API.

Event Gateway handles preflight OPTIONS requests for you. You don't need to setup subscription for OPTIONS method
because the Event Gateway will respond with all appropriate headers.

Legacy Mode

Legacy mode is deprecated and will be removed in upcoming releases.

In legacy mode, Event Gateway is able to recognize event type based on Event header. If the event is not formatted according to CloudEvents specification Event Gateway looks for this header and creates CloudEvent internally. In this case, whole request body is put into data field.

Event Data Type

The MIME type of the data block can be specified using the Content-Type header (by default it's
application/octet-stream). This allows the Event Gateway to understand how to deserialize the data block if it needs
to. In case of application/json type the Event Gateway passes JSON payload to the target functions. In any other case
the data block is base64 encoded.

Configuration API

OpenAPI spec

The Event Gateway exposes a RESTful JSON configuration API. By default Configuration API runs on :4001 port.

Event Types

Create Event Type

Endpoint

POST <Configuration API URL>/v1/spaces/<space>/eventtypes

Request

JSON object:

  • name - string - required, event type name
  • authorizerId - string - authorizer function ID
  • metadata - object - arbitrary metadata

Response

Status code:

  • 201 Created on success
  • 400 Bad Request on validation error
  • 409 Conflict if event type already exists

JSON object:

  • space - string - space name
  • name - string - event type name
  • authorizerId - string - authorizer function ID
  • metadata - object - arbitrary metadata

Update Event Type

Endpoint

PUT <Configuration API URL>/v1/spaces/<space>/eventtypes/<event type name>

Request

JSON object:

  • authorizerId - string - authorizer function ID
  • metadata - object - arbitrary metadata

Response

Status code:

  • 200 OK on success
  • 400 Bad Request on validation error or if the authorizer function doesn't exist
  • 404 Not Found if event type doesn't exist

JSON object:

  • space - string - space name
  • name - string - event type name
  • authorizerId - string - authorizer function ID
  • metadata - object - arbitrary metadata

Delete Event Type

Delete event type. This operation fails if there is at least one subscription using the event type.

Endpoint

DELETE <Configuration API URL>/v1/spaces/<space>/eventtypes/<event type name>

Response

Status code:

  • 204 No Content on success
  • 400 Bad Request if there are subscriptions using the event type
  • 404 Not Found if event type doesn't exist

List Event Types

Endpoint

GET <Configuration API URL>/v1/spaces/<space>/eventtypes

Query Parameters

Endpoint allows filtering list of returned object with filters passed as query parameters. Currently, filters can only use metadata properties e.g. metadata.service=usersService.

Response

Status code:

  • 200 OK on success

JSON object:

  • eventTypes - array of object - event types:
    • space - string - space name
    • name - string - event type name
    • authorizerId - string - authorizer function ID
    • metadata - object - arbitrary metadata

Get Event Type

Endpoint

GET <Configuration API URL>/v1/spaces/<space>/eventtypes/<event type name>

Response

Status code:

  • 200 OK on success
  • 404 Not Found if event type doesn't exist

JSON object:

  • space - string - space name
  • name - string - event type name
  • authorizerId - string - authorizer function ID
  • metadata - object - arbitrary metadata

Functions

Register Function

Endpoint

POST <Configuration API URL>/v1/spaces/<space>/functions

Request

JSON object:

  • functionId - string - required, function ID
  • type - string - required, provider type: awslambda or http
  • provider - object - required, provider specific information about a function, depends on type:
    • for AWS Lambda:
      • arn - string - required, AWS ARN identifier
      • region - string - required, region name
      • awsAccessKeyId - string - optional, AWS API key ID. By default credentials from the environment are used.
      • awsSecretAccessKey - string - optional, AWS API access key. By default credentials from the environment are used.
      • awsSessionToken - string - optional, AWS session token
    • for HTTP function:
      • url - string - required, the URL of an http or https remote endpoint
    • for AWS Kinesis connector:
      • streamName - string - required, AWS Kinesis Stream Name
      • region - string - required, region name
      • awsAccessKeyId - string - optional, AWS API key ID. By default credentials from the environment are used.
      • awsSecretAccessKey - string - optional, AWS API access key. By default credentials from the environment are used.
      • awsSessionToken - string - optional, AWS session token
    • for AWS Firehose connector:
      • deliveryStreamName - string - required, AWS Firehose Delivery Stream Name
      • region - string - required, region name
      • awsAccessKeyId - string - optional, AWS API key ID. By default credentials from the environment are used.
      • awsSecretAccessKey - string - optional, AWS API access key. By default credentials from the environment are used.
      • awsSessionToken - string - optional, AWS session token
    • for AWS SQS connector:
      • queueUrl - string - required, AWS SQS Queue URL
      • region - string - required, region name
      • awsAccessKeyId - string - optional, AWS API key ID. By default credentials from the environment are used.
      • awsSecretAccessKey - string - optional, AWS API access key. By default credentials from the environment are used.
      • awsSessionToken - string - optional, AWS session token
  • metadata - object - arbitrary metadata

Response

Status code:

  • 201 Created on success
  • 400 Bad Request on validation error
  • 409 Conflict if function already exists

JSON object:

  • space - string - space name
  • functionId - string - function ID
  • provider - object - provider specific information about a function
  • metadata - object - arbitrary metadata

Update Function

Endpoint

PUT <Configuration API URL>/v1/spaces/<space>/functions/<function ID>

Request

JSON object:

  • type - string - required, provider type: awslambda or http
  • provider - object - required, provider specific information about a function, depends on type:
    • for AWS Lambda:
      • arn - string - required, AWS ARN identifier
      • region - string - required, region name
      • awsAccessKeyId - string - optional, AWS API key ID
      • awsSecretAccessKey - string - optional, AWS API key
      • awsSessionToken - string - optional, AWS session token
    • for HTTP function:
      • url - string - required, the URL of an http or https remote endpoint
  • metadata - object - arbitrary metadata

Response

Status code:

  • 200 OK on success
  • 400 Bad Request on validation error
  • 404 Not Found if function doesn't exist

JSON object:

  • space - string - space name
  • functionId - string - function ID
  • provider - object - provider specific information about a function
  • metadata - object - arbitrary metadata

Delete Function

Delete all types of functions. This operation fails if the function is currently in-use by a subscription.

Endpoint

DELETE <Configuration API URL>/v1/spaces/<space>/functions/<function ID>

Response

Status code:

  • 204 No Content on success
  • 404 Not Found if function doesn't exist

List Functions

Endpoint

GET <Configuration API URL>/v1/spaces/<space>/functions

Query Parameters

Endpoint allows filtering list of returned object with filters passed as query parameters. Currently, filters can only use metadata properties e.g. metadata.service=usersService.

Response

Status code:

  • 200 OK on success

JSON object:

  • functions - array of object - functions:
    • space - string - space name
    • functionId - string - function ID
    • provider - object - provider specific information about a function
    • metadata - object - arbitrary metadata

Get Function

Endpoint

GET <Configuration API URL>/v1/spaces/<space>/functions/<function ID>

Response

Status code:

  • 200 OK on success
  • 404 Not Found if function doesn't exist

JSON object:

  • space - string - space name
  • functionId - string - function ID
  • provider - object - provider specific information about a function
  • metadata - object - arbitrary metadata

Subscriptions

Create Subscription

Endpoint

POST <Configuration API URL>/v1/spaces/<space>/subscriptions

Request

  • type - string - subscription type, sync or async
  • eventType - string - event type
  • functionId - string - ID of function to receive events
  • path - string - optional, URL path under which events (HTTP requests) are accepted, default: /
  • method - string - optional, HTTP method that accepts requests, default: POST
  • metadata - object - arbitrary metadata

Response

Status code:

  • 201 Created on success
  • 400 Bad Request on validation error
  • 409 Conflict if subscription already exists

JSON object:

  • space - string - space name
  • subscriptionId - string - subscription ID
  • type - string - subscription type
  • eventType - string - event type
  • functionId - function ID
  • method - string - HTTP method that accepts requests
  • path - string - path that accepts requests, starts with /
  • metadata - object - arbitrary metadata

Update Subscription

Endpoint

PUT <Configuration API URL>/v1/spaces/<space>/subscriptions/<subscription ID>

Request

Note that type, eventType, functionId, path, and method may not be updated in an UpdateSubscription call.

  • type - string - subscription type, sync or async
  • eventType - string - event type
  • functionId - string - ID of function to receive events
  • path - string - optional, URL path under which events (HTTP requests) are accepted, default: /
  • method - string - optional, HTTP method that accepts requests, default: POST
  • metadata - object - arbitrary metadata

Response

Status code:

  • 200 Created on success
  • 400 Bad Request on validation error
  • 404 Not Found if subscription doesn't exist

JSON object:

  • space - string - space name
  • subscriptionId - string - subscription ID
  • type - string - subscription type
  • eventType - string - event type
  • functionId - function ID
  • method - string - HTTP method that accepts requests
  • path - string - path that accepts requests, starts with /
  • metadata - object - arbitrary metadata

Delete Subscription

Endpoint

DELETE <Configuration API URL>/v1/spaces/<space>/subscriptions/<subscription ID>

Response

Status code:

  • 204 No Content on success
  • 404 Not Found if subscription doesn't exist

List Subscriptions

Endpoint

GET <Configuration API URL>/v1/spaces/<space>/subscriptions

Query Parameters

Endpoint allows filtering list of returned object with filters passed as query parameters. Currently, filters can only use metadata properties e.g. metadata.service=usersService.

Response

Status code:

  • 200 OK on success

JSON object:

  • subscriptions - array of object - subscriptions
    • space - string - space name
    • subscriptionId - string - subscription ID
    • type - string - subscription type
    • eventType - string - event type
    • functionId - function ID
    • method - string - HTTP method that accepts requests
    • path - string - path that accepts requests, starts with /
    • metadata - object - arbitrary metadata

Get Subscription

Endpoint

GET <Configuration API URL>/v1/spaces/<space>/subscriptions/<subscription ID>

Response

Status code:

  • 200 OK on success
  • 404 NotFound if subscription doesn't exist

JSON object:

  • space - string - space name
  • subscriptionId - string - subscription ID
  • type - string - subscription type
  • eventType - string - event type
  • functionId - function ID
  • method - string - HTTP method that accepts requests
  • path - string - path that accepts requests, starts with /
  • metadata - object - arbitrary metadata

CORS

Create CORS Configuration

Endpoint

POST <Configuration API URL>/v1/spaces/<space>/cors

Request

  • method - string - endpoint method
  • path - string - endpoint path
  • allowedOrigins - array of string - list of allowed origins. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com), default: *
  • allowedMethods - array of string - list of allowed methods, default: HEAD, GET, POST
  • allowedHeaders - array of string - list of allowed headers, default: Origin, Accept, Content-Type
  • allowCredentials - bool - allow credentials, default: false
  • metadata - object - arbitrary metadata

Response

Status code:

  • 201 Created on success
  • 400 Bad Request on validation error
  • 409 Conflict if CORS configuration already exists

JSON object:

  • space - string - space name
  • corsId - string - CORS configuration ID
  • method - string - endpoint method
  • path - string - endpoint path
  • allowedOrigins - array of string - list of allowed origins
  • allowedMethods - array of string - list of allowed methods
  • allowedHeaders - array of string - list of allowed headers
  • allowCredentials - boolean - allow credentials
  • metadata - object - arbitrary metadata

Update CORS Configuration

Endpoint

PUT <Configuration API URL>/v1/spaces/<space>/cors/<CORS ID>

Request

Note that method, and path may not be updated in an UpdateCORS call.

  • method - string - endpoint method
  • path - string - endpoint path
  • allowedOrigins - array of string - list of allowed origins
  • allowedMethods - array of string - list of allowed methods
  • allowedHeaders - array of string - list of allowed headers
  • allowCredentials - boolean - allow credentials
  • metadata - object - arbitrary metadata

Response

Status code:

  • 200 Created on success
  • 400 Bad Request on validation error
  • 404 Not Found if CORS configuration doesn't exist

JSON object:

  • space - string - space name
  • corsId - string - CORS configuration ID
  • method - string - endpoint method
  • path - string - endpoint path
  • allowedOrigins - array of string - allowed origins
  • allowedMethods - array of string - allowed methods
  • allowedHeaders - array of string - allowed headers
  • allowCredentials - boolean - allow credentials
  • metadata - object - arbitrary metadata

Delete CORS Configuration

Endpoint

DELETE <Configuration API URL>/v1/spaces/<space>/cors/<CORS ID>

Response

Status code:

  • 204 No Content on success
  • 404 Not Found if CORS configuration doesn't exist

List CORS Configurations

Endpoint

GET <Configuration API URL>/v1/spaces/<space>/cors

Query Parameters

Endpoint allows filtering list of returned object with filters passed as query parameters. Currently, filters can only use metadata properties e.g. metadata.service=usersService.

Response

Status code:

  • 200 OK on success

JSON object:

  • cors - array of object - CORS configurations
    • space - string - space name
    • corsId - string - CORS configuration ID
    • method - string - endpoint method
    • path - string - endpoint path
    • allowedOrigins - array of string - allowed origins
    • allowedMethods - array of string - allowed methods
    • allowedHeaders - array of string - allowed headers
    • allowCredentials - boolean - allow credentials
    • metadata - object - arbitrary metadata

Get CORS Configuration

Endpoint

GET <Configuration API URL>/v1/spaces/<space>/cors/<CORS ID>

Response

Status code:

  • 200 OK on success
  • 404 NotFound if CORS configuration doesn't exist

JSON object:

  • space - string - space name
  • corsId - string - CORS configuration ID
  • method - string - endpoint method
  • path - string - endpoint path
  • allowedOrigins - array of string - allowed origins
  • allowedMethods - array of string - allowed methods
  • allowedHeaders - array of string - allowed headers
  • allowCredentials - boolean - allow credentials
  • metadata - object - arbitrary metadata

Prometheus Metrics

Endpoint exposing Prometheus metrics.

Endpoint

GET <Configuration API URL>/metrics

Status

Dummy endpoint (always returning 200 OK status code) for checking if the event gateway instance is running.

Endpoint

GET <Configuration API URL>/v1/status


Architecture (`docs/architecture.md`)

Architecture

text
/* Detailed source-code truncated for AI context efficiency. */

Clustering (`docs/clustering.md`)

Clustering

The Event Gateway is a horizontally scalable system. It can be scaled by adding instances to the cluster. A cluster is
a group of instances sharing the same database. A cluster can be created in one cloud region, across multiple regions,
across multiple cloud provider or even in both cloud and on-premise data centers.

The Event Gateway instances use a strongly consistent, subscribable DB (initially etcd,
with support for Consul, and Zookeeper planned) to store and broadcast configuration. The instances locally
cache configuration used to drive low-latency event routing. The instance local cache is built asynchronously based on
events from backing DB.

The Event Gateway is a stateless service and there is no direct communication between different instances. All
configuration data is shared using backing DB. If the instance from region 1 needs to call a function from region 2 the
invocation is not routed through the instance in region 2. The instance from region 1 invokes the function from region 2
directly.

text
/* Detailed source-code truncated for AI context efficiency. */

Developing (`docs/developing.md`)

Developing

Clone GitHub repo

text
mkdir -p $GOPATH/src/github.com/serverless
cd $GOPATH/src/github.com/serverless
git clone [email protected]:serverless/event-gateway.git
cd event-gateway

Install [`dep`](https://github.com/golang/dep) package manager

On macOS you can install or upgrade to the latest released version with Homebrew:

sh
$ brew install dep
$ brew upgrade dep

Or you can install via go get:

sh
go get -u github.com/golang/dep/cmd/dep

Install dependencies

sh
go get -u github.com/hashicorp/go-plugin
go get -u github.com/hashicorp/go-hclog
go get -u golang.org/x/net/{context,http2,trace}
dep ensure

Run locally

sh
go run cmd/event-gateway/main.go -dev

Event Types (`docs/event-types.md`)

Event Types

Event Registry is a single source of truth about events occuring in the space. Every event emitted to a space has to have event
type registered beforehand. Event type can be registered using Event Types Configuration API.

Authorization

Event Type can define authorizer function that will be called before calling a subscribed function. Authorizer function is a function registered in Event Gateway beforehand.

Invocation Payload

The authorizer function is invoked with a special payload. The function has access to the whole request object because different parts of the request can be required for running authorization logic (e.g. API key can be stored in different headers or query params). The invocation payload is a JSON object with the following structure:

  • event - object - event received and parsed by EG
  • request - object - original HTTP request to the EG, this field is exactly the same as HTTP event, including body, which in case of CloudEvent will be exactly the same as event field

Invocation Result

The authorize function is expected to return authorization response JSON object with the following structure:

  • authorization - object - object containing authorization data, required if authorization is successful. Fields:
    • principalId - string - required if authorization is successful, the principal user identification associated with the token sent in the request
    • context - map[string]string - arbitrary data that will be accessible by the downstream function
  • error - object - error information, required if authorization is unsuccessful. Fields:
    • message - string - authorization error message

If the authorization is successful error field has to be null or not defined otherwise Event Gateway treats authorization process as unsuccessful and 403 Forbidden error is returned to the client assuming that there was sync subscription defined. authorization object is attached to CloudEvent Extensions field under eventgateway.authorization key.

If Event Gateway will not be able to parse the response from authorizer function or there will be error during invocation authorization process is considered as unsuccessful.


Metrics (`docs/metrics.md`)

Collecting and Viewing Event Gateway Metrics

This guide details how to collect and analyze metrics from the Event Gateway.

Introduction

The Event Gateway exposes a number of Prometheus-based metrics + counters via the configuration
API to help monitor the health of the gateway.

Contents

  1. Collecting Metrics
  2. Visualizing Data
    1. Prometheus
    2. InfluxDB
  3. List of Metrics
    1. Events API
    2. Configuration API

Collecting Metrics

Event Gateway metrics are exposed via a Prometheus text-based metric exporter and as such are
queryable by a variety of modern toolsets. While you can scrape data directly from your prometheus database, the following (non-exhaustive) list of sources can also
be configured to scrape Event Gateway metrics:

Visualizing Data

Included in the contrib folder are identical dashboards to visualize Event Gateway metrics with different
data sources. Depending on your setup you'll find a Prometheus and InfluxDB version
of the dashboard.

Importing a Grafana dashboard is relatively straightforward provided your datasources are configured
properly within your Grafana instance. Each of our dashboards lists respective datasources as a template variable, assuming the default datasource is set
to the type for your datasource; in other words, if you import the Prometheus dashboard the template will expect your default data source to be set
to Prometheus.

If your default datasource is not either of the Prometheus or InfluxDB sources, fear not! The template variable is set as the datasource for each
panel within the dashboard, so updating the datasource is as easy as choosing another option from the template dropdown.

NOTE: There are two dashboards included for each datasource: an aggregated dashboard for all spaces in a given Event Gateway deployment, and a second
dashboard that can drill down to individual spaces.

Prometheus

You can find the source for the Prometheus dashboard here.

InfluxDB

You can find the source for the InfluxDB dashboard here.

List of Metrics

Both Events and Configuration API exposes Prometheus metrics. The metrics are accesible via /v1/metrics endpoint of Configuration API. The table below outlines the specific metrics available to query from the endpoint:

Events API

Metric Type Labels Description
eventgateway_events_received_total counter space, type total of events received
eventgateway_events_processed_total counter space, type total of processed events
eventgateway_events_dropped_total counter space, type total of events dropped due to insufficient processing power
eventgateway_events_backlog gauge gauge of asynchronous events count waiting to be processed
eventgateway_events_custom_processing_seconds histogram bucketed histogram of processing duration of an event (from receiving the async custom event to calling a function)

Labels

  • space - space name
  • type - event type name

Configuration API

Metric Type Labels Description
eventgateway_eventtypes_total gauge space gauge of registered event types count
eventgateway_functions_total gauge space gauge of registered functions count
eventgateway_subscriptions_total gauge space gauge of created subscriptions count
eventgateway_config_requests_total counter space, resource, operation total of Config API requests
eventgateway_config_request_duration_seconds histogram bucketed histogram of request duration of Config API requests

Labels

  • space - space name
  • resource - Configuration API resource, possible values: eventtype, function or subscription
  • operation - Configuration API operation, possible values: create, get, delete, list, update

Reliability Guarantees (`docs/reliability-guarantees.md`)

Reliability Guarantees

Events are not durable

The event received by Event Gateway is stored only in memory, it's not persisted to disk before processing. This means that in case of hardware failure or software crash the event may not be delivered to the subscriber. For a synchronous subscription (http) it can manifest as error message returned to the requester. For asynchronous custom event with multiple subscribers it means that the event may not be delivered to all of the subscribers.

Events are delivered _at most once_

Event Gateway attempts delivery fulfillment for an event only once and consequently any event received successfully by the Event Gateway is guaranteed to be received by the subscriber at most once. That said, the nature of Event Gateway provider implementation could result in retries under specific circumstances, but these should not cause delivering the same event multiple times. For example, Providers for AWS Services that use the AWS SDK are subject to auto retry logic that's built into the SDK (AWS documentation on API retries).

AWS Lambda provider uses RequestResponse invocation type which means that retry logic for asynchronous AWS events doesn't apply here. Among others it means, that failed deliveries of custom events are not sent to DLQ. Please find more information in Understanding Retry Behavior, "Synchronous invocation" section.


Running Locally (`docs/running-locally.md`)

Running Locally

Installation (via GitHub releases)

On macOS or Linux run the following:

text
curl -sfL https://raw.githubusercontent.com/serverless/event-gateway/master/install.sh | sh

On Windows download binary.

Installation (via Docker)

There is a official Docker image.

text
docker pull serverless/event-gateway

Running Locally

Run event-gateway in dev mode:

text
event-gateway -dev

Running in Docker

text
docker run -p 4000:4000 -p 4001:4001 serverless/event-gateway -dev

Pass in your AWS credentials

Mounts the ~/.aws folder from the host to the /home/.aws folder inside the container. Event Gateway can then read the credentials from within the container.

text
docker run -p 4000:4000 -p 4001:4001 -e "HOME=/home" -v ~/.aws:/home/.aws serverless/event-gateway -dev

Preserve state of etcd

While testing if you restart the container running Event Gateway and want to preserve the data in etcd, you can specify a data dir with the -embed-data-dir "/home/data" flag specifying a destination folder. Then you can mount the folder ~/.event-gateway/data from your host into the container at /home/data. Event Gateway will read the data from there.

text
docker run -p 4000:4000 -p 4001:4001 -v ~/.event-gateway/data:/home/data serverless/event-gateway -embed-data-dir "/home/data" -dev

Operations

Register a Function

Register an AWS Lambda function in the Function Discovery.

text
curl --request POST \
  --url http://127.0.0.1:4001/v1/spaces/default/functions \
  --header 'content-type: application/json' \
  --data '{"functionId": "hello", "provider":{"type": "awslambda", "arn": "<Function AWS ARN>", "region": "<Region>", "accessKeyId": "<Access Key ID>", "secretAccessKey": "<Secret Access Key>"}}'

Subscribe to an Event

Once the function is register you can subscribe it to you custom event.

text
curl --request POST \
  --url http://127.0.0.1:4001/v1/spaces/default/subscriptions \
  --header 'content-type: application/json' \
  --data '{"functionId": "hello", "event": "pageVisited"}'

Emit an Event

An event can be emitted using Events API.

text
curl --request POST \
  --url http://127.0.0.1:4000/ \
  --header 'content-type: application/json' \
  --header 'event: pageVisited' \
  --data '{"userId": "123"}'

After emitting the event subscribed function is called asynchronously.


Storage (`docs/storage.md`)

Implications of Storage Semantics

For configuration, this system has this desired behavior:

  1. something wants to configure something
  2. everything that needs to know about that configuration reacts to it

We benefit from strongly consistent atomic writes because we want all configuration to
reach everything that needs to be configured. Weakly consistent systems
may not observe all updates, and we will end up with broken or half-working
features. We also benefit from efficient notification primitives for
keeping the overall system performant, such as watches. Without watches,
we have to continuously scan ranges of keys in the database until we
find new data, which is enormously expensive, slow, and hard to scale.

Applied to security, where we want to revoke a user's privileges completely,
if we are using a database without the ability to perform atomic writes,
even if it is strongly consistent,
then one component may try updating a user's account by first reading
their data, modifying it locally, then trying to update it in the database.
If we don't have atomic updates, the ordering could end up being this:

text
node A reads user Bob's data
node A locally updates Bob's data to add admin privileges
node B deletes Bob's data to remove all privileges
node B receives "write successful" from database
node A writes Bob's new data to the system with admin privileges
Node B then tells the system that was trying to delete Bob "Bob sucessfully deleted"
Bob steals all of the company's secrets

This is not desirable. However, some strongly consistent databases let us
perform compare-and-swap operations that let us atomically update Bob, without
losing any intermediate updates.

text
node A reads user Bob's data
node A locally updates Bob's data to add admin privileges
node B deletes Bob's data to remove all privileges
node B receives "write successful" from database
node A tries to do "update Bob unless changed since read" which fails
Node B then tells the system that was trying to delete Bob "Bob sucessfully deleted"
Bob is locked out and steals nothing

Atomic updates generally significantly reduce the cognitive burden for
building a system which must store and react to state changes in different
components. Watches are another significant help for this, as they
notify interested systems in relevant changes.

Watches are how we apply the event-driven model to our database.
When interesting changes happen, interested parties react to them.
This decouples the emitter from the reactor, greatly simplifying
interactions in the system. Without watches, we need to
have some way of detecting changes in a database. If we can scan through
all keys, we can do an O(N) traversal of the entire database, which
does not scale very high, but could work alright. If we don't have
the ability to scan through all data, we may actually have no way of
learning what changes are unless there is a top-level key that is set
that holds everything. This does not scale beyond a couple kilobytes.

Storage Options

stateless gateway services backed by zk/etcd/consul cluster, abstracted by docker/libkv

pros

  • flexible support for the three most popular configuration databases
  • very clear operational characteristics, does not confuse anyone about what's happening
  • the gateway is fully stateless, easy to autoscale, clear semantics for operators
  • lowest amount of work for us
  • write once, run anywhere in the same way
  • allows users to easily take advantage of existing database skills, tools, backup tools, monitoring, etc...

cons

  • requires users to run their own cluster (they are already running things though, so this isn't a high marginal cost)

embedded etcd in gateway, cluster of 3 or 5 active as "leaders", rest of gateways are stateless

pros

  • single binary

cons

  • unclear operational characteristics, when the cluster gets wedged it may be extremely hard to debug
  • harder than running your own cluster, because you can't reuse existing database skills
  • very hard on operators when things go wrong
  • harder on operators to get things safely set up
  • still effectively have a separate cluster if you want to autoscale without accidentally losing leaders
  • creating a reliable "autopilot" etcd deployment system took tyler 4 months in the past

embedded etcd in gateway, single node configured as "leader", rest of gateways are stateless

pros

  • easy to set up
  • can be used in combination with a separate etcd cluster for the best of both worlds
  • does not require setting up a cluster to try out, demo, or run in small deployments
  • clear operational characteristics, everyone knows it's not reliable

cons

  • single point of failure on the single leader node

dynamo + spanner + cosmosdb + on prem other databases

pros

  • single binary
  • easy deployment for users on cloud providers
  • one fewer piece to keep running

cons

  • dramatically increases the complexity for building the system for us
  • we need to target multiple consistency models (hard+++)
  • need to target systems without watch semantics (hard+++)
  • need to target systems without atomic write semantics (hard+++)
  • we need to spend much more effort on testing
  • we need to spend much more effort on fixing bugs
  • we need to spend much more effort on writing monitoring code
  • we need to master all of these databases in order to program against them

eventually consistent gossip-based config sharing with CRDTs

pros

  • single binary
  • no external dependencies, even on databases

cons

  • if all instances go away, the configuration is gone
  • we're basically building our own distributed database (hard+++++)
  • need to create backup tooling
  • unclear operational characteristics for people
  • huge extra effort needs to go into correctness testing

just config files, reloaded on file change

pros

  • single binary
  • flexible
  • can be used with any of the other approaches to decouple functionality

cons

  • users don't get an API for the gateway

Recommendation

Keep the system stateless to make it easy to operate. Store state
in a database that supports atomic updates and watches, such as
etcd, zookeeper, or consul. Use the docker/libkv library to
support all 3 backing databases. This lets us treat every
deployment environment the same way, regardless of whether
it's in a cloud provider or on-prem. This combination of
operational clarity and database semantics will significantly
lower the amount of effort we need to put into engineering over time.

Purely for demo and trial purposes, allow a gateway to be started
with an --embedded-master flag which will start an embedded etcd instance
that other gateways can use as a shared backing store. This allows people to
try out the system without standing up an etcd cluster first.

The main downside, having to run a separate cluster in production, is probably
not a big deal for people who are interested in running this themselves.

Assumptions relating to this cost, and it not seeming very high:

  1. most users will fall into these camps:
  • small/medium orgs interested in trying out locally but using the SaaS offering
  • orgs with more significant engineering resources who already run zk/etcd/consul
  • orgs with more significant engineering resources who are not averse to running zk/etcd/consul
  • orgs who are comfortable paying compose.com $30/mo for hosted etcd
  1. users who are not interested in paying for SaaS but still want to run the gateway
    themselves WITHOUT running a database are unlikely to be very upset when they occasionally
    need to reconfigure the gateway when the single master goes away.

Subscription Types (`docs/subscription-types.md`)

Subscription Types

Event Gateway supports two subscription types: async and sync.

`async` subscription

async subscription implements lightweight pub/sub system. There can be many async subscriptions listening to the same event type, on the same path and HTTP method. The function is asynchronously invoked by the Event Gateway.

`sync` subscription

In case of sync subscription invoked function can control the HTTP response returned by the Event Gateway. Because of that, there can be only one sync subscription for a path, HTTP method, and event type tuple.

Status code, headers and response body can be controlled by returning JSON object with the following fields:

  • statusCode - int - response status code, default: 200
  • headers - object - response headers
  • body - string - response body. Currently, the Event Gateway supports only string responses.

If the function invocation failed or the backing function didn't return JSON object in the above format Event Gateway returns 500 Internal Server Error.

Path parameters

The Event Gateway allows creating sync subscription with parameterized paths. Every path segment prefixed with : is
treated as a parameter, e.g. /users/:id.

The Event Gateway prevents from creating subscriptions in following conflicting situations:

  • registering static path when there is parameterized path registered already (/users/:id vs. /users/foo)
  • registering parameterized path with different parameter name (/users/:id vs. /users/:name)

Key and value of matched parameters are passed to a function in an HTTP Request Event under params field.

Wildcard parameters

Special type of path parameter is wildcard parameter. It's a path segment prefixed with *. Wildcard parameter can only
be specified at the end of the path and will match every character till the end of the path. For examples
parameter /users/*userpath for request path /users/group1/user1 will match group1/user1 as a userpath parameter.


System Events And Plugin System (`docs/system-events-and-plugin-system.md`)

System Events and Plugin System

System Events

System Events are special type of events emitted by the Event Gateway instance internally. They are emitted on each stage
of event processing flow starting from receiving event to function invocation end. Those events are:

  • eventgateway.event.received - the event is emitted when an event was received by Events API. Data fields:
    • event - event payload
    • path - Events API path
    • headers - HTTP request headers
  • eventgateway.function.invoking - the event emitted before invoking a function. Data fields:
    • space - space name
    • event - event payload
    • functionId - registered function ID
  • eventgateway.function.invoked - the event emitted after successful function invocation. Data fields:
    • space - space name
    • event - event payload
    • functionId - registered function ID
    • result - function response
  • eventgateway.function.invocationFailed - the event emitted after failed function invocation. Data fields:
    • space - space name
    • event - event payload
    • functionId - registered function ID
    • error - invocation error

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. 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.


Openapi Openapi Config Api.Yaml (`docs/openapi/openapi-config-api.yaml`)

openapi: "3.0.1"

info:
title: Event Gateway - Configuration API
version: "0.8.1"
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
contact:
name: Serverless, Inc.
url: http://www.serverless.com

servers:

tags:

  • name: "event type"

  • name: "function"

  • name: "subscription"

  • name: "cors"
    paths:
    /spaces/{spaceName}/eventtypes:
    summary: "Operations about event types"
    get:
    summary: "List event types"
    tags:
    - "event type"
    operationId: "ListEventTypes"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/Filters"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/EventTypes"
    500:
    $ref: '#/components/responses/Error'

    post:
    summary: "Create event type"
    tags:
    - "event type"
    operationId: "CreateEventType"
    parameters:
    - $ref: "#/components/parameters/Space"
    requestBody:
    $ref: "#/components/requestBodies/CreateEventType"
    responses:
    201:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/EventType"
    400:
    $ref: '#/components/responses/ValidationError'
    500:
    $ref: '#/components/responses/Error'

    /spaces/{spaceName}/eventtypes/{eventTypeName}:
    summary: "Operations about single event type"
    get:
    summary: "Get event type"
    tags:
    - "event type"
    operationId: "GetEventType"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/EventTypeName"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/EventType"
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

    put:
    summary: "Update event type"
    tags:
    - "event type"
    operationId: "UpdateEventType"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/EventTypeName"
    requestBody:
    $ref: "#/components/requestBodies/UpdateEventType"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/EventType"
    400:
    $ref: '#/components/responses/ValidationError'
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

    delete:
    summary: "Delete event type"
    tags:
    - "event type"
    operationId: "DeleteEventType"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/EventTypeName"
    responses:
    204:
    400:
    $ref: '#/components/responses/EventTypeHasSubscriptionsError'
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

    /spaces/{spaceName}/functions:
    summary: "Operations about functions"
    get:
    summary: "List functions"
    tags:
    - "function"
    operationId: "ListFunctions"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/Filters"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/Functions"
    500:
    $ref: '#/components/responses/Error'

    post:
    summary: "Register function"
    tags:
    - "function"
    operationId: "CreateFunction"
    parameters:
    - $ref: "#/components/parameters/Space"
    requestBody:
    $ref: "#/components/requestBodies/CreateFunction"
    responses:
    201:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/Function"
    400:
    $ref: '#/components/responses/ValidationError'
    500:
    $ref: '#/components/responses/Error'

    /spaces/{spaceName}/functions/{functionId}:
    summary: "Operations about single function"
    get:
    summary: "Get function"
    tags:
    - "function"
    operationId: "GetFunction"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/FunctionID"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/Function"
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

    put:
    summary: "Update function"
    tags:
    - "function"
    operationId: "UpdateFunction"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/FunctionID"
    requestBody:
    $ref: "#/components/requestBodies/UpdateFunction"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/Function"
    400:
    $ref: '#/components/responses/ValidationError'
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

    delete:
    summary: "Delete function"
    tags:
    - "function"
    operationId: "DeleteFunction"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/FunctionID"
    responses:
    204:
    400:
    $ref: '#/components/responses/FunctionHasSubscriptionsError'
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

    /spaces/{spaceName}/subscriptions:
    summary: "Operations about subscriptions"
    get:
    summary: "List subscriptions"
    tags:
    - "subscription"
    operationId: "ListSubscriptions"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/Filters"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/Subscriptions"
    500:
    $ref: '#/components/responses/Error'

    post:
    summary: "Create subscription"
    tags:
    - "subscription"
    operationId: "CreateSubscription"
    parameters:
    - $ref: "#/components/parameters/Space"
    requestBody:
    $ref: "#/components/requestBodies/CreateSubscription"
    responses:
    201:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/Subscription"
    400:
    $ref: '#/components/responses/ValidationError'
    500:
    $ref: '#/components/responses/Error'

    /spaces/{spaceName}/subscription/{subscriptionId}:
    summary: "Operations about single subscription"
    get:
    summary: "Get subscription"
    tags:
    - "subscription"
    operationId: "GetSubscription"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/SubscriptionID"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/Subscription"
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

    put:
    summary: "Update subscription"
    tags:
    - "subscription"
    operationId: "UpdateSubscription"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/SubscriptionID"
    requestBody:
    $ref: "#/components/requestBodies/UpdateSubscription"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/Subscription"
    400:
    $ref: '#/components/responses/ValidationError'
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

    delete:
    summary: "Delete subscription"
    tags:
    - "subscription"
    operationId: "DeleteSubscription"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/SubscriptionID"
    responses:
    204:
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

    /spaces/{spaceName}/cors:
    summary: "Operations about CORS configuration"
    get:
    summary: "List CORS configurations"
    tags:
    - "cors"
    operationId: "ListCORS"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/Filters"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/CORSes"
    500:
    $ref: '#/components/responses/Error'

    post:
    summary: "Create CORS configuration"
    tags:
    - "cors"
    operationId: "CreateCORS"
    parameters:
    - $ref: "#/components/parameters/Space"
    requestBody:
    $ref: "#/components/requestBodies/CreateCORS"
    responses:
    201:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/CORS"
    400:
    $ref: '#/components/responses/ValidationError'
    500:
    $ref: '#/components/responses/Error'

    /spaces/{spaceName}/cors/{corsId}:
    summary: "Operations about single CORS configuration"
    get:
    summary: "Get CORS configuration"
    tags:
    - "cors"
    operationId: "GetCORS"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/CORSID"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/CORS"
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

    put:
    summary: "Update CORS configuration"
    tags:
    - "cors"
    operationId: "UpdateCORS"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/CORSID"
    requestBody:
    $ref: "#/components/requestBodies/UpdateCORS"
    responses:
    200:
    content:
    application/json:
    schema:
    $ref: "#/components/schemas/CORS"
    400:
    $ref: '#/components/responses/ValidationError'
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

    delete:
    summary: "Delete CORS configuration"
    tags:
    - "cors"
    operationId: "DeleteCORS"
    parameters:
    - $ref: "#/components/parameters/Space"
    - $ref: "#/components/parameters/CORSID"
    responses:
    204:
    404:
    $ref: '#/components/responses/NotFoundError'
    500:
    $ref: '#/components/responses/Error'

components:
schemas:
SpaceName:
type: string
FunctionID:
type: string
SubscriptionID:
type: string
CORSID:
type: string
SubscriptionType:
type: string
enum:
- async
- sync
ProviderType:
type: string
enum:
- awsfirehose
- awskinesis
- awslambda
- awssqs
- http
Provider:
type: object
oneOf:
- $ref: '#/components/schemas/AWSFirehose'
- $ref: '#/components/schemas/AWSKinesis'
- $ref: '#/components/schemas/AWSLambda'
- $ref: '#/components/schemas/AWSSQS'
- $ref: '#/components/schemas/HTTP'
EventType:
type: object
properties:
space:
$ref: '#/components/schemas/SpaceName'
name:
$ref: '#/components/schemas/EventTypeName'
authorizerId:
$ref: '#/components/schemas/FunctionID'
Function:
type: object
properties:
space:
$ref: '#/components/schemas/SpaceName'
functionId:
$ref: '#/components/schemas/FunctionID'
type:
$ref: '#/components/schemas/ProviderType'
provider:
$ref: '#/components/schemas/Provider'
EventTypes:
type: object
properties:
eventTypes:
type: array
items:
$ref: '#/components/schemas/EventType'
Functions:
type: object
properties:
functions:
type: array
items:
$ref: '#/components/schemas/Function'
Subscription:
type: object
properties:
space:
$ref: '#/components/schemas/SpaceName'
subscriptionId:
$ref: '#/components/schemas/SubscriptionID'
type:
$ref: '#/components/schemas/SubscriptionType'
eventType:
$ref: '#/components/schemas/EventType'
functionId:
$ref: '#/components/schemas/FunctionID'
path:
$ref: '#/components/schemas/Path'
method:
$ref: '#/components/schemas/Method'
Subscriptions:
type: object
properties:
subscriptions:
type: array
items:
$ref: '#/components/schemas/Subscription'
CORS:
type: object
properties:
space:
$ref: '#/components/schemas/SpaceName'
corsId:
$ref: '#/components/schemas/CORSID'
method:
$ref: '#/components/schemas/Method'
path:
$ref: '#/components/schemas/Path'
allowedOrigins:
$ref: '#/components/schemas/AllowedOrigins'
allowedMethods:
$ref: '#/components/schemas/AllowedMethods'
allowedHeaders:
$ref: '#/components/schemas/AllowedHeaders'
allowCredentials:
$ref: '#/components/schemas/AllowCredentials'
CORSes:
type: object
properties:
cors:
type: array
items:
$ref: '#/components/schemas/CORS'
AWSFirehose:
type: object
properties:
deliveryStreamName:
$ref: '#/components/schemas/DeliveryStreamName'
region:
$ref: '#/components/schemas/Region'
awsAccessKeyId:
$ref: '#/components/schemas/AWSAccessKeyId'
awsSecretAccessKey:
$ref: '#/components/schemas/AWSSecretAccessKey'
awsSessionToken:
$ref: '#/components/schemas/AWSSessionToken'
AWSKinesis:
type: object
properties:
streamName:
$ref: '#/components/schemas/StreamName'
region:
$ref: '#/components/schemas/Region'
awsAccessKeyId:
$ref: '#/components/schemas/AWSAccessKeyId'
awsSecretAccessKey:
$ref: '#/components/schemas/AWSSecretAccessKey'
awsSessionToken:
$ref: '#/components/schemas/AWSSessionToken'
AWSLambda:
type: object
properties:
arn:
$ref: '#/components/schemas/ARN'
region:
$ref: '#/components/schemas/Region'
awsAccessKeyId:
$ref: '#/components/schemas/AWSAccessKeyId'
awsSecretAccessKey:
$ref: '#/components/schemas/AWSSecretAccessKey'
awsSessionToken:
$ref: '#/components/schemas/AWSSessionToken'
AWSSQS:
type: object
properties:
queueUrl:
$ref: '#/components/schemas/QueueURL'
region:
$ref: '#/components/schemas/Region'
awsAccessKeyId:
$ref: '#/components/schemas/AWSAccessKeyId'
awsSecretAccessKey:
$ref: '#/components/schemas/AWSSecretAccessKey'
awsSessionToken:
$ref: '#/components/schemas/AWSSessionToken'
HTTP:
type: object
properties:
url:
$ref: '#/components/schemas/URL'
ARN:
type: string
Region:
type: string
AWSAccessKeyId:
type: string
AWSSecretAccessKey:
type: string
AWSSessionToken:
type: string
StreamName:
type: string
DeliveryStreamName:
type: string
QueueURL:
type: string
URL:
type: string
format: url
EventTypeName:
type: string
Method:
type: string
Path:
type: string
default: "/"
AllowedOrigins:
type: array
items:
type: string
AllowedMethods:
type: array
items:
type: string
AllowedHeaders:
type: array
items:
type: string
AllowCredentials:
type: boolean
Error:
type: object
properties:
message:
type: string
Errors:
type: object
properties:
errors:
type: array
items:
$ref: "#/components/schemas/Error"
parameters:
Space:
in: "path"
name: "spaceName"
required: true
schema:
$ref: "#/components/schemas/SpaceName"
EventTypeName:
in: "path"
name: "eventTypeName"
required: true
schema:
$ref: "#/components/schemas/EventTypeName"
FunctionID:
in: "path"
name: "functionId"
required: true
schema:
$ref: "#/components/schemas/FunctionID"
SubscriptionID:
in: "path"
name: "subscriptionId"
required: true
schema:
$ref: "#/components/schemas/SubscriptionID"
CORSID:
in: "path"
name: "corsId"
required: true
schema:
$ref: "#/components/schemas/CORSID"
Filters:
in: "query"
name: "filters"
style: form
example:
"metadata.service": usersService
schema:
type: object
additionalProperties:
type: string
requestBodies:
CreateEventType:
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
$ref: '#/components/schemas/EventTypeName'
authorizerId:
$ref: '#/components/schemas/FunctionID'
UpdateEventType:
content:
application/json:
schema:
type: object
properties:
authorizerId:
$ref: '#/components/schemas/FunctionID'
CreateFunction:
content:
application/json:
schema:
type: object
required:
- functionId
- type
- provider
properties:
functionId:
$ref: '#/components/schemas/FunctionID'
type:
$ref: '#/components/schemas/ProviderType'
provider:
$ref: '#/components/schemas/Provider'
UpdateFunction:
content:
application/json:
schema:
type: object
properties:
type:
$ref: '#/components/schemas/ProviderType'
provider:
$ref: '#/components/schemas/Provider'
CreateSubscription:
content:
application/json:
schema:
type: object
required:
- type
- eventType
- functionId
properties:
type:
$ref: '#/components/schemas/SubscriptionType'
eventType:
$ref: '#/components/schemas/EventType'
functionId:
$ref: '#/components/schemas/FunctionID'
path:
$ref: '#/components/schemas/Path'
method:
$ref: '#/components/schemas/Method'
UpdateSubscription:
content:
application/json:
schema:
type: object
properties:
type:
$ref: '#/components/schemas/SubscriptionType'
eventType:
$ref: '#/components/schemas/EventType'
functionId:
$ref: '#/components/schemas/FunctionID'
path:
$ref: '#/components/schemas/Path'
method:
$ref: '#/components/schemas/Method'
CreateCORS:
content:
application/json:
schema:
type: object
properties:
method:
$ref: '#/components/schemas/Method'
path:
$ref: '#/components/schemas/Path'
allowedOrigins:
$ref: '#/components/schemas/AllowedOrigins'
allowedMethods:
$ref: '#/components/schemas/AllowedMethods'
allowedHeaders:
$ref: '#/components/schemas/AllowedHeaders'
allowCredentials:
$ref: '#/components/schemas/AllowCredentials'
UpdateCORS:
content:
application/json:
schema:
type: object
properties:
method:
$ref: '#/components/schemas/Method'
path:
$ref: '#/components/schemas/Path'
allowedOrigins:
$ref: '#/components/schemas/AllowedOrigins'
allowedMethods:
$ref: '#/components/schemas/AllowedMethods'
allowedHeaders:
$ref: '#/components/schemas/AllowedHeaders'
allowCredentials:
$ref: '#/components/schemas/AllowCredentials'
responses:
Error:
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
NotFoundError:
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
ValidationError:
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
FunctionHasSubscriptionsError:
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
EventTypeHasSubscriptionsError:
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'


--- METRICS ---