Skip to content

Commit

Permalink
feat: http example
Browse files Browse the repository at this point in the history
  • Loading branch information
ralvescosta committed May 2, 2024
1 parent 85d5bf7 commit 038fde9
Show file tree
Hide file tree
Showing 15 changed files with 373 additions and 0 deletions.
31 changes: 31 additions & 0 deletions examples/http_api/.env.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#App Configs
RUST_ENV=local
LOG_LEVEL=debug
SECRET_MANAGER=NONE

HOST_NAME=0.0.0.0
APP_PORT=4444

#Identity Server
IDENTITY_SERVER_URL=
IDENTITY_SERVER_REALM=
IDENTITY_SERVER_AUDIENCE=
IDENTITY_SERVER_ISSUER=
IDENTITY_SERVER_GRANT_TYPE=
IDENTITY_SERVER_CLIENT_ID=
IDENTITY_SERVER_CLIENT_SECRET=

#OTLP Configs
ENABLE_TRACES=false
TRACE_SERVICE_TYPE=HTTP
TRACE_HOST=localhost
TRACE_KEY=key
TRACE_EXPORT_TIMEOUT=60
TRACE_EXPORT_RATE_BASE=0.7

ENABLE_METRICS=false
METRIC_SERVICE_TYPE=HTTP
METRIC_HOST=localhost
METRIC_KEY=key
METRIC_EXPORT_TIMEOUT=60
METRIC_EXPORT_RATE_BASE=0.7
31 changes: 31 additions & 0 deletions examples/http_api/.env.prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#App Configs
RUST_ENV=local
LOG_LEVEL=debug
SECRET_MANAGER=NONE

HOST_NAME=0.0.0.0
APP_PORT=4444

#Identity Server
IDENTITY_SERVER_URL=
IDENTITY_SERVER_REALM=
IDENTITY_SERVER_AUDIENCE=
IDENTITY_SERVER_ISSUER=
IDENTITY_SERVER_GRANT_TYPE=
IDENTITY_SERVER_CLIENT_ID=
IDENTITY_SERVER_CLIENT_SECRET=

#OTLP Configs
ENABLE_TRACES=false
TRACE_SERVICE_TYPE=HTTP
TRACE_HOST=localhost
TRACE_KEY=key
TRACE_EXPORT_TIMEOUT=60
TRACE_EXPORT_RATE_BASE=0.7

ENABLE_METRICS=false
METRIC_SERVICE_TYPE=HTTP
METRIC_HOST=localhost
METRIC_KEY=key
METRIC_EXPORT_TIMEOUT=60
METRIC_EXPORT_RATE_BASE=0.7
31 changes: 31 additions & 0 deletions examples/http_api/.env.staging
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#App Configs
RUST_ENV=local
LOG_LEVEL=debug
SECRET_MANAGER=NONE

HOST_NAME=0.0.0.0
APP_PORT=4444

#Identity Server
IDENTITY_SERVER_URL=
IDENTITY_SERVER_REALM=
IDENTITY_SERVER_AUDIENCE=
IDENTITY_SERVER_ISSUER=
IDENTITY_SERVER_GRANT_TYPE=
IDENTITY_SERVER_CLIENT_ID=
IDENTITY_SERVER_CLIENT_SECRET=

#OTLP Configs
ENABLE_TRACES=false
TRACE_SERVICE_TYPE=HTTP
TRACE_HOST=localhost
TRACE_KEY=key
TRACE_EXPORT_TIMEOUT=60
TRACE_EXPORT_RATE_BASE=0.7

ENABLE_METRICS=false
METRIC_SERVICE_TYPE=HTTP
METRIC_HOST=localhost
METRIC_KEY=key
METRIC_EXPORT_TIMEOUT=60
METRIC_EXPORT_RATE_BASE=0.7
1 change: 1 addition & 0 deletions examples/http_api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
27 changes: 27 additions & 0 deletions examples/http_api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "http_api"
version = "0.1.0"
edition = "2021"

[dependencies]
configs = {git = "ssh://git@github.com/tointernet/ruskit.git", rev = "v0.3.0"}
configs-builder = {git = "ssh://git@github.com/tointernet/ruskit.git", rev = "v0.3.0"}
http-components = {git = "ssh://git@github.com/tointernet/ruskit.git", rev = "v0.3.0", features = ["auth"]}
http-server = {git = "ssh://git@github.com/tointernet/ruskit.git", rev = "v0.3.0", features = ["openapi"]}
auth = {git = "ssh://git@github.com/tointernet/ruskit.git", rev = "v0.3.0"}
logging = {git = "ssh://git@github.com/tointernet/ruskit.git", rev = "v0.3.0"}
traces = {git = "ssh://git@github.com/tointernet/ruskit.git", rev = "v0.3.0"}
metrics = {git = "ssh://git@github.com/tointernet/ruskit.git", rev = "v0.3.0"}
sql-pool = {git = "ssh://git@github.com/tointernet/ruskit.git", rev = "v0.3.0", features = ["postgres"] }
migrator = {git = "ssh://git@github.com/tointernet/ruskit.git", rev = "v0.3.0", features = ["postgres"] }
health-readiness = {git = "ssh://git@github.com/tointernet/ruskit.git", rev = "v0.3.0"}

