Skip to content
No description, website, or topics provided.
JavaScript Shell Dockerfile
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
dataManager
graphql
images
kubernetes
messageBroker
test
utilities
Dockerfile
LICENSE
README.md
config.js
docker-compose.yaml
docker-seed.sh
index.js
package.json

README.md

Seat Saver

Seat Saver is a project that demonstrates how to implement high volume message streaming under GraphQL.

Seat Saver in an GraphQL API that provides seat reservations services. The API exposes a set of types that describe seats in a venue, as shown in the figure below:

Seat Saver Data Model

The Seat Saver API allows developers to reserve and then buy a seat in within a given venue. Also, the Seat Saver API publishes a set of subscriptions that provides streams of messages according to events. These events are:

  • onSeatReserved
  • onSeatSold
  • onSeatReleased

NOTE: Seat Saver will automatically inject preliminary seed data into the datastore upon startup. This seed data describes 3 venues with each venue having a number of seats accordingly.

There are three ways to get the Seat Save project up and running. The first way is to use a Docker Compose installation on a single host machine. The Docker Compose installation will install an instance of MongoDB and Redis as containers on the host machine.

The second way is to run Seat Saver has a multi-instance distributed application in a Kubernetes cluster. To do this you will need to have created or identified a MongoDB database server (or server cluster) with an exposed endpoint URL. This endpoint URL is assigned to the application's environment variable MONGODB_URL

Also, you will need to have created or identified an Redis message broker server or server cluster that has an endpoint exposed to the internet. This endpoint is assigned to the application's environment variable MESSAGE_BROKER_HOST. Seat Saver will support the default port number for Redis, 6379 by default. Should you decide to use an alternate port, then you need to see this port number needs to be assigned to the application's environment variable, MESSAGE_BROKER_PORT.

The third way is to install Seat Saver as a standalone application on the host. This method requires configuring Seat Saver with references to running instances of MongoDB and Redis. These instances can be out on the internet or on an internal network. The references to MongoDB and Redis are defined by their URLs. Typically these URLs will contain username/password information.

These URLs need to be assigned to specific environment variables on the host machine(s) where Seat Saver is running.

General Architecture

Seat Saver is a GraphQL API written under Apollo Server. It relies upon access to a MongoDB database and a Redis PubSub Message broker. The MongoDB database is used to store data. The Redis message broker is the backing service that supports the physical message emission from the various subscriptions available via the Seat Saver subscription server.

General Architecture.

These backing services can exist anywhere, locally or on the internet, as long as the given service can be reached according to a URL.

Seat Saver Environment Variables

The environment variable for the MongoDB reference is:

MONGODB_URL, the URL that describes the location the MongoDB server, along with the username and password should those be required.

The environment variables for the Redis reference are:

MESSAGE_BROKER_HOST : The URL the defines the location of the Redis server message broker used by Seat-Saver

MESSAGE_BROKER_PORT (optional), the port number associated with the Redis server. The default value is 6379.

MESSAGE_BROKER_PASSWORD (optional), the password, should one be required by Redis

The sections that follow decribe how to setup Seat Saver as an application running under Docker Componse as well as running Seat Saver as a standalone application with references to MongoDB and Redis.

Also, there are sections that follow that provide examples for working with GraphQL queries, mutations and subscriptions in the Seat Saver API.

Running Seat Saver as a Docker Compose Application

(You can run this project under a Katabcoda Ubuntu Playground. Click here to go to the Ubuntu Playground.)

Step 1: Clone the source into your host machine.

git clone https://github.com/programmableweb/seat-saver

Step 2: Go to the project directory

cd seat-saver

Step 3: Spin up the docker-compose project

docker-compose up

Seat saver will be running on port 4000.

Seat-Saver 1

Running Seat Saver as a Kubernetes Cluster

You can use a minikube instance of the Katacoda interactive learning environment to install and run seat-saver as distributed, multi-instance application under Kubernetes. The following URL links to Katacoda's minikube playground.

https://katacoda.com/javajon/courses/kubernetes-fundamentals/minikube

