Public | Automated Build

Last pushed: 2 months ago
Short Description
A sample Spring Boot RESTful API microservice, backed by MongoDB.
Full Description

Voter Service

Introduction

The Voter Spring Boot Service is a RESTful Web Service, backed by MongoDB. The Voter service exposes several HTTP API endpoints, listed below. API users can review a list candidates, submit a candidate, view voting results, and inspect technical information about the running service. API users can also create random voting data by calling the /voter/simulation endpoint.

The Voter service is designed to work along with the Candidate Service, as part of a complete API. The Voter service is dependent on the Candidate service to supply a list of candidates. The Candidate service is called by the Voter service, using one of three methods:

  1. HTTP-based Synchronous IPC, when either the Voter service's /voter/candidates/http/{election} or /voter/simulation/http/{election} endpoints are called.
  2. Messaging-based Remote Procedure Call (RPC) IPC, when either the Voter service's /voter/candidates/rpc/{election} or /voter/simulation/rpc/{election} endpoints are called.
  3. Messaging-based Eventual Consistency, when either the Voter service's /voter/candidates/db/{election} or /voter/simulation/db/{election} endpoints are called.

Quick Start for Local Development

The Voter service requires MongoDB to be running locally, on port 27017, RabbitMQ running on 5672 and 15672, and the Candidate service to be running on 8097. To clone, build, test, and run the Voter service as a JAR file, locally:

git clone --depth 1 --branch rabbitmq \
  https://github.com/garystafford/voter-service.git
cd voter-service
./gradlew clean cleanTest build
java -jar build/libs/voter-service-0.3.0.jar

Quick Start with Docker

The easiest way to run the Voter API services locally is using the docker-compose-local.yml file, included in the project. The Docker Compose file will spin up single container instances of the Election service, Voter service, Candidate service, RabbitMQ, MongoDB, and the Voter API Gateway.

sh ./stack-deploy-local.sh
CONTAINER ID        IMAGE                                     COMMAND                  CREATED             STATUS              PORTS                                                                                        NAMES
32d73282ff3d        garystafford/voter-api-gateway:latest     "/docker-entrypoin..."   8 seconds ago       Up 5 seconds        0.0.0.0:8080->8080/tcp                                                                       voterstack_voter-api-gateway_1
1ece438c5da4        garystafford/candidate-service:rabbitmq   "java -Dspring.pro..."   10 seconds ago      Up 7 seconds        0.0.0.0:8097->8097/tcp                                                                       voterstack_candidate_1
30391faa3422        garystafford/voter-service:rabbitmq       "java -Dspring.pro..."   10 seconds ago      Up 7 seconds        0.0.0.0:8099->8099/tcp                                                                       voterstack_voter_1
35063ccfe706        garystafford/election-service:rabbitmq    "java -Dspring.pro..."   12 seconds ago      Up 10 seconds       0.0.0.0:8095->8095/tcp                                                                       voterstack_election_1
23eae86967a2        rabbitmq:management-alpine                "docker-entrypoint..."   14 seconds ago      Up 11 seconds       4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp   voterstack_rabbitmq_1
7e77ddecddbb        mongo:latest                              "docker-entrypoint..."   24 seconds ago      Up 21 seconds       0.0.0.0:27017->27017/tcp                                                                     voterstack_mongodb_1

Getting Started with the API

The easiest way to get started with the Candidate and Voter services API, using HTTPie from the command line:

  1. Create sample candidates: http http://localhost:8097/candidate/simulation
  2. View sample candidates: http http://localhost:8097/candidate/candidates/summary/2016%20Presidential%20Election
  3. Create sample voter data: http http://localhost:8099/voter/simulation/http/2016%20Presidential%20Election
  4. View sample voter results: http http://localhost:8099/voter/results

Alternately, for step 3 above, you can use service-to-service RPC IPC with RabbitMQ, to retrieve the candidates:
http http://localhost:8099/voter/simulation/rpc/2016%20Presidential%20Election

Alternately, for step 3 above, you can use eventual consistency using RabbitMQ, to retrieve the candidates from MongoDB (assumes candidates are already be in MongoDB):
http http://localhost:8099/voter/simulation/db/2016%20Presidential%20Election

Voter Service Endpoints

The service uses a context path of /voter. All endpoints must be are prefixed with this sub-path.

Purpose Method Endpoint
Create Random Sample Data (using HTTP) GET /voter/simulation/http/{election}
Create Random Sample Data (using RPC Messaging) GET /voter/simulation/rpc/{election}
Create Random Sample Data (using Eventual Consistency) GET /voter/simulation/db/{election}
List Candidates (using HTTP) GET /voter/candidates/http/{election}
List Candidates (using RPC Messaging) GET /voter/candidates/rpc/{election}
List Candidates (using Eventual Consistency) GET /voter/candidates/db/{election}
Submit Vote POST /voter/votes
View Voting Results GET /voter/results/{election}
View Total Votes GET /voter/results/{election}/votes
View Winner(s) GET /voter/winners/{election}
View Winning Vote Count GET /voter/winners/{election}/votes
Service Info GET /voter/info
Service Health GET /voter/health
Service Metrics GET /voter/metrics
Other Spring Actuator endpoints GET /actuator, /mappings, /env, /configprops, etc.
Other HATEOAS endpoints for /voter/votes Various DELETE, PATCH, PUT, page sort, size, etc.

The HAL Browser API browser for the hal+json media type is installed alongside the service. It can be accessed at http://localhost:8099/voter/actuator/.

Voting

