Add API versioning to the JSON-RPC API

This commit is contained in:
Franz Heinzmann (Frando)
2022-07-15 14:26:55 +02:00
parent 15019ce02b
commit 361b7f5b69
7 changed files with 32 additions and 20 deletions

View File

@@ -4453,17 +4453,29 @@ mod jsonrpc {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn dc_jsonrpc_init( pub unsafe extern "C" fn dc_jsonrpc_init(
account_manager: *mut dc_accounts_t, account_manager: *mut dc_accounts_t,
api_version: *const libc::c_char,
) -> *mut dc_jsonrpc_instance_t { ) -> *mut dc_jsonrpc_instance_t {
if account_manager.is_null() { if account_manager.is_null() {
eprintln!("ignoring careless call to dc_jsonrpc_init()"); eprintln!("ignoring careless call to dc_jsonrpc_init()");
return ptr::null_mut(); return ptr::null_mut();
} }
let api_version = to_string_lossy(api_version);
let cmd_api = let rpc_api = match &api_version {
deltachat_jsonrpc::api::CommandApi::from_arc((*account_manager).inner.clone()); "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 (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 }; let instance = dc_jsonrpc_instance_t { receiver, handle };

View File

@@ -34,20 +34,20 @@ use types::webxdc::WebxdcMessageInfo;
use self::types::message::MessageViewtype; use self::types::message::MessageViewtype;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CommandApi { pub struct DeltaChatApiV0 {
pub(crate) accounts: Arc<RwLock<Accounts>>, pub(crate) accounts: Arc<RwLock<Accounts>>,
} }
impl CommandApi { impl DeltaChatApiV0 {
pub fn new(accounts: Accounts) -> Self { pub fn new(accounts: Accounts) -> Self {
CommandApi { DeltaChatApiV0 {
accounts: Arc::new(RwLock::new(accounts)), accounts: Arc::new(RwLock::new(accounts)),
} }
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn from_arc(accounts: Arc<RwLock<Accounts>>) -> Self { pub fn from_arc(accounts: Arc<RwLock<Accounts>>) -> Self {
CommandApi { accounts } DeltaChatApiV0 { accounts }
} }
async fn get_context(&self, id: u32) -> Result<deltachat::context::Context> { async fn get_context(&self, id: u32) -> Result<deltachat::context::Context> {
@@ -63,7 +63,7 @@ impl CommandApi {
} }
#[rpc(all_positional, ts_outdir = "typescript/generated")] #[rpc(all_positional, ts_outdir = "typescript/generated")]
impl CommandApi { impl DeltaChatApiV0 {
// --------------------------------------------- // ---------------------------------------------
// Misc top level functions // Misc top level functions
// --------------------------------------------- // ---------------------------------------------

View File

@@ -19,9 +19,7 @@ pub enum Account {
color: String, color: String,
}, },
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
Unconfigured { Unconfigured { id: u32 },
id: u32,
},
} }
impl Account { impl Account {

View File

@@ -1,10 +1,11 @@
pub mod api; pub mod api;
pub use api::events; pub use api::events;
pub use api::{Accounts, DeltaChatApiV0};
pub use yerpc; pub use yerpc;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::api::{Accounts, CommandApi}; use super::api::{Accounts, DeltaChatApiV0};
use async_channel::unbounded; use async_channel::unbounded;
use futures::StreamExt; use futures::StreamExt;
use tempfile::TempDir; use tempfile::TempDir;

View File

@@ -6,7 +6,7 @@ use yerpc::{RpcClient, RpcSession};
mod api; mod api;
use api::events::event_to_json_rpc_notification; use api::events::event_to_json_rpc_notification;
use api::{Accounts, CommandApi}; use api::{Accounts, DeltaChatApiV0};
const DEFAULT_PORT: u16 = 20808; const DEFAULT_PORT: u16 = 20808;
@@ -20,10 +20,10 @@ async fn main() -> Result<(), std::io::Error> {
.unwrap_or(DEFAULT_PORT); .unwrap_or(DEFAULT_PORT);
log::info!("Starting with accounts directory `{path}`."); log::info!("Starting with accounts directory `{path}`.");
let accounts = Accounts::new(PathBuf::from(&path)).await.unwrap(); let accounts = Accounts::new(PathBuf::from(&path)).await.unwrap();
let state = CommandApi::new(accounts); let state = DeltaChatApiV0::new(accounts);
let app = Router::new() let app = Router::new()
.route("/ws", get(handler)) .route("/rpc/v0", get(handler))
.layer(Extension(state.clone())); .layer(Extension(state.clone()));
tokio::spawn(async move { tokio::spawn(async move {

View File

@@ -115,7 +115,7 @@ export class AccountManager extends EventEmitter {
debug('Started event handler') debug('Started event handler')
} }
startJsonRpcHandler(callback: ((response: string) => void) | null) { startJsonRpcHandler(callback: ((response: string) => void) | null, apiVersion: string = "v0") {
if (this.dcn_accounts === null) { if (this.dcn_accounts === null) {
throw new Error('dcn_account is null') throw new Error('dcn_account is null')
} }
@@ -126,7 +126,7 @@ export class AccountManager extends EventEmitter {
throw new Error('jsonrpc was started already') 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') debug('Started JSON-RPC handler')
this.jsonRpcStarted = true this.jsonRpcStarted = true
} }

View File

@@ -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_METHOD(dcn_accounts_start_jsonrpc) {
NAPI_ARGV(2); NAPI_ARGV(3);
NAPI_DCN_ACCOUNTS(); NAPI_DCN_ACCOUNTS();
napi_value callback = argv[1]; NAPI_ARGV_UTF8_MALLOC(api_version, 1);
napi_value callback = argv[2];
TRACE("calling.."); TRACE("calling..");
napi_value async_resource_name; napi_value async_resource_name;
@@ -3338,7 +3339,7 @@ NAPI_METHOD(dcn_accounts_start_jsonrpc) {
TRACE("done"); TRACE("done");
dcn_accounts->gc = 0; 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.."); TRACE("creating uv thread..");
uv_thread_create(&dcn_accounts->jsonrpc_thread, accounts_jsonrpc_thread_func, dcn_accounts); uv_thread_create(&dcn_accounts->jsonrpc_thread, accounts_jsonrpc_thread_func, dcn_accounts);