-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cat-gateway): config API (#981)
* feat: add config endpoint Signed-off-by: bkioshn <[email protected]> * feat: add jsonschema lib Signed-off-by: bkioshn <[email protected]> * fix: config table Signed-off-by: bkioshn <[email protected]> * fix: sql format Signed-off-by: bkioshn <[email protected]> * fix: comment to sql file Signed-off-by: bkioshn <[email protected]> * feat: add upsert sql Signed-off-by: bkioshn <[email protected]> * fix: update endpoint Signed-off-by: bkioshn <[email protected]> * fix: frontend key implementation Signed-off-by: bkioshn <[email protected]> * fix: config query Signed-off-by: bkioshn <[email protected]> * fix: sql lint Signed-off-by: bkioshn <[email protected]> * fix: refactor Signed-off-by: bkioshn <[email protected]> * fix: config endpoint Signed-off-by: bkioshn <[email protected]> * fix: lazy lock validator and rename function Signed-off-by: bkioshn <[email protected]> * fix: frontend default and json schema Signed-off-by: bkioshn <[email protected]> * chore:add license MIT Signed-off-by: bkioshn <[email protected]> * fix: remove migration v2 to v9 Signed-off-by: bkioshn <[email protected]> * fix: format Signed-off-by: bkioshn <[email protected]> * chore: change license to MIT-0 Signed-off-by: bkioshn <[email protected]> * chore: remove license Signed-off-by: bkioshn <[email protected]> * fix: add mit-0 license to deny.toml and test it Signed-off-by: bkioshn <[email protected]> * fix: update cat-gateway code gen Signed-off-by: bkioshn <[email protected]> * fix: update cat-gateway rust-ci version Signed-off-by: bkioshn <[email protected]> * fix: revert change Signed-off-by: bkioshn <[email protected]> * fix: add new endpoint and fix validate json Signed-off-by: bkioshn <[email protected]> * fix: cat-gateway api code gen Signed-off-by: bkioshn <[email protected]> * Update catalyst-gateway/bin/src/db/event/config/jsonschema/frontend.json * Update catalyst-gateway/bin/src/db/event/config/jsonschema/frontend.json * fix: frontend default and json schema Signed-off-by: bkioshn <[email protected]> * fix: error handling Signed-off-by: bkioshn <[email protected]> * fix: cat-gateway api code gen Signed-off-by: bkioshn <[email protected]> * fix: openapi lint Signed-off-by: bkioshn <[email protected]> * fix: frontend json schema Signed-off-by: bkioshn <[email protected]> * fix: error handling Signed-off-by: bkioshn <[email protected]> * fix: cat-gateway api code gen Signed-off-by: bkioshn <[email protected]> * fix: remove id Signed-off-by: bkioshn <[email protected]> * fix: error log Signed-off-by: bkioshn <[email protected]> * fix: bump ci to v3.2.18 Signed-off-by: bkioshn <[email protected]> --------- Signed-off-by: bkioshn <[email protected]> Co-authored-by: Steven Johnson <[email protected]>
- Loading branch information
Showing
36 changed files
with
616 additions
and
1,187 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -119,6 +119,7 @@ Joaquín | |
jorm | ||
jormungandr | ||
Jörmungandr | ||
jsonschema | ||
junitreport | ||
junitxml | ||
Keyhash | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
catalyst-gateway/bin/src/db/event/config/default/frontend.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"sentry": { | ||
"environment": "dev" | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
catalyst-gateway/bin/src/db/event/config/default/frontend_ip.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{} |
30 changes: 30 additions & 0 deletions
30
catalyst-gateway/bin/src/db/event/config/jsonschema/frontend.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"title": "Frontend JSON schema", | ||
"type": "object", | ||
"sentry": { | ||
"type": "object", | ||
"description": "Configuration for Sentry.", | ||
"properties": { | ||
"dsn": { | ||
"$ref": "#/definitions/httpsUrl", | ||
"description": "The Data Source Name (DSN) for Sentry." | ||
}, | ||
"release": { | ||
"type": "string", | ||
"description": "A version of the code deployed to an environment" | ||
}, | ||
"environment": { | ||
"type": "string", | ||
"description": "The environment in which the application is running, e.g., 'dev', 'qa'." | ||
} | ||
} | ||
}, | ||
"definitions": { | ||
"httpsUrl": { | ||
"type": "string", | ||
"format": "uri", | ||
"pattern": "^https?://" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
//! Configuration Key | ||
|
||
use std::{fmt::Display, net::IpAddr, sync::LazyLock}; | ||
|
||
use jsonschema::{BasicOutput, Validator}; | ||
use serde_json::{json, Value}; | ||
use tracing::error; | ||
|
||
/// Configuration key | ||
#[derive(Debug, Clone, PartialEq)] | ||
pub(crate) enum ConfigKey { | ||
/// Frontend general configuration. | ||
Frontend, | ||
/// Frontend configuration for a specific IP address. | ||
FrontendForIp(IpAddr), | ||
} | ||
|
||
impl Display for ConfigKey { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
ConfigKey::Frontend => write!(f, "config_key_frontend"), | ||
ConfigKey::FrontendForIp(_) => write!(f, "config_key_frontend_ip"), | ||
} | ||
} | ||
} | ||
|
||
/// Frontend schema. | ||
static FRONTEND_SCHEMA: LazyLock<Value> = | ||
LazyLock::new(|| load_json_lazy(include_str!("jsonschema/frontend.json"))); | ||
|
||
/// Frontend schema validator. | ||
static FRONTEND_SCHEMA_VALIDATOR: LazyLock<Validator> = | ||
LazyLock::new(|| schema_validator(&FRONTEND_SCHEMA)); | ||
|
||
/// Frontend default configuration. | ||
static FRONTEND_DEFAULT: LazyLock<Value> = | ||
LazyLock::new(|| load_json_lazy(include_str!("default/frontend.json"))); | ||
|
||
/// Frontend specific configuration. | ||
static FRONTEND_IP_DEFAULT: LazyLock<Value> = | ||
LazyLock::new(|| load_json_lazy(include_str!("default/frontend_ip.json"))); | ||
|
||
/// Helper function to create a JSON validator from a JSON schema. | ||
/// If the schema is invalid, a default JSON validator is created. | ||
fn schema_validator(schema: &Value) -> Validator { | ||
jsonschema::validator_for(schema).unwrap_or_else(|err| { | ||
error!( | ||
id = "schema_validator", | ||
error=?err, | ||
"Error creating JSON validator" | ||
); | ||
|
||
// Create a default JSON validator as a fallback | ||
// This should not fail since it is hard coded | ||
#[allow(clippy::expect_used)] | ||
Validator::new(&json!({ | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"type": "object" | ||
})) | ||
.expect("Failed to create default JSON validator") | ||
}) | ||
} | ||
|
||
/// Helper function to convert a JSON string to a JSON value. | ||
fn load_json_lazy(data: &str) -> Value { | ||
serde_json::from_str(data).unwrap_or_else(|err| { | ||
error!(id = "load_json_lazy", error=?err, "Error parsing JSON"); | ||
json!({}) | ||
}) | ||
} | ||
|
||
impl ConfigKey { | ||
/// Convert a `ConfigKey` to its corresponding IDs. | ||
pub(super) fn to_id(&self) -> (String, String, String) { | ||
match self { | ||
ConfigKey::Frontend => ("frontend".to_string(), String::new(), String::new()), | ||
ConfigKey::FrontendForIp(ip) => { | ||
("frontend".to_string(), "ip".to_string(), ip.to_string()) | ||
}, | ||
} | ||
} | ||
|
||
/// Validate the provided value against the JSON schema. | ||
pub(super) fn validate(&self, value: &Value) -> BasicOutput<'static> { | ||
// Retrieve the validator based on ConfigKey | ||
let validator = match self { | ||
ConfigKey::Frontend | ConfigKey::FrontendForIp(_) => &*FRONTEND_SCHEMA_VALIDATOR, | ||
}; | ||
|
||
// Validate the value against the schema | ||
validator.apply(value).basic() | ||
} | ||
|
||
/// Retrieve the default configuration value. | ||
pub(super) fn default(&self) -> Value { | ||
// Retrieve the default value based on the ConfigKey | ||
match self { | ||
ConfigKey::Frontend => FRONTEND_DEFAULT.clone(), | ||
ConfigKey::FrontendForIp(_) => FRONTEND_IP_DEFAULT.clone(), | ||
} | ||
} | ||
|
||
/// Retrieve the JSON schema. | ||
pub(crate) fn schema(&self) -> &Value { | ||
match self { | ||
ConfigKey::Frontend | ConfigKey::FrontendForIp(_) => &FRONTEND_SCHEMA, | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use serde_json::json; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn test_valid_validate() { | ||
let value = json!({ | ||
"test": "test" | ||
}); | ||
let result = ConfigKey::Frontend.validate(&value); | ||
assert!(result.is_valid()); | ||
println!("{:?}", serde_json::to_value(result).unwrap()); | ||
} | ||
|
||
#[test] | ||
fn test_invalid_validate() { | ||
let value = json!([]); | ||
let result = ConfigKey::Frontend.validate(&value); | ||
assert!(!result.is_valid()); | ||
println!("{:?}", serde_json::to_value(result).unwrap()); | ||
} | ||
|
||
#[test] | ||
fn test_default() { | ||
let result = ConfigKey::Frontend.default(); | ||
assert!(result.is_object()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
//! Configuration query | ||
|
||
use jsonschema::BasicOutput; | ||
use key::ConfigKey; | ||
use serde_json::Value; | ||
use tracing::error; | ||
|
||
use crate::db::event::EventDB; | ||
|
||
pub(crate) mod key; | ||
|
||
/// Configuration struct | ||
pub(crate) struct Config {} | ||
|
||
/// SQL get configuration. | ||
const GET_CONFIG: &str = include_str!("sql/get.sql"); | ||
/// SQL update if exist or else insert configuration. | ||
const UPSERT_CONFIG: &str = include_str!("sql/upsert.sql"); | ||
|
||
impl Config { | ||
/// Retrieve configuration based on the given `ConfigKey`. | ||
/// | ||
/// # Returns | ||
/// | ||
/// - A JSON value of the configuration, if not found or error, returns the default | ||
/// value. | ||
/// - Error if the query fails. | ||
pub(crate) async fn get(id: ConfigKey) -> anyhow::Result<Value> { | ||
let (id1, id2, id3) = id.to_id(); | ||
let rows = EventDB::query(GET_CONFIG, &[&id1, &id2, &id3]).await?; | ||
|
||
if let Some(row) = rows.first() { | ||
let value: Value = row.get(0); | ||
match id.validate(&value) { | ||
BasicOutput::Valid(_) => return Ok(value), | ||
BasicOutput::Invalid(errors) => { | ||
// This should not happen, expecting the schema to be valid | ||
error!(id=%id, error=?errors, "Get Config, schema validation failed, defaulting."); | ||
}, | ||
} | ||
} | ||
// Return the default config value as a fallback | ||
Ok(id.default()) | ||
} | ||
|
||
/// Set the configuration for the given `ConfigKey`. | ||
/// | ||
/// # Returns | ||
/// | ||
/// - A `BasicOutput` of the validation result, which can be valid or invalid. | ||
/// - Error if the query fails. | ||
pub(crate) async fn set(id: ConfigKey, value: Value) -> anyhow::Result<BasicOutput<'static>> { | ||
let validate = id.validate(&value); | ||
// Validate schema failed, return immediately with JSON schema error | ||
if !validate.is_valid() { | ||
return Ok(validate); | ||
} | ||
|
||
let (id1, id2, id3) = id.to_id(); | ||
EventDB::query(UPSERT_CONFIG, &[&id1, &id2, &id3, &value]).await?; | ||
|
||
Ok(validate) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
-- Select the 'value' column from the 'config' table | ||
SELECT value | ||
FROM config | ||
WHERE | ||
id1 = $1 -- Match rows where 'id1' equals the first parameter | ||
AND id2 = $2 -- Match rows where 'id2' equals the second parameter | ||
AND id3 = $3; -- Match rows where 'id3' equals the third parameter |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
-- Insert a new configuration entry into the 'config' table | ||
INSERT INTO config (id1, id2, id3, value) | ||
VALUES ($1, $2, $3, $4) -- Values to insert for each column | ||
|
||
-- Handle conflicts when attempting to insert a row that would violate the unique constraint | ||
ON CONFLICT (id1, id2, id3) -- Specify the unique constraint columns that identify conflicts | ||
|
||
-- If a conflict occurs, update the existing row 'value' column with the new value provided | ||
DO UPDATE SET value = excluded.value; -- 'EXCLUDED' refers to the values that were proposed for insertion |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.