Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions veno-core/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ fn generate_notification(artifacts: &Vec<(&Artifact, Result<Option<String>>)>) -
match result {
Ok(Some(new_version)) => {
let message = match &artifact.message_prefix {
Some(prefix) => create_custom_message(&prefix, &artifact.name, &new_version),
None => create_default_message(&artifact.name, &new_version),
Some(prefix) => create_custom_message(prefix, &artifact.name, new_version),
None => create_default_message(&artifact.name, new_version),
};
messages.push(message);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
use std::sync::Arc;

use crate::resources::errors::ResourceError;
use axum::{
extract::{Path, State},
extract::{OriginalUri, Path, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use serde_json::json;
use thiserror::Error;
use tracing::trace;
use utoipa::OpenApi;
use veno_core::app::AppState;

use crate::api::{errors::ApiError, version::ApiVersion};

use super::{model::ArtifactResponse, service::check_all_artifacts};

#[derive(OpenApi)]
Expand All @@ -24,13 +26,16 @@ pub struct V1ArtifactsApi;
responses(
(status= OK, description = "Returns a set of checked artifacts with its new versions if there are any.", body = ArtifactResponse),
(status= OK, description = "Retursn a message if there are no new versions", body = serde_json::Value),
(status= INTERNAL_SERVER_ERROR, description = "If during the check a server error occurs", body = ResourceError)
(status= INTERNAL_SERVER_ERROR, description = "If during the check a server error occurs", body = ApiError)
)
)]
#[tracing::instrument(level = tracing::Level::TRACE, skip_all)]
pub async fn check_versions(
version: ApiVersion,
original_uri: OriginalUri,
State(app): State<Arc<AppState>>,
) -> Result<impl IntoResponse, ResourceError> {
) -> Result<impl IntoResponse, ApiError> {
trace!("Using API version: {}", version);
let response = check_all_artifacts(&app).await;
match response {
Ok(Some(new_versions)) => return Ok(Json(new_versions).into_response()),
Expand All @@ -40,9 +45,7 @@ pub async fn check_versions(
)
.into_response())
}
Err(_err) => {
Err(ArtifactError::InternalServerError("/api/v1/artifacts/check".into()).into())
}
Err(_err) => Err(ArtifactError::InternalServerError(original_uri.path()).into()),
}
}

