diff --git a/Cargo.lock b/Cargo.lock index f5e6fbc..3e46497 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -882,7 +882,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openapi-mocker" -version = "0.1.0" +version = "0.1.1" dependencies = [ "actix-rt", "actix-web", diff --git a/Cargo.toml b/Cargo.toml index 71bb700..acc3cf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openapi-mocker" -version = "0.1.0" +version = "0.1.1" edition = "2021" repository = "https://github.com/pachecoio/openapi-mocker" keywords = ["openapi", "mock", "mock-server"] diff --git a/src/lib.rs b/src/lib.rs index 0496cca..20a0042 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,39 @@ -use std::path::PathBuf; - +//! # OpenAPI Mock Server +//! +//! `openapi-mocker` is a simple mock server for OpenAPI 3.0 specs. +//! It can be used to quickly create a mock server for an OpenAPI spec. +//! +//! The server will respond with example responses defined in the spec. +//! If no example is defined, it will respond with an empty JSON object. +//! The server will respond with a 200 status code by default, but you can +//! specify a different status code in the URL. +//! +//! ## Usage +//! ```sh +//! openapi-mocker [port] +//! ``` +//! * `` - Path to the OpenAPI spec file +//! * `[port]` - Port to bind the server to (default: 8080) +//! +//! ## Example +//! ```sh +//! openapi-mocker tests/testdata/petstore.yaml +//! ``` +//! This will start a server on port 8080 with the Petstore spec. +//! You can then make requests to the server to get example responses. +//! For example, to get a list of pets: +//! ```sh +//! curl http://localhost:8080/200/pets +//! ``` +//! This will return a list of pets from the example response in the spec. +//! You can also specify a different status code in the URL: +//! ```sh +//! curl http://localhost:8080/404/pets +//! ``` +//! This will return a 404 status code with the example response for a 404 error. +//! use clap::Parser; - +use std::path::PathBuf; pub mod server; pub mod spec; diff --git a/src/server.rs b/src/server.rs index f34a753..5c9ce9f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -5,10 +5,12 @@ use actix_web::{ use crate::spec::{load_endpoint, load_example, load_response, Method}; +/// Application state for the Actix Web server. pub struct AppState { pub spec: oas3::OpenApiV3Spec, } +/// Returns a new Actix Web scope with all the routes for the server. pub fn get_scope() -> Scope { web::scope("") .route("/{status}/{tail:.*}", get().to(handle_all)) diff --git a/src/spec.rs b/src/spec.rs index 1294812..41b6a22 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -2,6 +2,7 @@ use oas3::spec::{Operation, PathItem, Response}; pub type SpecResult = Result>; +/// HTTP methods pub enum Method { Get, Post, @@ -29,10 +30,43 @@ impl From<&str> for Method { } } +/// Load an OpenAPI spec from a file +/// +/// # Arguments +/// * `path` - Path to the OpenAPI spec file +/// +/// # Returns +/// An OpenAPI spec object +/// +/// # Example +/// ``` +/// use openapi_mocker::spec::load_spec; +/// +/// let spec = load_spec("tests/testdata/petstore.yaml"); +/// assert_eq!(spec.openapi, "3.0.0"); +/// ``` pub fn load_spec(path: &str) -> oas3::OpenApiV3Spec { oas3::from_path(path).unwrap() } +/// Load an endpoint from an OpenAPI spec +/// +/// # Arguments +/// * `spec` - OpenAPI spec object +/// * `path` - Path to the endpoint +/// * `method` - HTTP method +/// +/// # Returns +/// An OpenAPI operation object +/// +/// # Example +/// ``` +/// use openapi_mocker::spec::{load_spec, load_endpoint, Method}; +/// +/// let spec = load_spec("tests/testdata/petstore.yaml"); +/// let op = load_endpoint(&spec, "/pets", Method::Get).unwrap(); +/// assert_eq!(op.operation_id, Some("listPets".to_string())); +/// ``` pub fn load_endpoint( spec: &oas3::OpenApiV3Spec, path: &str, @@ -46,6 +80,14 @@ pub fn load_endpoint( Ok(op.clone()) } +/// Load a method from a PathItem +/// +/// # Arguments +/// * `method` - HTTP method +/// * `path_item` - PathItem object +/// +/// # Returns +/// An Option with the Operation object fn load_method<'a>(method: Method) -> impl Fn(&PathItem) -> Option<&Operation> + 'a { move |path_item: &PathItem| match method { Method::Get => path_item.get.as_ref(), @@ -59,6 +101,25 @@ fn load_method<'a>(method: Method) -> impl Fn(&PathItem) -> Option<&Operation> + } } +/// Load a response from an OpenAPI operation +/// +/// # Arguments +/// * `spec` - OpenAPI spec object +/// * `op` - OpenAPI operation object +/// * `status` - HTTP status code +/// +/// # Returns +/// An OpenAPI response object +/// +/// # Example +/// ``` +/// use openapi_mocker::spec::{load_spec, load_endpoint, load_response, Method}; +/// +/// let spec = load_spec("tests/testdata/petstore.yaml"); +/// let op = load_endpoint(&spec, "/pets", Method::Get).unwrap(); +/// let response = load_response(&spec, &op, 200).unwrap(); +/// assert_eq!(response.description, Some("A paged array of pets".to_string())); +/// ``` pub fn load_response( spec: &oas3::OpenApiV3Spec, op: &Operation, @@ -73,6 +134,40 @@ pub fn load_response( } } +/// Load an example from an OpenAPI response +/// +/// # Arguments +/// * `spec` - OpenAPI spec object +/// * `response` - OpenAPI response object +/// * `content_type` - Content type +/// +/// # Returns +/// A JSON value with the example +/// +/// # Example +/// ``` +/// use openapi_mocker::spec::{load_spec, load_endpoint, load_response, load_example, Method}; +/// use serde_json::json; +/// +/// let spec = load_spec("tests/testdata/petstore.yaml"); +/// let op = load_endpoint(&spec, "/pets", Method::Get).unwrap(); +/// let response = load_response(&spec, &op, 200).unwrap(); +/// let content_type = "application/json"; +/// let example = load_example(&spec, &response, content_type).unwrap(); +/// let expected = json!([ +/// { +/// "id": 1, +/// "name": "doggie", +/// "tag": "dog" +/// }, +/// { +/// "id": 2, +/// "name": "kitty", +/// "tag": "cat" +/// } +/// ]); +/// assert_eq!(example, expected); +/// ``` pub fn load_example( spec: &oas3::OpenApiV3Spec, response: &Response,