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:
- HTTP-based Synchronous IPC, when either the Voter service's
/voter/candidates/election/{election}
or/voter/simulation/election/{election}
endpoints are called. - Messaging-based Remote Procedure Call (RPC) IPC, when either the Voter service's
/voter/candidates/rpc/election/{election}
or/voter/simulation/rpc/election/{election}
endpoints are called. - Messaging-based Eventual Consistency, when either the Voter service's
/voter/candidates/db/election/{election}
or/voter/simulation/db/election/{election}
endpoints are called.
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
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 Voter service, Candidate service, RabbitMQ, and MongoDB.
sh ./stack-deploy-local.sh
COMMAND CREATED STATUS PORTS NAMES
6232727faffd garystafford/voter-api-gateway:latest "/docker-entrypoin..." 39 seconds ago Up 38 seconds 0.0.0.0:8080->8080/tcp voterstack_voter-api-gateway_1
97444d209416 garystafford/voter-service:rabbitmq "java -Dspring.pro..." 40 seconds ago Up 39 seconds 0.0.0.0:8099->8099/tcp voterstack_voter_1
f78ab6fb491b garystafford/candidate-service:rabbitmq "java -Dspring.pro..." 40 seconds ago Up 39 seconds 0.0.0.0:8097->8097/tcp voterstack_candidate_1
0f851d9d0e4c rabbitmq:management-alpine "docker-entrypoint..." 41 seconds ago Up 40 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
8238959426a1 mongo:latest "docker-entrypoint..." 41 seconds ago Up 40 seconds 0.0.0.0:27017->27017/tcp voterstack_mongodb_1
The easiest way to get started with the Candidate and Voter services API, using HTTPie from the command line:
- Create sample candidates:
http http://localhost:8097/candidate/simulation
- View sample candidates:
http http://localhost:8097/candidate/candidates/summary/election/2016%20Presidential%20Election
- Create sample voter data:
http http://localhost:8099/voter/simulation/election/2016%20Presidential%20Election
- 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/election/2016%20Presidential%20Election
Alternately, for step 3 above, you can use eventual consistency using RabbitMQ, to retrieve the candidates from MongoDB:
http http://localhost:8099/voter/simulation/db/election/2016%20Presidential%20Election
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 | GET | /voter/simulation/election/{election} |
Create Random Sample Data (using RPC Messaging) | GET | /voter/simulation/rpc/election/{election} |
List Candidates | GET | /voter/candidates/election/{election} |
List Candidates (using RPC Messaging) | GET | /voter/candidates/rpc/election/{election} |
List Candidates (using Message Queue and Database) | GET | /voter/candidates/db/election/{election} |
Submit Vote | POST | /voter/votes |
View Voting Results | GET | /voter/results |
View Total Votes | GET | /voter/results/votes |
View Winner(s) | GET | /voter/winners |
View Winning Vote Count | GET | /voter/winners/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/
.
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
Using HTTPie command line HTTP client.
http http://localhost:8099/voter/simulation/election/2016%20Presidential%20Election
or
http http://localhost:8099/voter/simulation/rpc/election/2016%20Presidential%20Election
or
http http://localhost:8099/voter/simulation/db/election/2016%20Presidential%20Election
{
"message": "Simulation data created!"
}
http http://localhost:8099/voter/candidates/election/2016%20Presidential%20Election
or
http http://localhost:8099/voter/candidates/rpc/election/2016%20Presidential%20Election
or
http http://localhost:8099/voter/candidates/db/election/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
{
"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/votes
{
"votes": 80
}
http http://localhost:8099/voter/winners
{
"results": [
{
"candidate": "Darrell Castle",
"votes": 19
}
]
}
http http://localhost:8099/voter/winners/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"
}
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
).
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