Skip to content

Commit

Permalink
Implement API Gateway skeleton and unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ayeletstarkware committed Mar 21, 2024
1 parent 80cd54e commit b66023d
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 1 deletion.
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,13 @@ unused = "deny"

[workspace.lints.clippy]
as_conversions = "deny"

[workspace.dependencies]
hyper = "0.13.9"
tokio = { version = "0.2", features = ["macros"] }
thiserror = "1.0"
starknet_api = "0.8.0"
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0"
assert_matches = "1.5.0"

7 changes: 7 additions & 0 deletions crates/gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ repository.workspace = true
license.workspace = true

[dependencies]
hyper.workspace = true
tokio.workspace = true
thiserror.workspace = true
serde.workspace = true
serde_json.workspace = true
starknet_api.workspace = true
assert_matches.workspace = true

[lints]
workspace = true
21 changes: 21 additions & 0 deletions crates/gateway/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use thiserror::Error;

#[derive(Debug, Error)]
pub enum GatewayError {
#[error(transparent)]
ConfigError(#[from] GatewayConfigError),
#[error(transparent)]
HTTPError(#[from] hyper::http::Error),
#[error("Internal server error")]
InternalServerError,
#[error("Invalid transaction format")]
InvalidTransactionFormat,
#[error("Error while starting the server")]
ServerStartError,
}

#[derive(Debug, Error)]
pub enum GatewayConfigError {
#[error("Server address is not an bind IP address: {0}")]
InvalidServerBindAddress(String),
}
71 changes: 71 additions & 0 deletions crates/gateway/src/gateway.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use crate::errors::{GatewayConfigError, GatewayError};
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use std::convert::Infallible;
use std::net::SocketAddr;
use std::str::FromStr;

#[cfg(test)]
#[path = "gateway_test.rs"]
pub mod gateway_test;

const NOT_FOUND_RESPONSE: &str = "Not found.";
type RequestBody = Request<Body>;
type ResponseBody = Response<Body>;
pub type GatewayResult = Result<(), GatewayError>;

pub struct Gateway {
pub gateway_config: GatewayConfig,
}

impl Gateway {
pub async fn build_server(&self) -> GatewayResult {
let addr = SocketAddr::from_str(&self.gateway_config.bind_address).map_err(|_| {
GatewayConfigError::InvalidServerBindAddress(self.gateway_config.bind_address.clone())
})?;

let make_service = make_service_fn(|_conn| async {
let ctx = HandleContext {};
Ok::<_, Infallible>(service_fn(move |req| handle_request(ctx.clone(), req)))
});

Server::bind(&addr)
.serve(make_service)
.await
.map_err(|_| GatewayError::ServerStartError)?;

Ok(())
}
}

pub struct GatewayConfig {
pub bind_address: String,
}

/// Stores routing information for request's handling.
#[derive(Clone)]
struct HandleContext {}

async fn handle_request(
_ctx: HandleContext,
request: RequestBody,
) -> Result<Response<Body>, GatewayError> {
let (parts, _body) = request.into_parts();
let response = match (parts.method, parts.uri.path()) {
(Method::GET, "/is_alive") => is_alive(),
_ => response(StatusCode::NOT_FOUND, NOT_FOUND_RESPONSE.to_string()),
};
response
}

// This function hasn't been implemented yet. It might need a HandleContext parameter to verify if
// the server is alive.
fn is_alive() -> Result<ResponseBody, GatewayError> {
unimplemented!("Future handling should be implemented here.");
}

fn response(status: StatusCode, body_content: String) -> Result<Response<Body>, GatewayError> {
Ok(Response::builder()
.status(status)
.body(Body::from(body_content))?)
}
18 changes: 18 additions & 0 deletions crates/gateway/src/gateway_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::gateway::handle_request;
use crate::gateway::HandleContext;
use hyper::{Body, Request};

#[tokio::test]
async fn test_invalid_request() {
// Create a sample GET request for an invalid path
let request = Request::get("/some_invalid_path")
.body(Body::empty())
.unwrap();
let response = handle_request(HandleContext {}, request).await.unwrap();

assert_eq!(response.status(), 404);
assert_eq!(
String::from_utf8_lossy(&hyper::body::to_bytes(response.into_body()).await.unwrap()),
"Not found."
);
}
3 changes: 2 additions & 1 deletion crates/gateway/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@

pub mod errors;
pub mod gateway;

0 comments on commit b66023d

Please sign in to comment.