actix-web = { version = "4.5.1" }
serde = { version = "1.0.200" }
serde_json = { version = "1.0.116" }
opentelemetry = { version = "0.22" }
async-trait = { version = "0.1.77" }
tracing = { version = "0.1.40" }
tokio = { version = "1.37.0", features = ["default", "rt-multi-thread", "macros", "signal"]}
utoipa = { version = "4.2.0" }
validator = { version = "0.18.1" }
5 changes: 5 additions & 0 deletions examples/http_api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# HTTP API Example

- Swagger
- Opentelemetry Trace with OTLP
- Opentelemetry Metrics with OTLP
52 changes: 52 additions & 0 deletions examples/http_api/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
mod openapi;
mod todos;

use actix_web::web::{Data, ServiceConfig};
use auth::{auth0::Auth0JwtManager, manager::JwtManager};
use configs::{Configs, Empty};
use configs_builder::ConfigBuilder;
use http_components::CustomServiceConfigure;
use http_server::server::HTTPServer;
use openapi::ApiDoc;
use std::{error::Error, sync::Arc};
use utoipa::OpenApi;

use todos::routes as todos_routes;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let cfgs = default_setup().await?;

let doc = ApiDoc::openapi();

let identity_configs = cfgs.identity.clone();

HTTPServer::new(&cfgs.app)
//if you need to share structs to all the controllers, use something similar
.custom_configure(CustomServiceConfigure::new(
move |cfg: &mut ServiceConfig| {
let auth0_manager: Arc<dyn JwtManager> = Auth0JwtManager::new(&identity_configs);
cfg.app_data(Data::<dyn JwtManager>::from(auth0_manager));
},
))
.custom_configure(todos_routes::basic_routes())
.openapi(&doc)
.start()
.await?;

Ok(())
}

async fn default_setup<'cfg>() -> Result<Configs<Empty>, Box<dyn Error>> {
let cfg = ConfigBuilder::new()
.trace()
.metric()
.identity_server()
.build::<Empty>()
.await?;

traces::provider::init(&cfg)?;
metrics::provider::init(&cfg)?;

Ok(cfg)
}
54 changes: 54 additions & 0 deletions examples/http_api/src/openapi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use crate::todos::{controllers as tc, viewmodels as tvm};
use http_components::viewmodels::HTTPError;
use utoipa::{
openapi::{
self,
security::{Http, HttpAuthScheme, SecurityScheme},
},
Modify, OpenApi,
};

#[derive(OpenApi)]
#[openapi(
paths(
tc::post, tc::get, tc::list, tc::delete,
),
components(
schemas(
HTTPError,
tvm::ToDoRequest, tvm::ToDoResponse,
)
),
tags(
(name = "examples", description = "Examples endpoints."),
),
modifiers(&SecurityAddon),
info(
title = "Hedro HTTP API",
version = "v0.0.1",
description = "Hedro HTTP API's built in rust"
),
)]
#[cfg_attr(debug_assertions, openapi(
servers(
(url = "http://localhost:4444", description = "Local server"),
),
))]
#[cfg_attr(not(debug_assertions), openapi(
servers(
(url = "https://stg-api.com.br", description = "Staging server"),
),
))]
pub struct ApiDoc;

pub struct SecurityAddon;

impl Modify for SecurityAddon {
fn modify(&self, openapi: &mut openapi::OpenApi) {
let components = openapi.components.as_mut().unwrap();
components.add_security_scheme(
"auth",
SecurityScheme::Http(Http::new(HttpAuthScheme::Bearer)),
)
}
}
3 changes: 3 additions & 0 deletions examples/http_api/src/todos/controllers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod todos;

pub use todos::{__path_delete, __path_get, __path_list, __path_post, delete, get, list, post};
103 changes: 103 additions & 0 deletions examples/http_api/src/todos/controllers/todos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use crate::todos::viewmodels::ToDoRequest;
use actix_web::{delete, get, post, web::Json, HttpRequest, HttpResponse, Responder};
use http_components::extractors::JwtAuthenticateExtractor;

