The basic idea is that since a subset of HTTP verbs map nicely to Mongo Database CRUD operations, it would be useful to provide a RESTful web service for read/write access by a variety of clients.
This idea is not new but serves as a useful exercise in learning Kotlin.
The database and collection names are embedded in the first two path parameters and the query parameters correspond to the specific document identifiers.
HTTP Method | ARMS API | MongoDB Action |
---|---|---|
GET | /{database}/{collection}?key0=value0&key1=value1 ... | database.collection.find({ key0 : value0, key1 : value1, ... }) |
PUT | /{database}/{collection} plus a json document in the request message body |
database.collection.insertOne({ json document }) |
PUT | /{database}/{collection}?key0=value0&key1=value1 ... plus a json document in the request message body |
database.collection.replaceOne( { key0 : value0, key1 : value1, ... }, { json document }, upsert: true) |
DELETE | /{database}/{collection}?key0=value0&key1=value1 ... | database.collection.deleteMany({ key0 : value0, key1 : value1, ... }) |
After cloning this repo, create one or more environment configuration folders under src/main/configurations. A typical deployment will have dev, qa, and prod, like this:
mkdir -p src/main/configurations/{dev,qa,prod}
The configuration.json file defines the service host and port, as well as the Mongo Connection String URI, as well as authentication options per client, per HTTP action.
The default values are:
{
"authSeeds": {},
"authenticate": false,
"mongoURI": "mongodb://localhost:27017",
"serviceURI": "http://localhost:9001/"
}
Copy configuration.json into each of the src/main/configurations environment folders, and make the appropriate edits for each environment.
Everything under src/main/configurations is excluded from source control.
When "authenticate" in the configuration.json file is set to true, each client request must include these two headers, otherwise the server will reply with 403 Forbidden:
- API-KEY — a unique client identifier string, defined as a key in the "authSeeds" hash
- API-TOTP — a Time-Based One-Time Password (TOTP) string, generated as defined in RFC 6238, using the seed value in the "authSeeds" hash for the corresponding client identifier
Each API client needs to know its seed value and keep it secret, so that it can produce the TOTP within 30 seconds of each request it makes to the server.
Permissions are granular, per each HTTP action (GET, PUT, DELETE), and an API client have have any combination of allowed and disallowed requests.
The test configuration file has an example for two API clients, the first of which has read-only access (i.e., find using GET, but not PUT nor DELETE), and the second of which has all permissions.
After installing Kotlin and defining your environment-specific setup in configuration.json, use the gradle distTar or distZip task along with the desired environment.
For example, to build a zip file using the configuration defined in src/main/configurations/qa, run this command:
./gradlew distZip -Penv=qa
If the -Penv=qa option is missing, the default configuration.json file from src/main/resources will be used instead.
The resulting zip file, ARMS-0.0.1.zip will be created in the build/distributions folder.
Copy or move it to the appropriate place on your server.
Unzip it, and run it using either one of these launcher scripts:
- ARMS-0.0.1/bin/ARMS
- ARMS-0.0.1/bin/ARMS.bat
Features to add, and other things to implement in the future:
-
Unit tests and continuous integration -
Deployable jars and instructions - Support for more complex queries, using QueryBuilder
- Handling mongo connection errors using an appropriate HTTP status code for the API requests (e.g., 410 Gone or 503 Service Unavailable, etc.), along with an alert to the service maintainer
-
Authentication, especially for PUT and DELETE requests - Prevent Jersey from returning 500 when the json document used in the PUT request is invalid (alas, this solution doesn't seem to work)
Pull requests for both roadmap ideas and implementations are welcome!