Skip to content

Commit

Permalink
/events and /health routes
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick Meade committed Aug 22, 2024
1 parent 4f9bd23 commit 1755c83
Show file tree
Hide file tree
Showing 21 changed files with 1,504 additions and 316 deletions.
49 changes: 39 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 11 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@ publish = false

[dependencies]
axum = "0.7.5"
axum-extra = { version = "0.9.3", features = [ "erased-json" ] }
chrono = "0.4.38"
env_logger = "0.11.5"
futures-util = "0.3.30"
gotham = "0.7.4"
gotham_restful = "0.9.0"
log = "0.4.22"
mongodb = "3.0.1"
serde = "1.0.205"
serde_json = "1.0.122"
tokio = { version = "1.39.2", features = [ "full" ] }
serde = "1.0.208"
serde_json = "1.0.125"
tokio = { version = "1.39.3", features = [ "full" ] }
uuid = { version = "1.10.0", features = [ "serde", "v4" ] }

[lib]
name = "wipac_disk_tracking"
path = "src/lib.rs"
38 changes: 29 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ The REST API for the wipac-disk-tracking service is documented below.

The API is rooted at:

/api/v#

Where # is the version number of the API under use.
/api/v1

### Disks
Disks represent a unique archival disk.
While the disk entity does carry some immutable identifying information, it
is mostly a container for archival disk events.
Disks represent a collection of unique archival disks. While the disk
entity does carry some immutable identifying information, it is mostly
a container for archival disk events.

#### Routes
These routes are implemented to work with disks:
Expand All @@ -24,7 +22,7 @@ These routes are implemented to work with disks:
GET /disks/:disk_id/search?query Find a disk based on a key-value query

### Events
Events represent a record of an event involving an archival disk.
Events represent a record of an event involving an archival disk.
There are four distinct events that are tracked by the system.

sighted This disk was observed to be loaded in a host that processes archival disks
Expand All @@ -37,6 +35,28 @@ The format for each of these events is specified with a JSON Schema file.
#### Routes
These routes are implemented to work with events:

POST /events Create a new event
POST /events/closed Create a new 'closed' event
POST /events/formatted Create a new 'formatted' event
POST /events/opened Create a new 'opened' event
POST /events/sighted Create a new 'sighted' event
GET /events/:event_id Get the data for a given event
GET /events/search?query Find an event based on key-value query

## Development
As typical in a Rust project, you can run the unit and integration tests with:

cargo test -- --show-output

### MongoDB Tests
The MongoDB integration test will look for the temporary MongoDB container
running locally. If you'd rather run the test against a different MongoDB,
you can supply the URL through this environment variable:

DESTRUCTIVE_TEST_MONGODB_URL

Note that the integration test is destructive. It may wipe out your collections,
indexes, or databases! DON'T point this at any data that you want to keep!

If MongoDB is not available, locally or through the provided URL, then a
message will appear in the output of the tests:

"MongoDB is not available. Skipping test."
1 change: 1 addition & 0 deletions bin/run-app
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export MONGODB_DATABASE=${MONGODB_DATABASE:="disk_tracking"}
export MONGODB_HOSTNAME=${MONGODB_HOSTNAME:="localhost"}
export MONGODB_PASSWORD=${MONGODB_PASSWORD:="hunter2"}
export MONGODB_PORT_NUMBER=${MONGODB_PORT_NUMBER:="27017"}
export MONGODB_TIMEOUT_SECS=${MONGODB_TIMEOUT_SECS:="5"}
export MONGODB_USERNAME=${MONGODB_USERNAME:="disk_tracking"}
export PORT=${PORT:="8080"}
export RUST_LOG=${RUST_LOG:="debug"}
Expand Down
2 changes: 2 additions & 0 deletions bin/run-app-docker
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
: ${MONGODB_DATABASE:="disk_tracking"}
: ${MONGODB_HOSTNAME:="localhost"}
: ${MONGODB_PASSWORD:="hunter2"}
: ${MONGODB_TIMEOUT_SECS:="5"}
: ${MONGODB_USERNAME:="disk_tracking"}
: ${MONGODB_PORT_NUMBER:="27017"}
: ${PORT:="8080"}
Expand All @@ -20,6 +21,7 @@ docker run \
--env=MONGODB_HOSTNAME=${DOCKER_MONGO_CONTAINER_NAME} \
--env=MONGODB_PASSWORD=${MONGODB_PASSWORD} \
--env=MONGODB_PORT_NUMBER=${MONGODB_PORT_NUMBER} \
--env=MONGODB_TIMEOUT_SECS=${MONGODB_TIMEOUT_SECS} \
--env=MONGODB_USERNAME=${MONGODB_USERNAME} \
--env=PORT=${PORT} \
--env=RUST_LOG=debug \
Expand Down
65 changes: 46 additions & 19 deletions src/context.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,25 @@
// context.rs