Step 1: Clone the source into your host machine.

git clone https://github.com/programmableweb/seat-saver

Step 2: Go to the project directory

cd seat-saver

Step 3: Run the bash script that creates a Docker repository on the local machine, creates the required Docker images from source code and adds them to the local Docker repository.

sh docker-seed.sh

You will get output similar to the following:

Successfully tagged seatsaver:latest
The push refers to repository [localhost:5000/seatsaver]
9ff0dabcdea0: Pushed
6c5e43a0e84c: Pushed
129dd9cbb45d: Pushed
2faeaaebb113: Pushed
387bc77dd3f2: Pushed
df64d3292fd6: Pushed
beta: digest: sha256:ab15cd9bf6d5f9930c87fc40a373ad19da7f0651664f51ed28a1007af71c76e5 size: 1579
{"repositories":["seatsaver"]}

Step 4: Navigate to the kubernetes directory

cd kubernetes

Step 5: Adjust the Kubernetes secret manifest file, ./kubernetes/secrets.yaml by adding the location of and access information for the MongDB and Redis services.

vi secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: seatsaver-data
type: Opaque
data:
  mongodb_url: xxxxxxx
  message_broker_host: xxxxxxx
  message_broker_port: xxxxxxx
  message_broker_password: xxxxxx

Replace xxxxxx with the information particular to the environment variable.

Step 6: Run the bash script that creates the Kubernetes secret, deployment, and service for the Seat-Saver application.

sh k8s-gen.sh

You will get output as follows:

secret/seatsaver-data created
deployment.extensions/seatsaver created
service/seatsaver created

Step 7: The Kubernetes service you installed is of type: NodePort. Thus, Kubernetes will assign an arbitrary port number over 30000 that binds to the port number exposed by the pod running the Seat-Saver container. To discover the NodePort port number type:

kubectl get services

You'll get output similar to the following:

NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        11m
seatsaver    NodePort    10.102.138.23   <none>        80:32538/TCP   3m57s

In the case above, the NodePort port number is 32538 and it binds to the pod/container port 80. Your NodePort number will most likely be different.

Be advised! The Seat-Saver application seeds the MongoDB datasource you declare with seed data on first initialization. The ramification of this seeding is that it might take a little time for Seat-Saver to come online upon first startup. Subsequent startups of Seat-Saver will go much faster once the intial seeding has executed.

Step 8: Go to your web browser and access GraphQL Playground at the NodePort port number assigned by Kubernetes. If you are using Katacoda, go to Select port to view on Host 1 by clicking the plus sign (+) on the Katacoda menu bar and then entering the NodePort port number.

Katacoda access 1

Katacoda access 2

Step 9: You will be taken to the GraphQL Playground web page. Enter the following GraphQL query:

{
  venues{
    id
    seats{
      id
      number
      section
      status
    }
  }
}

This query will list all the venues and seats avaiable for reservation.

Katacoda access 3

You're now ready to exercise the Kubernetes installation of Seat Saver.

Running Seat Saver as a Standalone Application