Submitting a new candidate requires an HTTP POST request to the /voter/votes endpoint, as follows:

HTTPie

http POST http://localhost:8099/voter/votes \
  candidate="Jill Stein" \
  election="2016 Presidential Election"

cURL

curl -X POST \
  -H "Content-Type: application/json" \
  -d '{ "candidate": "Jill Stein", "election": "2016 Presidential Election" }' \
  "http://localhost:8099/voter/votes"

wget

wget --method POST \
  --header 'content-type: application/json' \
  --body-data '{ "candidate": "Jill Stein", "election": "2016 Presidential Election" }' \
  --no-verbose \
  --output-document - http://localhost:8099/voter/votes

Sample Output

Using HTTPie command line HTTP client.

http http://localhost:8099/voter/simulation/http/2016%20Presidential%20Election
or
http http://localhost:8099/voter/simulation/rpc/2016%20Presidential%20Election
or
http http://localhost:8099/voter/simulation/db/2016%20Presidential%20Election

{
    "message": "Simulation data created!"
}

http http://localhost:8099/voter/candidates/http/2016%20Presidential%20Election
or
http http://localhost:8099/voter/candidates/rpc/2016%20Presidential%20Election
or
http http://localhost:8099/voter/candidates/db/2016%20Presidential%20Election

{
    "candidates": [
        {
            "election": "2016 Presidential Election",
            "fullName": "Darrell Castle",
            "politicalParty": "Constitution Party"
        },
        {
            "election": "2016 Presidential Election",
            "fullName": "Hillary Clinton",
            "politicalParty": "Democratic Party"
        },
        {
            "election": "2016 Presidential Election",
            "fullName": "Gary Johnson",
            "politicalParty": "Libertarian Party"
        }
    ]
}

http http://localhost:8099/voter/results/2016%20Presidential%20Election

{
    "results": [
        {
            "candidate": "Darrell Castle",
            "votes": 19
        },
        {
            "candidate": "Donald Trump",
            "votes": 15
        },
        {
            "candidate": "Gary Johnson",
            "votes": 15
        },
        {
            "candidate": "Jill Stein",
            "votes": 13
        }
    ]
}

http http://localhost:8099/voter/results/2016%20Presidential%20Election/votes

{
    "votes": 80
}

http http://localhost:8099/voter/winners/2016%20Presidential%20Election

{
    "results": [
        {
            "candidate": "Darrell Castle",
            "votes": 19
        }
    ]
}

http http://localhost:8099/voter/winners/2016%20Presidential%20Election/votes

{
    "votes": 19
}

http POST http://localhost:8099/voter/votes \ candidate="Jill Stein" \ election="2016 Presidential Election"

{
    "_links": {
        "self": {
            "href": "http://localhost:8099/voter/votes/590548541b8ebf700f9c2a62"
        },
        "candidate": {
            "href": "http://localhost:8099/voter/votes/590548541b8ebf700f9c2a62"
        }
    },
    "candidate": "Jill Stein",
    "election": "2016 Presidential Election"
}

Continuous Integration

The project's source code is continuously built and tested on every commit to GitHub, using Travis CI. If all unit tests pass, the resulting Spring Boot JAR is pushed to the build-artifacts branch of the voter-service GitHub repository. The JAR's filename is incremented with each successful build (i.e. voter-service-0.2.10.jar).

Spring Profiles

The Voter service includes several Spring Boot Profiles, in a multi-profile YAML document: src/main/resources/application.yml. The profiles are default, docker-development, docker-production, and aws-production. You will need to ensure your MongoDB instance is available at that host address and port of the profile you choose, or you may override the profile's properties.

endpoints:
  enabled: true
  sensitive: false
info:
  java:
    source: "${java.version}"
logging:
  level:
    root: INFO
management:
  info:
    build:
      enabled: true
    git:
      mode: full
server:
  port: 8099
  context-path: /voter
services:
  candidate:
    host: localhost
    port: 8097
    context-path: candidate
spring:
  data:
    mongodb:
      database: voters
      host: localhost
      port: 27017
  rabbitmq:
    host: localhost
---
services:
  candidate:
    host: candidate
spring:
  data:
    mongodb:
      host: mongodb
  rabbitmq:
    host: rabbitmq
  profiles: docker-local
---
endpoints:
  candidate:
    host: candidate
  enabled: false
  health:
    enabled: true
  info:
    enabled: true
  sensitive: true
  services: ~
logging:
  level:
    root: WARN
management:
  info:
    build:
      enabled: false
    git:
      enabled: false
spring:
  data:
    mongodb:
      host: "10.0.1.6"
  rabbitmq:
    host: "10.0.1.8"
  profiles: aws-production
---
endpoints:
  enabled: false
  health:
    enabled: true
  info:
    enabled: true
  sensitive: true
logging:
  level:
    root: WARN
management:
  info:
    build:
      enabled: false
    git:
      enabled: false
services:
  candidate:
    host: candidate
spring:
  data:
    mongodb:
      host: mongodb
  rabbitmq:
    host: rabbitmq
  profiles: docker-production

All profile property values may be overridden on the command line, or in a .conf file. For example, to start the Voter service with the aws-production profile, but override the mongodb.host value with a new host address, you might use the following command:

java -jar <name_of_jar_file> \
  --spring.profiles.active=aws-production \
  --spring.data.mongodb.host=<new_host_address>
  -Dlogging.level.root=DEBUG \
  -Djava.security.egd=file:/dev/./urandom

References

Docker Pull Command
Owner
garystafford
Source Repository