Skip to content

Commit

Permalink
refactor(rest): improve rest versions
Browse files Browse the repository at this point in the history
Part of the motivation for this is that we should not mix api versions
 on the same openapi spec.
This change makes adding api versions easier by wrapping each version
 module with its own openapi wrapper which then generates and exposes
 versioned api specs and versioned swagger-ui's.

While we're here, make use of the openapi base path to clean up the
openapi paths and allow for easier to read auto generated code.
Also clean up some redundant type definitions.
  • Loading branch information
tiagolobocastro committed Feb 4, 2021
1 parent 9bce95f commit 2302520
Show file tree
Hide file tree
Showing 18 changed files with 200 additions and 246 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[patch.crates-io]
h2 = { git = "https://github.com/gila/h2", branch = "v0.2.7"}
partition-identity = { git = "https://github.com/openebs/partition-identity.git" }
#paperclip = { git = "https://github.com/tiagolobocastro/paperclip.git", branch = "tuple_struct_doc", features = ["actix3-nightly"] }

[profile.dev]
panic = "abort"
Expand Down
41 changes: 32 additions & 9 deletions mayastor-macros/actix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ impl Method {
// so they can be used with the paperclip::actix::api_v2_operation
fn paperclip_attributes(attr: TokenStream) -> TokenStream {
let mut attr = parse_macro_input!(attr as syn::AttributeArgs);
if attr.len() < 2 {
if attr.len() < 3 {
TokenStream::new()
} else {
// remove the URI path
// remove the base URI path
attr.remove(0);
// remove the relative URI path
attr.remove(0);
let mut paperclip_attr = "".to_string();
for i in attr {
Expand All @@ -30,8 +32,25 @@ impl Method {
paperclip_attr.parse().unwrap()
}
}
/// URI with the full path used to register the handler
fn handler_uri(attr: TokenStream) -> TokenStream {
let attr = parse_macro_input!(attr as syn::AttributeArgs);
let mut attr = parse_macro_input!(attr as syn::AttributeArgs);
let base = attr.first().to_token_stream().to_string();
attr.remove(0);
let uri = attr.first().to_token_stream().to_string();
let base_unquoted = base.trim_matches('"');
let uri_unquoted = uri.trim_matches('"');
let handler_uri = format!("{}{}", base_unquoted, uri_unquoted);
let handler_uri_token = quote! {
#handler_uri
};
handler_uri_token.into()
}
/// relative URI (full URI minus the openapi base path)
fn openapi_uri(attr: TokenStream) -> TokenStream {
let mut attr = parse_macro_input!(attr as syn::AttributeArgs);
// remove the Base Path
attr.remove(0);
attr.first().into_token_stream().into()
}
fn handler_name(item: TokenStream) -> syn::Result<syn::Ident> {
Expand All @@ -43,7 +62,8 @@ impl Method {
attr: TokenStream,
item: TokenStream,
) -> syn::Result<TokenStream2> {
let uri: TokenStream2 = Self::handler_uri(attr.clone()).into();
let full_uri: TokenStream2 = Self::handler_uri(attr.clone()).into();
let relative_uri: TokenStream2 = Self::openapi_uri(attr.clone()).into();
let handler_name = Self::handler_name(item.clone())?;
let handler_fn: TokenStream2 = item.into();
let method: TokenStream2 = self.method().parse()?;
Expand All @@ -59,7 +79,7 @@ impl Method {
fn resource() -> paperclip::actix::web::Resource {
#[paperclip::actix::api_v2_operation(#attr)]
#handler_fn
paperclip::actix::web::Resource::new(#uri)
paperclip::actix::web::Resource::new(#full_uri)
.name(#handler_name_str)
.guard(actix_web::guard::#variant())
.route(paperclip::actix::web::#method().to(#handler_name))
Expand All @@ -72,9 +92,10 @@ impl Method {
}
}


impl paperclip::actix::Mountable for #handler_name {
fn path(&self) -> &str {
#uri
#relative_uri
}

fn operations(
Expand Down Expand Up @@ -143,15 +164,17 @@ trait actix_web::Responder.
```
# Attributes
- `"path"` - Raw literal string with path for which to register handler.
- any paperclip api_v2_operation attributes
- `"base"` - Raw literal string with the handler base path used by the openapi `paths`.
- `"path"` - Raw literal string representing the uri path for which to register the handler
when combined with the base path.
- any paperclip api_v2_operation attributes.
# Example
```rust
# use actix_web::Json;
# use mayastor_macros::"#, stringify!($method), ";
#[", stringify!($method), r#"("/")]
#[", stringify!($method), r#"("", "/")]
async fn example() -> Json<()> {
Json(())
}
Expand Down
16 changes: 9 additions & 7 deletions mbus-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,10 @@ impl FromStr for Channel {
type Err = strum::ParseError;

fn from_str(source: &str) -> Result<Self, Self::Err> {
match &source[0 ..= 2] {
"v0/" => {
let c: v0::ChannelVs = source[3 ..].parse()?;
match source.split('/').next() {
Some(v0::VERSION) => {
let c: v0::ChannelVs =
source[v0::VERSION.len() + 1 ..].parse()?;
Ok(Self::v0(c))
}
_ => Err(strum::ParseError::VariantNotFound),
Expand Down Expand Up @@ -208,9 +209,10 @@ impl FromStr for MessageId {
type Err = strum::ParseError;

fn from_str(source: &str) -> Result<Self, Self::Err> {
match &source[0 ..= 2] {
"v0/" => {
let id: v0::MessageIdVs = source[3 ..].parse()?;
match source.split('/').next() {
Some(v0::VERSION) => {
let id: v0::MessageIdVs =
source[v0::VERSION.len() + 1 ..].parse()?;
Ok(Self::v0(id))
}
_ => Err(strum::ParseError::VariantNotFound),
Expand All @@ -220,7 +222,7 @@ impl FromStr for MessageId {
impl ToString for MessageId {
fn to_string(&self) -> String {
match self {
Self::v0(id) => format!("v0/{}", id.to_string()),
Self::v0(id) => format!("{}/{}", v0::VERSION, id.to_string()),
}
}
}
Expand Down
79 changes: 1 addition & 78 deletions mbus-api/src/message_bus/v0.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// clippy warning caused by the instrument macro
#![allow(clippy::unit_arg)]

use crate::{v0::*, *};
pub use crate::{v0::*, *};
use async_trait::async_trait;

/// Error sending/receiving
Expand All @@ -28,83 +28,6 @@ impl From<Error> for BusError {
/// Result for sending/receiving
pub type BusResult<T> = Result<T, BusError>;

/// Node
pub type Node = crate::v0::Node;
/// Node list
pub type Nodes = crate::v0::Nodes;
/// Pool
pub type Pool = crate::v0::Pool;
/// Pool list
pub type Pools = crate::v0::Pools;
/// Replica
pub type Replica = crate::v0::Replica;
/// Replica list
pub type Replicas = crate::v0::Replicas;
/// Protocol
pub type Protocol = crate::v0::Protocol;
/// Replica Create
pub type CreateReplica = crate::v0::CreateReplica;
/// Pool Create
pub type CreatePool = crate::v0::CreatePool;
/// Replica Destroy
pub type DestroyReplica = crate::v0::DestroyReplica;
/// Pool Destroy
pub type DestroyPool = crate::v0::DestroyPool;
/// Replica Share
pub type ShareReplica = crate::v0::ShareReplica;
/// Replica Unshare
pub type UnshareReplica = crate::v0::UnshareReplica;
/// Query Filter
pub type Filter = crate::v0::Filter;
/// Nexus from the volume service
pub type Nexus = crate::v0::Nexus;
/// Vector of Nexuses from the volume service
pub type Nexuses = crate::v0::Nexuses;
/// State of the nexus
pub type NexusState = crate::v0::NexusState;
/// State of the volume
pub type VolumeState = crate::v0::VolumeState;
/// Child of the nexus
pub type Child = crate::v0::Child;
/// State of the child
pub type ChildState = crate::v0::ChildState;
/// Nexus Create
pub type CreateNexus = crate::v0::CreateNexus;
/// Nexus Destroy
pub type DestroyNexus = crate::v0::DestroyNexus;
/// Nexus Share
pub type ShareNexus = crate::v0::ShareNexus;
/// Nexus Unshare
pub type UnshareNexus = crate::v0::UnshareNexus;
/// Remove Nexus Child
pub type RemoveNexusChild = crate::v0::RemoveNexusChild;
/// Add Nexus Child
pub type AddNexusChild = crate::v0::AddNexusChild;
/// Volume
pub type Volume = crate::v0::Volume;
/// Volumes
pub type Volumes = crate::v0::Volumes;
/// Create Volume
pub type CreateVolume = crate::v0::CreateVolume;
/// Delete Volume
pub type DestroyVolume = crate::v0::DestroyVolume;
/// Add Volume Nexus
pub type AddVolumeNexus = crate::v0::AddVolumeNexus;
/// Remove Volume Nexus
pub type RemoveVolumeNexus = crate::v0::RemoveVolumeNexus;
/// Id of a mayastor node
pub type NodeId = crate::v0::NodeId;
/// Id of a mayastor pool
pub type PoolId = crate::v0::PoolId;
/// UUID of a mayastor pool replica
pub type ReplicaId = crate::v0::ReplicaId;
/// UUID of a mayastor nexus
pub type NexusId = crate::v0::NexusId;
/// URI of a mayastor nexus child
pub type ChildUri = crate::v0::ChildUri;
/// UUID of a mayastor volume
pub type VolumeId = crate::v0::VolumeId;

macro_rules! only_one {
($list:ident) => {
if let Some(obj) = $list.first() {
Expand Down
2 changes: 2 additions & 0 deletions mbus-api/src/v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use serde::{Deserialize, Serialize};
use std::{cmp::Ordering, fmt::Debug};
use strum_macros::{EnumString, ToString};

pub(super) const VERSION: &str = "v0";

/// Versioned Channels
#[derive(Clone, Debug, EnumString, ToString)]
#[strum(serialize_all = "camelCase")]
Expand Down
2 changes: 1 addition & 1 deletion nix/pkgs/mayastor/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ let
buildProps = rec {
name = "mayastor";
#cargoSha256 = "0000000000000000000000000000000000000000000000000000";
cargoSha256 = "0jxi2z78kc0knr3bscyk622rg7b5ynjiw205xl6g4v8saychxpbd";
cargoSha256 = "1kv66ngq371ss2ra8hpr7zxdwvqwnfg0kskl96fqmc2kbbpah2r8";
inherit version;
src = whitelistSource ../../../. [
"Cargo.lock"
Expand Down
1 change: 1 addition & 0 deletions rest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ path = "./src/lib.rs"
[dependencies]
rustls = "0.18"
actix-web = { version = "3.2.0", features = ["rustls"] }
actix-service = "1.0.6"
mbus_api = { path = "../mbus-api" }
async-trait = "0.1.41"
serde_json = "1.0"
Expand Down
51 changes: 36 additions & 15 deletions rest/service/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
mod v0;

use actix_web::{middleware, App, HttpServer};
use paperclip::actix::OpenApiExt;
use actix_service::ServiceFactory;
use actix_web::{
dev::{MessageBody, ServiceRequest, ServiceResponse},
middleware,
App,
HttpServer,
};
use rustls::{
internal::pemfile::{certs, rsa_private_keys},
NoClientAuth,
Expand All @@ -10,9 +15,6 @@ use rustls::{
use std::io::BufReader;
use structopt::StructOpt;

/// uri where the openapi spec is exposed
pub(crate) const SPEC_URI: &str = "/v0/api/spec";

#[derive(Debug, StructOpt)]
pub(crate) struct CliArgs {
/// The bind address for the REST interface (with HTTPS)
Expand Down Expand Up @@ -60,6 +62,34 @@ fn init_tracing() -> Option<(Tracer, Uninstall)> {
}
}

/// Extension trait for actix-web applications.
pub trait OpenApiExt<T, B> {
/// configures the App with this version's handlers and openapi generation
fn configure_api(
self,
config: &dyn Fn(actix_web::App<T, B>) -> actix_web::App<T, B>,
) -> actix_web::App<T, B>;
}

impl<T, B> OpenApiExt<T, B> for actix_web::App<T, B>
where
B: MessageBody,
T: ServiceFactory<
Config = (),
Request = ServiceRequest,
Response = ServiceResponse<B>,
Error = actix_web::Error,
InitError = (),
>,
{
fn configure_api(
self,
config: &dyn Fn(actix_web::App<T, B>) -> actix_web::App<T, B>,
) -> actix_web::App<T, B> {
config(self)
}
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
// need to keep the jaeger pipeline tracer alive, if enabled
Expand All @@ -83,16 +113,7 @@ async fn main() -> std::io::Result<()> {
App::new()
.wrap(RequestTracing::new())
.wrap(middleware::Logger::default())
.wrap_api()
.configure(v0::nodes::configure)
.configure(v0::pools::configure)
.configure(v0::replicas::configure)
.configure(v0::nexuses::configure)
.configure(v0::children::configure)
.configure(v0::volumes::configure)
.with_json_spec_at(SPEC_URI)
.build()
.configure(v0::swagger_ui::configure)
.configure_api(&v0::configure_api)
})
.bind_rustls(CliArgs::from_args().https, config)?;
if let Some(http) = CliArgs::from_args().http {
Expand Down
Loading

0 comments on commit 2302520

Please sign in to comment.