/// Request to create a new ToDo.
///
/// If the request was registered correctly this endpoint will return 201 Accepted and 4xx/5xx if some error occur.
///
#[utoipa::path(
post,
path = "",
context_path = "/v1/todos",
tag = "todos",
request_body = ToDoRequest,
responses(
(status = 202, description = "Todo requested successfully", body = ToDoResponse),
(status = 400, description = "Bad request", body = HTTPError),
(status = 401, description = "Unauthorized", body = HTTPError),
(status = 403, description = "Forbidden", body = HTTPError),
(status = 500, description = "Internal error", body = HTTPError)
),
security()
)]
#[post("")]
pub async fn post(_thing: Json<ToDoRequest>, _auth: JwtAuthenticateExtractor) -> impl Responder {
HttpResponse::Ok().body("post::things")
}

/// Request to get all ToDo's that was created.
///
/// If the request was process correctly this endpoint will return 200 Ok and 4xx/5xx if some error occur.
///
#[utoipa::path(
get,
path = "",
context_path = "/v1/todos",
tag = "todos",
responses(
(status = 200, description = "Success", body = Vec<ToDoResponse>),
(status = 400, description = "Bad request", body = HTTPError),
(status = 401, description = "Unauthorized", body = HTTPError),
(status = 403, description = "Forbidden", body = HTTPError),
(status = 500, description = "Internal error", body = HTTPError)
),
security(
("auth" = [])
)
)]
#[get("")]
pub async fn list(_req: HttpRequest, _: JwtAuthenticateExtractor) -> impl Responder {
HttpResponse::Ok().body("list::things")
}

/// Request to get a specific ToDo by ID.
///
/// If the request was process correctly this endpoint will return 200 Ok and 4xx/5xx if some error occur.
///
#[utoipa::path(
get,
path = "/{id}",
context_path = "/v1/todos",
tag = "todos",
responses(
(status = 200, description = "Success", body = ToDoResponse),
(status = 400, description = "Bad request", body = HTTPError),
(status = 401, description = "Unauthorized", body = HTTPError),
(status = 403, description = "Forbidden", body = HTTPError),
(status = 500, description = "Internal error", body = HTTPError)
),
security(
("auth" = [])
)
)]
#[get("/{id}")]
pub async fn get(_req: HttpRequest, _: JwtAuthenticateExtractor) -> impl Responder {
HttpResponse::Ok().body("get::things")
}

/// Request to delete a specific ToDo by ID.
///
/// If the request was process correctly this endpoint will return 200 Ok and 4xx/5xx if some error occur.
///
#[utoipa::path(
delete,
path = "/{id}",
context_path = "/v1/todos",
tag = "todos",
request_body = ToDoRequest,
responses(
(status = 200, description = "Deleted", body = ToDoResponse),
(status = 400, description = "Bad request", body = HTTPError),
(status = 401, description = "Unauthorized", body = HTTPError),
(status = 403, description = "Forbidden", body = HTTPError),
(status = 500, description = "Internal error", body = HTTPError)
),
security(
("auth" = [])
)
)]
#[delete("/{id}")]
pub async fn delete(_req: HttpRequest, _: JwtAuthenticateExtractor) -> impl Responder {
HttpResponse::Ok().body("delete::things")
}
3 changes: 3 additions & 0 deletions examples/http_api/src/todos/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod controllers;
pub mod routes;
pub mod viewmodels;
3 changes: 3 additions & 0 deletions examples/http_api/src/todos/routes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod todos;

pub use todos::basic_routes;
18 changes: 18 additions & 0 deletions examples/http_api/src/todos/routes/todos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::todos::controllers;
use actix_web::web::{self, ServiceConfig};
use http_components::CustomServiceConfigure;

pub fn basic_routes() -> CustomServiceConfigure {
CustomServiceConfigure::new(|cfg: &mut ServiceConfig| {
cfg.service(
web::scope("/v1/todos")
// If you would like to add authentication middleware for all this routes bellow, just use the middleware as follow:
//
// .wrap(http_components::middlewares::authentication::AuthenticationMiddleware)
.service(controllers::post)
.service(controllers::list)
.service(controllers::get)
.service(controllers::delete),
);
})
}
3 changes: 3 additions & 0 deletions examples/http_api/src/todos/viewmodels/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod todos;

pub use todos::{ToDoRequest, ToDoResponse};
8 changes: 8 additions & 0 deletions examples/http_api/src/todos/viewmodels/todos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

#[derive(Serialize, Deserialize, ToSchema)]
pub struct ToDoRequest {}

#[derive(Serialize, Deserialize, ToSchema)]
pub struct ToDoResponse {}

0 comments on commit 038fde9

Please sign in to comment.