From e0f48cf1048935ff8ea2207516cea5c2147646b0 Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Wed, 6 Jul 2022 16:43:37 +0100 Subject: [PATCH] [bugfix] - Fix openrpc doc generation subscription method handling (#2862) * fix open rpc doc generation subscription handling * address PR comment, remove clone --- crates/sui-open-rpc-macros/src/lib.rs | 54 +++++++++++++++++++++------ crates/sui-open-rpc/spec/openrpc.json | 47 ++++++++++++++--------- crates/sui-open-rpc/src/lib.rs | 34 ++++++++++++----- 3 files changed, 98 insertions(+), 37 deletions(-) diff --git a/crates/sui-open-rpc-macros/src/lib.rs b/crates/sui-open-rpc-macros/src/lib.rs index 34f5f0b1495a1..d93288bbea0a2 100644 --- a/crates/sui-open-rpc-macros/src/lib.rs +++ b/crates/sui-open-rpc-macros/src/lib.rs @@ -57,11 +57,12 @@ pub fn open_rpc(attr: TokenStream, item: TokenStream) -> TokenStream { } else { quote! {None;} }; + let is_pubsub = method.is_pubsub; methods.push(quote! { let mut inputs: Vec = Vec::new(); #(#inputs)* let result = #returns_ty - builder.add_method(#namespace, #name, inputs, result, #doc, #tag); + builder.add_method(#namespace, #name, inputs, result, #doc, #tag, #is_pubsub); }) } let open_rpc_name = quote::format_ident!("{}OpenRpc", &rpc_definition.name); @@ -118,18 +119,32 @@ struct Method { params: Vec<(String, Type)>, returns: Option, doc: String, + is_pubsub: bool, } fn parse_rpc_method(trait_data: &mut syn::ItemTrait) -> Result { let mut methods = Vec::new(); for trait_item in &mut trait_data.items { if let TraitItem::Method(method) = trait_item { - let method_name = if let Some(attr) = find_attr(&method.attrs, "method").cloned() { - let token: TokenStream = attr.tokens.clone().into(); - parse::(token)?.value.value() - } else { - "Unknown method name".to_string() - }; + let (method_name, returns, is_pubsub) = + if let Some(attr) = find_attr(&method.attrs, "method") { + let token: TokenStream = attr.tokens.clone().into(); + let returns = match &method.sig.output { + syn::ReturnType::Default => None, + syn::ReturnType::Type(_, output) => extract_type_from(output, "RpcResult"), + }; + ( + parse::(token)?.value.value(), + returns, + false, + ) + } else if let Some(attr) = find_attr(&method.attrs, "subscription") { + let token: TokenStream = attr.tokens.clone().into(); + let attribute = parse::(token)?; + (attribute.value.value(), Some(attribute.item), true) + } else { + panic!("Unknown method name") + }; let doc = extract_doc_comments(&method.attrs).to_string(); @@ -155,15 +170,12 @@ fn parse_rpc_method(trait_data: &mut syn::ItemTrait) -> Result>()?; - let returns = match &method.sig.output { - syn::ReturnType::Default => None, - syn::ReturnType::Type(_, output) => extract_type_from(output, "RpcResult"), - }; methods.push(Method { name: method_name, params, returns, doc, + is_pubsub, }); } } @@ -288,3 +300,23 @@ struct NamedAttribute { #[inside(_paren_token)] value: syn::LitStr, } + +#[derive(Parse, Debug)] +struct SubscriptionNamedAttribute { + #[paren] + _paren_token: Paren, + #[inside(_paren_token)] + _ident: Ident, + #[inside(_paren_token)] + _eq_token: Token![=], + #[inside(_paren_token)] + value: syn::LitStr, + #[inside(_paren_token)] + _comma: Token![,], + #[inside(_paren_token)] + _item_ident: Ident, + #[inside(_paren_token)] + _item_eq_token: Token![=], + #[inside(_paren_token)] + item: Type, +} diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index be2ab2e6a8df8..af5fdc4bb270f 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -15,23 +15,6 @@ "version": "0.1.0" }, "methods": [ - { - "name": "sui_Unknown method name", - "tags": [ - { - "name": "Event Subscription" - } - ], - "params": [ - { - "name": "filter", - "required": true, - "schema": { - "$ref": "#/components/schemas/EventFilter" - } - } - ] - }, { "name": "sui_batchTransaction", "tags": [ @@ -1111,6 +1094,36 @@ } } }, + { + "name": "sui_subscribeEvent", + "tags": [ + { + "name": "Event Subscription" + }, + { + "name": "Websocket" + }, + { + "name": "PubSub" + } + ], + "params": [ + { + "name": "filter", + "required": true, + "schema": { + "$ref": "#/components/schemas/EventFilter" + } + } + ], + "result": { + "name": "SuiEventEnvelope", + "required": true, + "schema": { + "$ref": "#/components/schemas/EventEnvelope" + } + } + }, { "name": "sui_syncAccountState", "tags": [ diff --git a/crates/sui-open-rpc/src/lib.rs b/crates/sui-open-rpc/src/lib.rs index 41970bf90a81b..046d83f6ebf76 100644 --- a/crates/sui-open-rpc/src/lib.rs +++ b/crates/sui-open-rpc/src/lib.rs @@ -109,7 +109,7 @@ struct Method { struct Tag { name: String, #[serde(skip_serializing_if = "Option::is_none")] - summery: Option, + summary: Option, #[serde(skip_serializing_if = "Option::is_none")] description: Option, } @@ -196,6 +196,7 @@ impl RpcModuleDocBuilder { result: Option, doc: &str, tag: Option, + is_pubsub: bool, ) { let description = if doc.trim().is_empty() { None @@ -203,6 +204,28 @@ impl RpcModuleDocBuilder { Some(doc.trim().to_string()) }; let name = format!("{}_{}", namespace, name); + let mut tags = tag + .map(|t| Tag { + name: t, + summary: None, + description: None, + }) + .into_iter() + .collect::>(); + + if is_pubsub { + tags.push(Tag { + name: "Websocket".to_string(), + summary: None, + description: None, + }); + tags.push(Tag { + name: "PubSub".to_string(), + summary: None, + description: None, + }); + } + self.methods.insert( name.clone(), Method { @@ -210,14 +233,7 @@ impl RpcModuleDocBuilder { description, params, result, - tags: tag - .map(|t| Tag { - name: t, - summery: None, - description: None, - }) - .into_iter() - .collect(), + tags, }, ); }