Expand All @@ -54,7 +57,11 @@ pub async fn check_versions(
)
)]
#[tracing::instrument(level = tracing::Level::TRACE, skip_all)]
pub async fn all_artifacts(State(app): State<Arc<AppState>>) -> impl IntoResponse {
pub async fn all_artifacts(
version: ApiVersion,
State(app): State<Arc<AppState>>,
) -> impl IntoResponse {
trace!("Using API version: {}", version);
let artifacts: Vec<ArtifactResponse> = app
.artifacts
.iter()
Expand All @@ -68,14 +75,17 @@ pub async fn all_artifacts(State(app): State<Arc<AppState>>) -> impl IntoRespons
path="/{artifact_id}",
responses(
(status= OK, description = "Get a specific artifact with id = artifact_id", body = ArtifactResponse),
(status= NOT_FOUND, description = "Returns not_found if the artifact_id had no match", body = ResourceError),
(status= NOT_FOUND, description = "Returns not_found if the artifact_id had no match", body = ApiError),
)
)]
#[tracing::instrument(level = tracing::Level::TRACE, skip_all)]
pub async fn artifact_for_id(
Path(artifact_id): Path<String>,
version: ApiVersion,
original_uri: OriginalUri,
Path((_version, artifact_id)): Path<(String, String)>,
State(app): State<Arc<AppState>>,
) -> Result<impl IntoResponse, ResourceError> {
) -> Result<impl IntoResponse, ApiError> {
trace!("Using API version: {}", version);
let artifact = app
.artifacts
.iter()
Expand All @@ -86,35 +96,33 @@ pub async fn artifact_for_id(
let response_boddy = ArtifactResponse::from(artifact.clone());
Ok((StatusCode::OK, Json(response_boddy)).into_response())
}
None => Err(ArtifactError::NotFoundWithParam {
None => Err(ArtifactError::NotFoundParam {
param: artifact_id.clone(),
path: format!("/api/v1/artifacts/{artifact_id}"),
path: original_uri.path(),
}
.into()),
}
}

#[derive(Debug, Error)]
pub enum ArtifactError {
pub enum ArtifactError<'a> {
#[error("The artifact with the id={param} was not found.")]
NotFoundWithParam { param: String, path: String },
NotFoundParam { param: String, path: &'a str },
#[error("There was an internal server error. Please try again later.")]
InternalServerError(String),
InternalServerError(&'a str),
}

impl From<ArtifactError> for ResourceError {
impl<'a> From<ArtifactError<'a>> for ApiError {
fn from(err: ArtifactError) -> Self {
let message = err.to_string();
match err {
ArtifactError::NotFoundWithParam { param: _, path } => {
ResourceError::new(StatusCode::NOT_FOUND)
.message(message)
.path(format!("{}", path).as_str())
}
ArtifactError::NotFoundParam { param: _, path } => ApiError::new(StatusCode::NOT_FOUND)
.message(message)
.path(path),
ArtifactError::InternalServerError(path) => {
ResourceError::new(StatusCode::INTERNAL_SERVER_ERROR)
ApiError::new(StatusCode::INTERNAL_SERVER_ERROR)
.message(message)
.path(format!("{}", path).as_str())
.path(path)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use tracing::error;
use utoipa::ToSchema;

#[derive(Clone, Debug, Default, Serialize, Deserialize, ToSchema)]
pub struct ResourceError {
pub struct ApiError {
pub code: u16,
pub kind: String,
#[serde(skip_serializing_if = "Option::is_none")]
Expand All @@ -13,7 +13,7 @@ pub struct ResourceError {
pub path: Option<String>,
}

impl ResourceError {
impl ApiError {
pub fn new(status_code: StatusCode) -> Self {
let kind = match status_code.canonical_reason() {
Some(reason) => reason.to_owned(),
Expand All @@ -37,13 +37,13 @@ impl ResourceError {
}
}

impl From<StatusCode> for ResourceError {
impl From<StatusCode> for ApiError {
fn from(status_code: StatusCode) -> Self {
Self::new(status_code)
}
}

impl IntoResponse for ResourceError {
impl IntoResponse for ApiError {
fn into_response(self) -> axum::response::Response {
error!("Error response: {:?}", self);
let status_code =
Expand Down
27 changes: 15 additions & 12 deletions veno-web/src/resources/mod.rs → veno-web/src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
use std::sync::Arc;
mod artifacts;
mod errors;
mod notifiers;
mod openapi;
mod version;

use anyhow::Result;
use errors::ResourceError;
use artifacts::routes::artifacts_routes;
use errors::ApiError;
use notifiers::routes::notifiers_routes;
use openapi::ApiDoc;
use serde_json::json;
use std::sync::Arc;
use thiserror::Error;
use tower_http::cors::{Any, CorsLayer};
use tracing::trace;
use utoipa::OpenApi;
use utoipa_redoc::{Redoc, Servable};
use v1::v1_routes;
use veno_core::app::AppState;

use axum::{
Expand All @@ -25,15 +31,12 @@ use axum::{
Json, Router,
};

mod errors;
mod openapi;
mod v1;

pub fn serve_api(app: Arc<AppState>) -> Router {
Router::new()
.merge(Redoc::with_url("/redoc", ApiDoc::openapi()))
.route("/health", get(health_handler))
.nest("/api/v1", v1_routes())
.nest("/api/{version}/artifacts", artifacts_routes())
.nest("/api/{version}/notifiers", notifiers_routes())
.fallback(error_404_handler)
.layer(cors_layer())
.layer(middleware::from_fn(logging_middleware))
Expand All @@ -52,12 +55,12 @@ fn cors_layer() -> CorsLayer {
.allow_methods([Method::GET, Method::POST])
.allow_headers([ACCEPT, CONTENT_TYPE])
}
pub async fn error_404_handler(request: Request) -> ResourceError {
pub async fn error_404_handler(request: Request) -> ApiError {
tracing::error!("route not found: {:?}", request);
RootError::NotFound(format!("{:?}", request.uri())).into()
}

pub async fn health_handler() -> Result<impl IntoResponse, ResourceError> {
pub async fn health_handler() -> Result<impl IntoResponse, ApiError> {
Ok(Json(json!({"status": "healthy"})))
}

Expand All @@ -67,11 +70,11 @@ enum RootError {
NotFound(String),
}

impl From<RootError> for ResourceError {
impl From<RootError> for ApiError {
fn from(value: RootError) -> Self {
let message = value.to_string();
match value {
RootError::NotFound(path) => ResourceError::new(StatusCode::NOT_FOUND)
RootError::NotFound(path) => ApiError::new(StatusCode::NOT_FOUND)
.message(message)
.path(&path),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use std::sync::Arc;

use axum::{
extract::{Path, State},
extract::{OriginalUri, Path, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use thiserror::Error;
use tracing::trace;
use utoipa::OpenApi;
use veno_core::app::AppState;

use crate::resources::errors::ResourceError;
use crate::api::{errors::ApiError, version::ApiVersion};

use super::model::NotifierResponse;

Expand All @@ -25,7 +26,11 @@ pub struct V1NotifiersApi;
(status= OK, description = "Get all notifier configuration", body = Vec<NotifierResponse>)
)
)]
pub async fn all_notifiers(State(app): State<Arc<AppState>>) -> impl IntoResponse {
pub async fn all_notifiers(
version: ApiVersion,
State(app): State<Arc<AppState>>,
) -> impl IntoResponse {
trace!("Using API version: {}", version);
let notifiers: Vec<NotifierResponse> = app
.notifiers
.iter()
Expand All @@ -42,9 +47,12 @@ pub async fn all_notifiers(State(app): State<Arc<AppState>>) -> impl IntoRespons
)
)]
pub async fn notifier_for_id(
Path(notifier_id): Path<String>,
version: ApiVersion,
original_uri: OriginalUri,
Path((_version, notifier_id)): Path<(String, String)>,
State(app): State<Arc<AppState>>,
) -> Result<impl IntoResponse, ResourceError> {
) -> Result<impl IntoResponse, ApiError> {
trace!("Using API version: {}", version);
let notifier = app
.notifiers
.iter()
Expand All @@ -55,9 +63,9 @@ pub async fn notifier_for_id(
let response_boddy = NotifierResponse::from(notifier.clone());
Ok((StatusCode::OK, Json(response_boddy)).into_response())
}
None => Err(NotifierError::NotFoundWithParam {
None => Err(NotifierError::NotFoundParam {
param: notifier_id.clone(),
path: format!("/api/v1/notifiers/{notifier_id}"),
path: original_uri.path(),
}
.into()),
}
Expand All @@ -70,26 +78,25 @@ pub async fn notifier_for_id(
(status= OK, description = "Runs all notifiers")
)
)]
pub async fn notify(State(app): State<Arc<AppState>>) -> impl IntoResponse {
pub async fn notify(version: ApiVersion, State(app): State<Arc<AppState>>) -> impl IntoResponse {
trace!("Using API version: {}", version);
app.notify().await;
StatusCode::OK
}

#[derive(Debug, Error)]
pub enum NotifierError {
pub enum NotifierError<'a> {
#[error("The Notifier with the id={param} was not found.")]
NotFoundWithParam { param: String, path: String },
NotFoundParam { param: String, path: &'a str },
}

impl From<NotifierError> for ResourceError {
impl<'a> From<NotifierError<'a>> for ApiError {
fn from(err: NotifierError) -> Self {
let message = err.to_string();
match err {
NotifierError::NotFoundWithParam { param: _, path } => {
ResourceError::new(StatusCode::NOT_FOUND)
.message(message)
.path(format!("{}", path).as_str())
}
NotifierError::NotFoundParam { param: _, path } => ApiError::new(StatusCode::NOT_FOUND)
.message(message)
.path(path),
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::resources::v1::artifacts::handlers::V1ArtifactsApi;
use crate::resources::v1::notifiers::handlers::V1NotifiersApi;
use utoipa::OpenApi;

use crate::api::{artifacts::handlers::V1ArtifactsApi, notifiers::handlers::V1NotifiersApi};

#[derive(OpenApi)]
#[openapi(
nest(
Expand Down
Loading