From 361b7f5b693fb29fba36a82280c277dccbe65a9b Mon Sep 17 00:00:00 2001 From: "Franz Heinzmann (Frando)" Date: Fri, 15 Jul 2022 14:26:55 +0200 Subject: [PATCH] Add API versioning to the JSON-RPC API --- deltachat-ffi/src/lib.rs | 18 +++++++++++++++--- deltachat-jsonrpc/src/api/mod.rs | 10 +++++----- deltachat-jsonrpc/src/api/types/account.rs | 4 +--- deltachat-jsonrpc/src/lib.rs | 3 ++- deltachat-jsonrpc/src/webserver.rs | 6 +++--- node/lib/deltachat.ts | 4 ++-- node/src/module.c | 7 ++++--- 7 files changed, 32 insertions(+), 20 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 3948ecd61..67bf6f068 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -4453,17 +4453,29 @@ mod jsonrpc { #[no_mangle] pub unsafe extern "C" fn dc_jsonrpc_init( account_manager: *mut dc_accounts_t, + api_version: *const libc::c_char, ) -> *mut dc_jsonrpc_instance_t { if account_manager.is_null() { eprintln!("ignoring careless call to dc_jsonrpc_init()"); return ptr::null_mut(); } + let api_version = to_string_lossy(api_version); - let cmd_api = - deltachat_jsonrpc::api::CommandApi::from_arc((*account_manager).inner.clone()); + let rpc_api = match &api_version { + "v0" => { + deltachat_jsonrpc::api::DeltaChatApiV0::from_arc((*account_manager).inner.clone()) + } + _ => { + error!( + ctx, + "The requested JSON-RPC API version is not supported.", err + ); + return ptr::null_mut(); + } + }; let (request_handle, receiver) = RpcClient::new(); - let handle = RpcSession::new(request_handle, cmd_api); + let handle = RpcSession::new(request_handle, rpc_api); let instance = dc_jsonrpc_instance_t { receiver, handle }; diff --git a/deltachat-jsonrpc/src/api/mod.rs b/deltachat-jsonrpc/src/api/mod.rs index a54f19e0d..2a6dfc0f2 100644 --- a/deltachat-jsonrpc/src/api/mod.rs +++ b/deltachat-jsonrpc/src/api/mod.rs @@ -34,20 +34,20 @@ use types::webxdc::WebxdcMessageInfo; use self::types::message::MessageViewtype; #[derive(Clone, Debug)] -pub struct CommandApi { +pub struct DeltaChatApiV0 { pub(crate) accounts: Arc>, } -impl CommandApi { +impl DeltaChatApiV0 { pub fn new(accounts: Accounts) -> Self { - CommandApi { + DeltaChatApiV0 { accounts: Arc::new(RwLock::new(accounts)), } } #[allow(dead_code)] pub fn from_arc(accounts: Arc>) -> Self { - CommandApi { accounts } + DeltaChatApiV0 { accounts } } async fn get_context(&self, id: u32) -> Result { @@ -63,7 +63,7 @@ impl CommandApi { } #[rpc(all_positional, ts_outdir = "typescript/generated")] -impl CommandApi { +impl DeltaChatApiV0 { // --------------------------------------------- // Misc top level functions // --------------------------------------------- diff --git a/deltachat-jsonrpc/src/api/types/account.rs b/deltachat-jsonrpc/src/api/types/account.rs index 9a2d88100..8e0f80aef 100644 --- a/deltachat-jsonrpc/src/api/types/account.rs +++ b/deltachat-jsonrpc/src/api/types/account.rs @@ -19,9 +19,7 @@ pub enum Account { color: String, }, #[serde(rename_all = "camelCase")] - Unconfigured { - id: u32, - }, + Unconfigured { id: u32 }, } impl Account { diff --git a/deltachat-jsonrpc/src/lib.rs b/deltachat-jsonrpc/src/lib.rs index 344e91afb..a60fe536a 100644 --- a/deltachat-jsonrpc/src/lib.rs +++ b/deltachat-jsonrpc/src/lib.rs @@ -1,10 +1,11 @@ pub mod api; pub use api::events; +pub use api::{Accounts, DeltaChatApiV0}; pub use yerpc; #[cfg(test)] mod tests { - use super::api::{Accounts, CommandApi}; + use super::api::{Accounts, DeltaChatApiV0}; use async_channel::unbounded; use futures::StreamExt; use tempfile::TempDir; diff --git a/deltachat-jsonrpc/src/webserver.rs b/deltachat-jsonrpc/src/webserver.rs index 3f6b62702..d1c0ff317 100644 --- a/deltachat-jsonrpc/src/webserver.rs +++ b/deltachat-jsonrpc/src/webserver.rs @@ -6,7 +6,7 @@ use yerpc::{RpcClient, RpcSession}; mod api; use api::events::event_to_json_rpc_notification; -use api::{Accounts, CommandApi}; +use api::{Accounts, DeltaChatApiV0}; const DEFAULT_PORT: u16 = 20808; @@ -20,10 +20,10 @@ async fn main() -> Result<(), std::io::Error> { .unwrap_or(DEFAULT_PORT); log::info!("Starting with accounts directory `{path}`."); let accounts = Accounts::new(PathBuf::from(&path)).await.unwrap(); - let state = CommandApi::new(accounts); + let state = DeltaChatApiV0::new(accounts); let app = Router::new() - .route("/ws", get(handler)) + .route("/rpc/v0", get(handler)) .layer(Extension(state.clone())); tokio::spawn(async move { diff --git a/node/lib/deltachat.ts b/node/lib/deltachat.ts index 7a3cf79db..959a063d9 100644 --- a/node/lib/deltachat.ts +++ b/node/lib/deltachat.ts @@ -115,7 +115,7 @@ export class AccountManager extends EventEmitter { debug('Started event handler') } - startJsonRpcHandler(callback: ((response: string) => void) | null) { + startJsonRpcHandler(callback: ((response: string) => void) | null, apiVersion: string = "v0") { if (this.dcn_accounts === null) { throw new Error('dcn_account is null') } @@ -126,7 +126,7 @@ export class AccountManager extends EventEmitter { throw new Error('jsonrpc was started already') } - binding.dcn_accounts_start_jsonrpc(this.dcn_accounts, callback.bind(this)) + binding.dcn_accounts_start_jsonrpc(this.dcn_accounts, apiVersion, callback.bind(this)) debug('Started JSON-RPC handler') this.jsonRpcStarted = true } diff --git a/node/src/module.c b/node/src/module.c index 4fc185c1c..c856ca69d 100644 --- a/node/src/module.c +++ b/node/src/module.c @@ -3313,9 +3313,10 @@ static void call_accounts_js_jsonrpc_handler(napi_env env, napi_value js_callbac } NAPI_METHOD(dcn_accounts_start_jsonrpc) { - NAPI_ARGV(2); + NAPI_ARGV(3); NAPI_DCN_ACCOUNTS(); - napi_value callback = argv[1]; + NAPI_ARGV_UTF8_MALLOC(api_version, 1); + napi_value callback = argv[2]; TRACE("calling.."); napi_value async_resource_name; @@ -3338,7 +3339,7 @@ NAPI_METHOD(dcn_accounts_start_jsonrpc) { TRACE("done"); dcn_accounts->gc = 0; - dcn_accounts->jsonrpc_instance = dc_jsonrpc_init(dcn_accounts->dc_accounts); + dcn_accounts->jsonrpc_instance = dc_jsonrpc_init(dcn_accounts->dc_accounts, api_version); TRACE("creating uv thread.."); uv_thread_create(&dcn_accounts->jsonrpc_thread, accounts_jsonrpc_thread_func, dcn_accounts);