From e7e55514a24ddf8d792df4b51e35d23360baaafe Mon Sep 17 00:00:00 2001 From: Dominic Date: Tue, 5 May 2020 00:34:19 +0200 Subject: [PATCH] support scopes inside openapi router (implements #5) --- .gitlab-ci.yml | 2 +- gotham_restful/src/openapi/builder.rs | 2 +- gotham_restful/src/openapi/router.rs | 47 ++++++++++---- gotham_restful/src/routing.rs | 1 + .../tests/openapi_supports_scope.rs | 61 +++++++++++++++++++ 5 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 gotham_restful/tests/openapi_supports_scope.rs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6ba7d54..5ec18d8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,7 +12,7 @@ test-default: before_script: - cargo -V script: - - cargo test --workspace --lib + - cargo test --workspace --tests cache: paths: - cargo/ diff --git a/gotham_restful/src/openapi/builder.rs b/gotham_restful/src/openapi/builder.rs index 0ca4cb1..bf81caa 100644 --- a/gotham_restful/src/openapi/builder.rs +++ b/gotham_restful/src/openapi/builder.rs @@ -14,7 +14,7 @@ pub struct OpenapiInfo pub urls : Vec } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct OpenapiBuilder { pub openapi : Arc> diff --git a/gotham_restful/src/openapi/router.rs b/gotham_restful/src/openapi/router.rs index 86d92cb..6dd7a13 100644 --- a/gotham_restful/src/openapi/router.rs +++ b/gotham_restful/src/openapi/router.rs @@ -19,13 +19,36 @@ pub trait GetOpenapi #[derive(Debug)] pub struct OpenapiRouter<'a, D> { - pub router : &'a mut D, - pub openapi_builder : &'a mut OpenapiBuilder + pub(crate) router : &'a mut D, + pub(crate) scope : Option<&'a str>, + pub(crate) openapi_builder : &'a mut OpenapiBuilder } macro_rules! implOpenapiRouter { ($implType:ident) => { - + + impl<'a, 'b, C, P> OpenapiRouter<'a, $implType<'b, C, P>> + where + C : PipelineHandleChain

+ Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static + { + pub fn scope(&mut self, path : &str, callback : F) + where + F : FnOnce(&mut OpenapiRouter<'_, ScopeBuilder<'_, C, P>>) + { + let mut openapi_builder = self.openapi_builder.clone(); + let new_scope = self.scope.map(|scope| format!("{}/{}", scope, path).replace("//", "/")); + self.router.scope(path, |router| { + let mut router = OpenapiRouter { + router, + scope: Some(new_scope.as_ref().map(String::as_ref).unwrap_or(path)), + openapi_builder: &mut openapi_builder + }; + callback(&mut router); + }); + } + } + impl<'a, 'b, C, P> GetOpenapi for OpenapiRouter<'a, $implType<'b, C, P>> where C : PipelineHandleChain

+ Copy + Send + Sync + 'static, @@ -57,7 +80,7 @@ macro_rules! implOpenapiRouter { { let schema = (self.0).openapi_builder.add_schema::(); - let path = format!("/{}", &self.1); + let path = format!("{}/{}", self.0.scope.unwrap_or_default(), self.1); let mut item = (self.0).openapi_builder.remove_path(&path); item.get = Some(OperationDescription::new::(schema).into_operation()); (self.0).openapi_builder.add_path(path, item); @@ -70,7 +93,7 @@ macro_rules! implOpenapiRouter { let schema = (self.0).openapi_builder.add_schema::(); let id_schema = (self.0).openapi_builder.add_schema::(); - let path = format!("/{}/{{id}}", &self.1); + let path = format!("{}/{}/{{id}}", self.0.scope.unwrap_or_default(), self.1); let mut item = (self.0).openapi_builder.remove_path(&path); item.get = Some(OperationDescription::new::(schema).add_path_param("id", id_schema).into_operation()); (self.0).openapi_builder.add_path(path, item); @@ -82,8 +105,8 @@ macro_rules! implOpenapiRouter { { let schema = (self.0).openapi_builder.add_schema::(); - let path = format!("/{}/search", &self.1); - let mut item = (self.0).openapi_builder.remove_path(&self.1); + let path = format!("{}/{}/search", self.0.scope.unwrap_or_default(), self.1); + let mut item = (self.0).openapi_builder.remove_path(&path); item.get = Some(OperationDescription::new::(schema).with_query_params(Handler::Query::schema()).into_operation()); (self.0).openapi_builder.add_path(path, item); @@ -98,7 +121,7 @@ macro_rules! implOpenapiRouter { let schema = (self.0).openapi_builder.add_schema::(); let body_schema = (self.0).openapi_builder.add_schema::(); - let path = format!("/{}", &self.1); + let path = format!("{}/{}", self.0.scope.unwrap_or_default(), self.1); let mut item = (self.0).openapi_builder.remove_path(&path); item.post = Some(OperationDescription::new::(schema).with_body::(body_schema).into_operation()); (self.0).openapi_builder.add_path(path, item); @@ -114,7 +137,7 @@ macro_rules! implOpenapiRouter { let schema = (self.0).openapi_builder.add_schema::(); let body_schema = (self.0).openapi_builder.add_schema::(); - let path = format!("/{}", &self.1); + let path = format!("{}/{}", self.0.scope.unwrap_or_default(), self.1); let mut item = (self.0).openapi_builder.remove_path(&path); item.put = Some(OperationDescription::new::(schema).with_body::(body_schema).into_operation()); (self.0).openapi_builder.add_path(path, item); @@ -131,7 +154,7 @@ macro_rules! implOpenapiRouter { let id_schema = (self.0).openapi_builder.add_schema::(); let body_schema = (self.0).openapi_builder.add_schema::(); - let path = format!("/{}/{{id}}", &self.1); + let path = format!("{}/{}/{{id}}", self.0.scope.unwrap_or_default(), self.1); let mut item = (self.0).openapi_builder.remove_path(&path); item.put = Some(OperationDescription::new::(schema).add_path_param("id", id_schema).with_body::(body_schema).into_operation()); (self.0).openapi_builder.add_path(path, item); @@ -143,7 +166,7 @@ macro_rules! implOpenapiRouter { { let schema = (self.0).openapi_builder.add_schema::(); - let path = format!("/{}", &self.1); + let path = format!("{}/{}", self.0.scope.unwrap_or_default(), self.1); let mut item = (self.0).openapi_builder.remove_path(&path); item.delete = Some(OperationDescription::new::(schema).into_operation()); (self.0).openapi_builder.add_path(path, item); @@ -156,7 +179,7 @@ macro_rules! implOpenapiRouter { let schema = (self.0).openapi_builder.add_schema::(); let id_schema = (self.0).openapi_builder.add_schema::(); - let path = format!("/{}/{{id}}", &self.1); + let path = format!("{}/{}/{{id}}", self.0.scope.unwrap_or_default(), self.1); let mut item = (self.0).openapi_builder.remove_path(&path); item.delete = Some(OperationDescription::new::(schema).add_path_param("id", id_schema).into_operation()); (self.0).openapi_builder.add_path(path, item); diff --git a/gotham_restful/src/routing.rs b/gotham_restful/src/routing.rs index 1714a32..1b0aa46 100644 --- a/gotham_restful/src/routing.rs +++ b/gotham_restful/src/routing.rs @@ -324,6 +324,7 @@ macro_rules! implDrawResourceRoutes { { let router = OpenapiRouter { router: self, + scope: None, openapi_builder: &mut OpenapiBuilder::new(info) }; block(router); diff --git a/gotham_restful/tests/openapi_supports_scope.rs b/gotham_restful/tests/openapi_supports_scope.rs new file mode 100644 index 0000000..02ba509 --- /dev/null +++ b/gotham_restful/tests/openapi_supports_scope.rs @@ -0,0 +1,61 @@ +#[cfg(feature = "openapi")] +mod openapi_supports_scope +{ + + +use gotham::{ + router::builder::*, + test::TestServer +}; +use gotham_restful::*; +use mime::TEXT_PLAIN; + +const RESPONSE : &[u8] = b"This is the only valid response."; + +#[derive(Resource)] +#[resource(read_all)] +struct FooResource; + +#[read_all(FooResource)] +fn read_all() -> Raw<&'static [u8]> +{ + Raw::new(RESPONSE, TEXT_PLAIN) +} + + +fn test_response(server : &TestServer, path : &str) +{ + let res = server.client().get(path).perform().unwrap().read_body().unwrap(); + let body : &[u8] = res.as_ref(); + assert_eq!(body, RESPONSE); +} + +#[test] +fn test() +{ + let info = OpenapiInfo { + title: "Test".to_owned(), + version: "1.2.3".to_owned(), + urls: Vec::new() + }; + let server = TestServer::new(build_simple_router(|router| { + router.with_openapi(info, |mut router| { + router.resource::("foo1"); + router.scope("/bar", |router| { + router.resource::("foo2"); + router.scope("/baz", |router| { + router.resource::("foo3"); + }) + }); + router.resource::("foo4"); + }); + })).unwrap(); + + test_response(&server, "http://localhost/foo1"); + test_response(&server, "http://localhost/bar/foo2"); + test_response(&server, "http://localhost/bar/baz/foo3"); + test_response(&server, "http://localhost/foo4"); +} + + +} // mod test