From 2dd361ce7e8190dd1edd8b4e36d024776f96bafa Mon Sep 17 00:00:00 2001 From: Martin Berg Alstad Date: Thu, 20 Jun 2024 13:13:26 +0200 Subject: [PATCH] Not found page with html and moved utils into index and split http requests to separate files. Static dir for hosting html files. Helper function for loading HTML. Changed location where openapi.html is generated and updated dockerfile --- .idea/webResources.xml | 14 ++ Dockerfile | 8 +- http/index.http | 17 ++ http/simplify.http | 38 --- http/table.http | 29 +++ spec/package.json | 2 +- src/config.rs | 6 + src/main.rs | 3 +- src/resources/static/not-found.html | 60 +++++ src/resources/static/openapi.html | 351 ++++++++++++++++++++++++++++ src/routing/routes/index.rs | 44 ++-- src/routing/routes/mod.rs | 2 - src/routing/routes/util.rs | 18 -- src/utils/axum.rs | 25 ++ src/utils/mod.rs | 2 +- 15 files changed, 537 insertions(+), 82 deletions(-) create mode 100644 .idea/webResources.xml create mode 100644 http/index.http create mode 100644 http/table.http create mode 100644 src/resources/static/not-found.html create mode 100644 src/resources/static/openapi.html delete mode 100644 src/routing/routes/util.rs diff --git a/.idea/webResources.xml b/.idea/webResources.xml new file mode 100644 index 0000000..bfbe3fe --- /dev/null +++ b/.idea/webResources.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index c671e3e..bb5d388 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,9 @@ COPY ./src ./src RUN rm ./target/release/deps/simplify_truths* RUN cargo build --release -FROM node:20.14.0 as spec +FROM node:20.14.0 as static + +COPY ./src/resources/static ./src/resources/static WORKDIR /spec @@ -33,8 +35,8 @@ LABEL authors="Martin Berg Alstad" # copy the build artifact from the build stage COPY --from=build /simplify_truths/target/release/simplify_truths . -# copy the generated html file for REDOC documentation -COPY --from=spec /spec/dist/index.html ./openapi/index.html +# copy the static html files +COPY --from=static ./src/resources/static ./static EXPOSE 8000 diff --git a/http/index.http b/http/index.http new file mode 100644 index 0000000..4371bf4 --- /dev/null +++ b/http/index.http @@ -0,0 +1,17 @@ +### GET index page + +GET {{url}} + +### GET OpenAPI page + +GET {{url}}/openapi + +### GET should fallback to 404 page + +GET {{url}}/something-that-does-not-exist + +> {% + client.test("Response status is 404", () => { + client.assert(response.status === 404, "Response status is not 404"); + }); +%} diff --git a/http/simplify.http b/http/simplify.http index dea1797..32c5f91 100644 --- a/http/simplify.http +++ b/http/simplify.http @@ -1,11 +1,3 @@ -### GET index page - -GET {{url}} - -### GET OpenAPI page - -GET {{url}}/openapi - ### GET Atomic Expression GET {{url}}/simplify/A @@ -131,33 +123,3 @@ GET {{url}}/simplify/{{expression}} ### GET with simplify="true" GET {{url}}/simplify/A?simplify=true&hide=NONE&sort=DEFAULT&caseSensitive=false&hideIntermediate=false - -### GET only table - -GET {{url}}/table/A - -> {% - client.test("Response body contains only the truth table", () => { - client.assert(response.body.truthTable, "Response body does not contain the truth table") - }); -%} - -### GET table and hide intermediate values - -< {% - import {expression} from "./common"; - - expression("A & B | C") -%} -GET {{url}}/table/{{expression}}?hideIntermediateSteps=true - -> {% - client.test("Response body does not contain intermediate steps", () => { - const header = response.body.truthTable.header; - const matrix = response.body.truthTable.truthMatrix; - client.assert(header.length === 4, "Response body contains intermediate steps") - for (let i = 0; i < matrix.length; i++) { - client.assert(matrix[i].length === 4, "Response body contains intermediate steps") - } - }); -%} diff --git a/http/table.http b/http/table.http new file mode 100644 index 0000000..b822da7 --- /dev/null +++ b/http/table.http @@ -0,0 +1,29 @@ +### GET only table + +GET {{url}}/table/A + +> {% + client.test("Response body contains only the truth table", () => { + client.assert(response.body.truthTable, "Response body does not contain the truth table") + }); +%} + +### GET table and hide intermediate values + +< {% + import {expression} from "./common"; + + expression("A & B | C") +%} +GET {{url}}/table/{{expression}}?hideIntermediateSteps=true + +> {% + client.test("Response body does not contain intermediate steps", () => { + const header = response.body.truthTable.header; + const matrix = response.body.truthTable.truthMatrix; + client.assert(header.length === 4, "Response body contains intermediate steps") + for (let i = 0; i < matrix.length; i++) { + client.assert(matrix[i].length === 4, "Response body contains intermediate steps") + } + }); +%} diff --git a/spec/package.json b/spec/package.json index f914812..18f7f77 100644 --- a/spec/package.json +++ b/spec/package.json @@ -5,7 +5,7 @@ "author": "Martin Berg Alstad", "scripts": { "tsp-compile": "tsp compile . --output-dir dist", - "redoc-build": "redocly build-docs dist/@typespec/openapi3/openapi.v2.yaml --output dist/index.html" + "redoc-build": "redocly build-docs dist/@typespec/openapi3/openapi.v2.yaml --output ../src/resources/static/openapi.html" }, "dependencies": { "@typespec/compiler": "latest", diff --git a/src/config.rs b/src/config.rs index 89fce49..f53240a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1 +1,7 @@ pub const PORT: u16 = 8000; +pub const IS_DEV: bool = cfg!(debug_assertions); +pub const RESOURCE_DIR: &str = if IS_DEV { + "./src/resources/static" +} else { + "./static" +}; diff --git a/src/main.rs b/src/main.rs index 3340dd4..eb21eae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use tower_http::trace::TraceLayer; use tracing::Level; use crate::routing::routes::*; +use crate::routing::routes::index::not_found; mod expressions; mod parsing; @@ -29,7 +30,7 @@ async fn main() { let routes = simplify::router() .merge(table::router()) .merge(index::router()) - .merge(util::router()); + .fallback(not_found); let app = routes .layer(CorsLayer::new().allow_origin(Any)) diff --git a/src/resources/static/not-found.html b/src/resources/static/not-found.html new file mode 100644 index 0000000..930a8b9 --- /dev/null +++ b/src/resources/static/not-found.html @@ -0,0 +1,60 @@ + + + + + + + 404 Not Found + + + +
+