(If you like, you can run this code standalone from within the Katacoda Ubuntu Playground found https://katacoda.com/courses/ubuntu/playground.)

Step 1: Clone the Seat Saver source code from the GitHub repository.

git clone https://github.com/programmableweb/seat-saver

Step 2: Set the required environment variables as described in the section above, Seat Saver Environment Variables.

export MONGODB_URL=<some_value>
export MESSAGE_BROKER_HOST=<some_value>
export MESSAGE_BROKER_PORT=<some_value_optional>
export MESSAGE_BROKER_PASSWORD=<some_value_optional>

Step 3: Navigate to the directory that has the Seat Saver source code.

cd seat-saver

Step 4: Install and run the application

npm install

node index.js

Step 5: Access the Seat Saver API via GraphQL Playground. To to your web browser and access the API via localhost:4000.

You're now ready to exercise the Seat Saver API via GraphQL Playground.

Working with the Seat Saver API

The following decribes the basic GraphQL types defined with the Seat Saver API.

Venue

    type Venue {
        id: ID
        name: String
        address: String
        city: String
        state_province: String
        postal_code: String
        country: String
        seats: [Seat]
    }

Seat

  type Seat {
        id:ID
        number: String!
        section: String!
        status: SeatStatus!
        customer: Customer
    }
    type Customer {
        firstName: String
        lastName: String
        email: String
    }

Subscriptions and Mutations

The following describes some queries and mutations you can use against the Seat Saver API.

Reserving a Seat

In order to make working with mutations against the Seat Saver API easier, you can use the following sample query variable.

{
  "seat": {
    "venueId": "5dce2cabba1d1d320106525a",
    "number": "A0",
    "customer": {
      "firstName": "Gonzalo",
  		"lastName": "Considine",
  		"email": "Gonzalo.Considine@dagmar.name" 
    }
  }
}

reserveSeat

The following GraphQL mutation will reserve a seat and also generate a SeatEvent message to the subscription, onSeatReserved.

mutation reserveSeat($seat: SeatInput!) {
  reserveSeat(seat: $seat) {
  	 id
    number
    section
    status 
  }
}

Output

{
  "data": {
    "reserveSeat": {
      "id": "5dce2cabba1d1d320106525b",
      "number": "A0",
      "section": "Section-A",
      "status": "RESERVED"
    }
  }
}

onSeatReserved

In a separate browser window, execute the following GraphQL Query Language code to bind to the subscription, onSeatReserved.

subscription onSeatReserved{
  onSeatReserved{
    venueId
    message
    number
    section
    status
    seatId  
  }
}

Sample Output

{
  "data": {
    "onSeatReserved": {
      "venueId": "5dce2cabba1d1d320106525a",
      "message": "Reserving Seat",
      "number": "A0",
      "section": null,
      "status": "RESERVED",
      "seatId": null
    }
  }
}

Buying a Seat

The following GraphQL mutation will buy a seat and also generate a SeatEvent message to the subscription, onSeatSold.

buySeat

mutation buySeat($seat: SeatInput!) {
  buySeat(seat: $seat) {
  	 id
    number
    section
    status 
  }
}

Sample Output

{
  "data": {
    "buySeat": {
      "id": "5dce2cabba1d1d320106525b",
      "number": "A0",
      "section": "Section-A",
      "status": "SOLD"
    }
  }
}

onSeatSold

In a separate browser window, execute the following GraphQL Query Language code to bind to the subscription, onSeatSold.

subscription onSeatSold{
  onSeatSold{
    venueId
    message
    number
    section
    status
    seatId
  }
}

Sample Output

{
  "data": {
    "onSeatSold": {
      "venueId": "5dce2cabba1d1d320106525a",
      "message": "Bought Seat",
      "number": "A0",
      "section": "Section-A",
      "status": "SOLD",
      "seatId": "5dce2cabba1d1d320106525b"
    }
  }
}

Releasing a Seat

The following GraphQL mutation will release a seat from either RESERVERED or SOLD stati and also generate a SeatEvent message to the subscription, onSeatReleased .

releaseSeat

mutation releaseSeat($seat: SeatInput!) {
  releaseSeat(seat: $seat) {
  	 id
    number
    section
    status 
  }
}

Output

{
  "data": {
    "releaseSeat": {
      "id": "5dce2cabba1d1d320106525b",
      "number": "A0",
      "section": "Section-A",
      "status": "OPEN"
    }
  }
}

onSeatReleased

In a separate browser window, execute the following GraphQL Query Language code to bind to the subscription, onSeatReleased .

subscription onSeatReleased{
  onSeatReleased{
    venueId
    message
    number
    section
    status
    seatId

  }
}

Output

{
  "data": {
    "onSeatReleased": {
      "venueId": "5dce2cabba1d1d320106525a",
      "message": "Released Seat",
      "number": "A0",
      "section": "Section-A",
      "status": "OPEN",
      "seatId": "5dce2cabba1d1d320106525b"
    }
  }
}
You can’t perform that action at this time.