Skip to content

Commit

Permalink
feat: Adds client features endpoint (#3)
Browse files Browse the repository at this point in the history
This is worth a comment, since the way it was solved depends on which
mode you're executing edge in. Currently edge only supports one mode, so
we could've just explicitly passed in the provider for getting features.

However, we are aware that we want at least two more providers for
features, so this lays the ground work for implementing more modes and
providers, but still using the same actix handler for the
`/api/client/features` endpoint.

Co-authored-by: Simon Hornby <liquidwicked64@gmail.com>
  • Loading branch information
Christopher Kolstad and sighphyre authored Jan 20, 2023
1 parent f4dad47 commit 4bf25a3
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 34 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
name: Checkout code
with:
token: ${{ secrets.GH_PUSH_TOKEN }}
fetch-depth: 0
- name: setup git config
run: |
git config user.name "Github Release Bot"
Expand All @@ -27,7 +28,10 @@ jobs:
rustup set auto-self-update disable
rustup toolchain install stable --profile minimal
rustup show
- uses: Swatinem/rust-cache@v2
- name: Use rust cache
uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- name: Install cargo smart-release
run: |
cargo install cargo-smart-release
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ tokio = { version = "1.24.2", features = ["macros", "rt-multi-thread", "tracing"
tracing = { version = "0.1.37", features = ["log"] }
tracing-opentelemetry = "0.18.0"
tracing-subscriber = { version = "0.3.16", features = ["json", "env-filter"] }
unleash-types = "0.4.0"
unleash-types = "0.4.1"
unleash-yggdrasil = "0.2.0"
5 changes: 3 additions & 2 deletions server/src/client_api.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::offline_provider::OfflineProvider;
use crate::types::{EdgeJsonResult, FeaturesProvider};
use actix_web::get;
use actix_web::web::{self, Json};
use unleash_types::client_features::ClientFeatures;

#[get("/client/features")]
async fn features(features_source: web::Data<OfflineProvider>) -> EdgeJsonResult<ClientFeatures> {
async fn features(
features_source: web::Data<dyn FeaturesProvider>,
) -> EdgeJsonResult<ClientFeatures> {
let client_features = features_source.get_client_features();
Ok(Json(client_features))
}
Expand Down
3 changes: 3 additions & 0 deletions server/src/edge_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use actix_web::web;

pub fn configure_edge_api(_cfg: &mut web::ServiceConfig) {}
10 changes: 7 additions & 3 deletions server/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use actix_web::{http::StatusCode, HttpResponseBuilder, ResponseError};

#[derive(Debug)]
pub enum EdgeError {
InvalidBackupFile(String),
InvalidBackupFile(String, String),
NoFeaturesFile,
TlsError,
}
Expand All @@ -15,7 +15,11 @@ impl Error for EdgeError {}
impl Display for EdgeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EdgeError::InvalidBackupFile(msg) => write!(f, "{}", msg),
EdgeError::InvalidBackupFile(path, why_invalid) => write!(
f,
"file at path: {} was invalid due to {}",
path, why_invalid
),
EdgeError::TlsError => write!(f, "Could not configure TLS"),
EdgeError::NoFeaturesFile => write!(f, "No features file located"),
}
Expand All @@ -25,7 +29,7 @@ impl Display for EdgeError {
impl ResponseError for EdgeError {
fn status_code(&self) -> actix_web::http::StatusCode {
match self {
EdgeError::InvalidBackupFile(_) => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::InvalidBackupFile(_, _) => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::TlsError => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::NoFeaturesFile => StatusCode::INTERNAL_SERVER_ERROR,
}
Expand Down
3 changes: 3 additions & 0 deletions server/src/frontend_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use actix_web::web;

pub fn configure_frontend_api(_cfg: &mut web::ServiceConfig) {}
18 changes: 11 additions & 7 deletions server/src/internal_backstage.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use actix_web::{
get,
web::{self, Json},
HttpRequest,
};
use actix_web_opentelemetry::PrometheusMetricsHandler;
use serde::Serialize;

use crate::types::EdgeJsonResult;
Expand All @@ -20,12 +20,16 @@ impl EdgeStatus {
}
}
#[get("/health")]
pub async fn health(_req: HttpRequest) -> EdgeJsonResult<EdgeStatus> {
pub async fn health() -> EdgeJsonResult<EdgeStatus> {
Ok(Json(EdgeStatus::ok()))
}

pub fn configure_internal_backstage(cfg: &mut web::ServiceConfig) {
cfg.service(health);
pub fn configure_internal_backstage(
cfg: &mut web::ServiceConfig,
metrics_handler: PrometheusMetricsHandler,
) {
cfg.service(health)
.service(web::resource("/metrics").route(web::get().to(metrics_handler)));
}

#[cfg(test)]
Expand All @@ -34,9 +38,9 @@ mod tests {

#[actix_web::test]
async fn test_health_ok() {
let app = test::init_service(App::new().service(
web::scope("/internal-backstage").configure(super::configure_internal_backstage),
))
let app = test::init_service(
App::new().service(web::scope("/internal-backstage").service(super::health)),
)
.await;
let req = test::TestRequest::get()
.uri("/internal-backstage/health")
Expand Down
25 changes: 18 additions & 7 deletions server/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
use std::sync::Arc;

use crate::cli::EdgeMode;
use crate::offline_provider::OfflineProvider;
use actix_web::{middleware, web, App, HttpServer};
use actix_web_opentelemetry::RequestTracing;
use clap::Parser;
use cli::CliArgs;
use types::FeaturesProvider;

mod cli;
mod client_api;
mod edge_api;
mod error;
mod frontend_api;
mod internal_backstage;
mod metrics;
mod offline_provider;
Expand All @@ -24,19 +29,25 @@ async fn main() -> Result<(), anyhow::Error> {
}
.map_err(anyhow::Error::new)?;
let server = HttpServer::new(move || {
let client_provider_arc: Arc<dyn FeaturesProvider> = Arc::new(client_provider.clone());
let client_provider_data = web::Data::from(client_provider_arc);
App::new()
.app_data(web::Data::new(client_provider.clone()))
.app_data(client_provider_data)
.wrap(RequestTracing::new())
.wrap(request_metrics.clone())
.wrap(middleware::Logger::default())
.service(web::scope("/internal-backstage").configure(|service_cfg| {
internal_backstage::configure_internal_backstage(
service_cfg,
metrics_handler.clone(),
)
}))
.service(
web::scope("/internal-backstage")
.configure(internal_backstage::configure_internal_backstage)
.service(
web::resource("/metrics").route(web::get().to(metrics_handler.clone())),
),
web::scope("/api")
.configure(client_api::configure_client_api)
.configure(frontend_api::configure_frontend_api),
)
.service(web::scope("/api").configure(client_api::configure_client_api))
.service(web::scope("/edge").configure(edge_api::configure_edge_api))
});
let server = if args.http.tls.tls_enable {
let config = tls::config(args.clone().http.tls)
Expand Down
14 changes: 4 additions & 10 deletions server/src/offline_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use crate::types::FeaturesProvider;
use std::fs::File;
use std::io::BufReader;
use std::path::PathBuf;
use tracing::info;
use unleash_types::client_features::ClientFeatures;

#[derive(Debug, Clone)]
Expand All @@ -21,18 +20,13 @@ impl OfflineProvider {
pub fn instantiate_provider(
bootstrap_file: Option<PathBuf>,
) -> Result<OfflineProvider, EdgeError> {
info!("Instantiate offline provider");
if let Some(bootstrap) = bootstrap_file {
info!("Opening bootstrap file");
let file = File::open(bootstrap.clone()).map_err(|_| EdgeError::NoFeaturesFile)?;
info!("Opened");
let reader = BufReader::new(file);
info!("Buffered reader");
let client_features: ClientFeatures =
serde_json::from_reader(reader).map_err(|_| {
let path = format!("{}", bootstrap.clone().display());
EdgeError::InvalidBackupFile(path)
})?;
let client_features: ClientFeatures = serde_json::from_reader(reader).map_err(|e| {
let path = format!("{}", bootstrap.clone().display());
EdgeError::InvalidBackupFile(path, e.to_string())
})?;
Ok(OfflineProvider::new(client_features))
} else {
Err(EdgeError::NoFeaturesFile)
Expand Down

0 comments on commit 4bf25a3

Please sign in to comment.