404

+

Oops! Page not found.

+ Go back to the documentation +
+ + diff --git a/src/resources/static/openapi.html b/src/resources/static/openapi.html new file mode 100644 index 0000000..d53a584 --- /dev/null +++ b/src/resources/static/openapi.html @@ -0,0 +1,351 @@ + + + + + + Simplify Truth Expressions + + + + + + + + + +

Simplify Truth Expressions (v2)

Download OpenAPI specification:Download

Simplify_simplify

path Parameters
exp
required
string
query Parameters
object (SimplifyOptions)

Responses

Response samples

Content type
application/json
{
  • "before": "string",
  • "after": "string",
  • "orderOfOperations": [ ],
  • "expression": {
    }
}
+ + + + diff --git a/src/routing/routes/index.rs b/src/routing/routes/index.rs index 7e292bf..1373e63 100644 --- a/src/routing/routes/index.rs +++ b/src/routing/routes/index.rs @@ -1,14 +1,17 @@ -use axum::body::Body; +use axum::extract::Path; use axum::http::StatusCode; -use axum::response::{Html, IntoResponse, Response}; -use tokio::fs::File; -use tokio_util::io::ReaderStream; +use axum::response::{IntoResponse, Response}; +use crate::expressions::expression::Expression; use crate::router; +use crate::routing::error::{Error, ErrorKind}; +use crate::routing::response::IsLegalResponse; +use crate::utils::axum::load_html; router!( get "/" => index, - get "/openapi" => open_api + get "/openapi" => open_api, + get "/is-valid/:exp" => is_valid ); async fn index() -> &'static str { @@ -16,17 +19,22 @@ async fn index() -> &'static str { } async fn open_api() -> Response { - let file_path = if cfg!(debug_assertions) { - "./spec/dist/index.html" - } else { - "./openapi/index.html" - }; - let file = match File::open(file_path).await { - Ok(file) => file, - Err(err) => return (StatusCode::NOT_FOUND, format!("File not found: {err}")).into_response(), - }; - let stream = ReaderStream::new(file); - let body = Body::from_stream(stream); - - Html(body).into_response() + match load_html("openapi.html").await { + Ok(html) => html.into_response(), + Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, err).into_response() + } +} + +async fn is_valid(Path(path): Path) -> Response { + match Expression::try_from(path.as_str()) { + Ok(_) => IsLegalResponse { is_legal: true }.into_response(), + Err(error) => Error::new(error.to_string(), ErrorKind::InvalidExpression).into_response() + } +} + +pub(crate) async fn not_found() -> Response { + match load_html("not-found.html").await { + Ok(html) => (StatusCode::NOT_FOUND, html).into_response(), + Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, err).into_response() + } } diff --git a/src/routing/routes/mod.rs b/src/routing/routes/mod.rs index e476db7..46e0b20 100644 --- a/src/routing/routes/mod.rs +++ b/src/routing/routes/mod.rs @@ -3,5 +3,3 @@ pub(crate) mod index; pub(crate) mod simplify; pub(crate) mod table; - -pub(crate) mod util; diff --git a/src/routing/routes/util.rs b/src/routing/routes/util.rs deleted file mode 100644 index 08f3e69..0000000 --- a/src/routing/routes/util.rs +++ /dev/null @@ -1,18 +0,0 @@ -use axum::extract::Path; -use axum::response::{IntoResponse, Response}; - -use crate::expressions::expression::Expression; -use crate::router; -use crate::routing::error::{Error, ErrorKind}; -use crate::routing::response::IsLegalResponse; - -router!( - get "/is-legal/:exp" => is_legal -); - -async fn is_legal(Path(path): Path) -> Response { - match Expression::try_from(path.as_str()) { - Ok(_) => IsLegalResponse { is_legal: true }.into_response(), - Err(error) => Error::new(error.to_string(), ErrorKind::InvalidExpression).into_response() - } -} diff --git a/src/utils/axum.rs b/src/utils/axum.rs index 6efaa15..10d694a 100644 --- a/src/utils/axum.rs +++ b/src/utils/axum.rs @@ -1,3 +1,10 @@ +use axum::body::Body; +use axum::response::Html; +use tokio::fs::File; +use tokio_util::io::ReaderStream; + +use crate::config::RESOURCE_DIR; + /// Create an axum router function with the given body or routes. /// # Examples /// ``` @@ -40,3 +47,21 @@ macro_rules! routes { $(.route($route, axum::routing::$method($func)))* }; } + +/// Load an HTML file from the given file path, relative to the resource directory. +/// # Arguments +/// * `file_path` - The path to the HTML file. +/// # Returns +/// The HTML file as a `Html` object containing the content-type 'text/html' or an error message if the file is not found or cannot be read. +/// # Examples +/// ``` +/// let html = load_html("openapi.html").await.unwrap(); +/// ``` +pub async fn load_html(file_path: &str) -> Result, String> { + let file = match File::open(format!("{}/{}", RESOURCE_DIR, file_path)).await { + Ok(file) => file, + Err(err) => return Err(format!("File not found: {err}")), + }; + let stream = ReaderStream::new(file); + Ok(Html(Body::from_stream(stream))) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index d23a11a..4ac8329 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,3 @@ pub mod array; pub mod serialize; -mod axum; \ No newline at end of file +pub mod axum; \ No newline at end of file