use mongodb::{options::ClientOptions, Client};
use std::env;
use std::time::Duration;

use crate::error::{ApplicationError::ContextError, Result};

#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct ApplicationContext {
pub mongo_client: Client,
pub mongo_database: String,
pub mongo_host: String,
pub mongo_password: String,
pub mongo_port: String,
pub mongo_user: String,
pub port: u16,
}

impl ApplicationContext {
pub fn get_mongo_url(&self) -> String {
format!(
"mongodb://{}:{}@{}:{}/{}",
self.mongo_user,
self.mongo_password,
self.mongo_host,
self.mongo_port,
self.mongo_database
)
}
}

pub fn build_context() -> Result<ApplicationContext> {
// read database parameters from the environment
let mongo_host = env::var("MONGODB_HOSTNAME").unwrap_or_else(|_| "localhost".into());
let mongo_port = env::var("MONGODB_PORT_NUMBER").unwrap_or_else(|_| "27017".into());
pub async fn build_context() -> Result<ApplicationContext> {
// read database connection parameters from the environment
let mongo_user = env::var("MONGODB_USERNAME").unwrap_or_else(|_| "disk_tracking".into());
let mongo_database = env::var("MONGO_DB_NAME").unwrap_or_else(|_| "disk_tracking".into());
let mongo_password = match env::var("MONGODB_PASSWORD") {
Err(_) => {
return Err(ContextError(
Expand All @@ -40,14 +28,53 @@ pub fn build_context() -> Result<ApplicationContext> {
}
Ok(x) => x,
};
let mongo_host = env::var("MONGODB_HOSTNAME").unwrap_or_else(|_| "localhost".into());
let mongo_port = env::var("MONGODB_PORT_NUMBER").unwrap_or_else(|_| "27017".into());
let mongo_database = env::var("MONGODB_DATABASE").unwrap_or_else(|_| "disk_tracking".into());

// read application port from the environment
let port_str = env::var("PORT").unwrap_or_else(|_| "8080".into());
let port = match port_str.parse::<u16>() {
Err(_) => {
return Err(ContextError(format!(
"environment variable PORT contains invalid value: {}",
port_str
)))
}
Ok(x) => x,
};

// read database connection timeout from the environment
let mongodb_timeout_secs_str = env::var("MONGODB_TIMEOUT_SECS").unwrap_or_else(|_| "30".into());
let mongodb_timeout_secs = match mongodb_timeout_secs_str.parse::<u64>() {
Err(_) => {
return Err(ContextError(format!(
"environment variable MONGODB_TIMEOUT_SECS contains invalid value: {}",
mongodb_timeout_secs_str
)))
}
Ok(x) => x,
};

// build the database client
let conn_str = format!(
"mongodb://{}:{}@{}:{}/{}",
mongo_user, mongo_password, mongo_host, mongo_port, mongo_database
);
let mut client_options = ClientOptions::parse(conn_str).await?;
client_options.connect_timeout = Some(Duration::from_secs(mongodb_timeout_secs));
client_options.server_selection_timeout = Some(Duration::from_secs(mongodb_timeout_secs));
let mongo_client = Client::with_options(client_options)?;

// return the application context to the caller
Ok(ApplicationContext {
mongo_client,
mongo_database,
mongo_host,
mongo_password,
mongo_port,
mongo_user,
port,
})
}

Expand Down
Loading

0 comments on commit 1755c83

Please # to comment.