use crate as odoo_api;
use crate::jsonrpc::OdooApiMethod;
use odoo_api_macros::odoo_api;
use serde::de::Visitor;
use serde::ser::SerializeTuple;
use serde::{Deserialize, Serialize};
use serde_tuple::Serialize_tuple;
#[odoo_api(
service = "db",
method = "create_database",
name = "db_create_database",
auth = false
)]
#[derive(Debug, Serialize_tuple)]
pub struct CreateDatabase {
pub passwd: String,
pub db_name: String,
pub demo: bool,
pub lang: String,
pub user_password: String,
pub login: String,
pub country_code: Option<String>,
pub phone: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct CreateDatabaseResponse {
pub ok: bool,
}
#[odoo_api(
service = "db",
method = "duplicate_database",
name = "db_duplicate_database",
auth = false
)]
#[derive(Debug, Serialize_tuple)]
pub struct DuplicateDatabase {
pub passwd: String,
pub db_original_name: String,
pub db_name: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct DuplicateDatabaseResponse {
pub ok: bool,
}
#[odoo_api(service = "db", method = "drop", name = "db_drop", auth = false)]
#[derive(Debug, Serialize_tuple)]
pub struct Drop {
pub passwd: String,
pub db_name: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct DropResponse {
pub ok: bool,
}
#[odoo_api(service = "db", method = "dump", name = "db_dump", auth = false)]
#[derive(Debug, Serialize_tuple)]
pub struct Dump {
pub passwd: String,
pub db_name: String,
pub format: crate::service::db::DumpFormat,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum DumpFormat {
#[serde(rename = "zip")]
Zip,
#[serde(rename = "dump")]
Dump,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct DumpResponse {
pub b64_bytes: String,
}
#[odoo_api(service = "db", method = "restore", name = "db_restore", auth = false)]
#[derive(Debug, Serialize_tuple)]
pub struct Restore {
pub passwd: String,
pub b64_data: String,
pub restore_type: RestoreType,
}
#[derive(Debug)]
pub enum RestoreType {
Copy,
Move,
}
impl Serialize for RestoreType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_bool(match self {
Self::Copy => true,
Self::Move => false,
})
}
}
struct RestoreTypeVisitor;
impl<'de> Visitor<'de> for RestoreTypeVisitor {
type Value = bool;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a boolean (`true` or `false`)")
}
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v)
}
}
impl<'de> Deserialize<'de> for RestoreType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let b = deserializer.deserialize_bool(RestoreTypeVisitor)?;
Ok(match b {
true => Self::Copy,
false => Self::Move,
})
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct RestoreResponse {
pub ok: bool,
}
#[odoo_api(service = "db", method = "rename", name = "db_rename", auth = false)]
#[derive(Debug, Serialize_tuple)]
pub struct Rename {
pub passwd: String,
pub old_name: String,
pub new_name: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct RenameResponse {
pub ok: bool,
}
#[odoo_api(
service = "db",
method = "change_admin_password",
name = "db_change_admin_password",
auth = false
)]
#[derive(Debug, Serialize_tuple)]
pub struct ChangeAdminPassword {
pub passwd: String,
pub new_passwd: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ChangeAdminPasswordResponse {
pub ok: bool,
}
#[odoo_api(
service = "db",
method = "migrate_databases",
name = "db_migrate_databases",
auth = false
)]
#[derive(Debug, Serialize_tuple)]
pub struct MigrateDatabases {
pub passwd: String,
pub databases: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct MigrateDatabasesResponse {
pub ok: bool,
}
#[odoo_api(service = "db", method = "db_exist", auth = false)]
#[derive(Debug, Serialize_tuple)]
pub struct DbExist {
pub db_name: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct DbExistResponse {
pub exists: bool,
}
#[odoo_api(service = "db", method = "list", name = "db_list", auth = false)]
#[derive(Debug, Serialize_tuple)]
pub struct List {
pub document: bool,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ListResponse {
pub databases: Vec<String>,
}
#[odoo_api(
service = "db",
method = "list_lang",
name = "db_list_lang",
auth = false
)]
#[derive(Debug)]
pub struct ListLang {}
impl Serialize for ListLang {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let state = serializer.serialize_tuple(0)?;
state.end()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ListLangResponse {
pub languages: Vec<ListLangResponseItem>,
}
#[derive(Debug, Serialize_tuple, Deserialize)]
pub struct ListLangResponseItem {
pub code: String,
pub name: String,
}
#[odoo_api(
service = "db",
method = "list_countries",
name = "db_list_countries",
auth = false
)]
#[derive(Debug, Serialize_tuple)]
pub struct ListCountries {
pub passwd: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ListCountriesResponse {
pub countries: Vec<ListLangResponseItem>,
}
#[derive(Debug, Serialize_tuple, Deserialize)]
pub struct ListCountriesResponseItem {
pub code: String,
pub name: String,
}
#[odoo_api(
service = "db",
method = "server_version",
name = "db_server_version",
auth = false
)]
#[derive(Debug)]
pub struct ServerVersion {}
impl Serialize for ServerVersion {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let state = serializer.serialize_tuple(0)?;
state.end()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ServerVersionResponse {
pub version: String,
}
#[cfg(test)]
mod test {
use super::*;
use crate::client::error::Result;
use crate::jsonrpc::{JsonRpcParams, JsonRpcResponse};
use serde_json::{from_value, json, to_value};
#[test]
fn create_database() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "create_database",
"args": [
"master-password",
"new-database",
false,
"en_US",
"password",
"admin",
null,
"123 123 123"
]
}
});
let actual = to_value(
CreateDatabase {
passwd: "master-password".into(),
db_name: "new-database".into(),
demo: false,
lang: "en_US".into(),
user_password: "password".into(),
login: "admin".into(),
country_code: None,
phone: Some("123 123 123".into()),
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn create_database_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": true
});
let response: JsonRpcResponse<CreateDatabaseResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn duplicate_database() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "duplicate_database",
"args": [
"master-password",
"old-database",
"new-database",
]
}
});
let actual = to_value(
DuplicateDatabase {
passwd: "master-password".into(),
db_original_name: "old-database".into(),
db_name: "new-database".into(),
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn duplicate_database_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": true
});
let response: JsonRpcResponse<DuplicateDatabaseResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn drop() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "drop",
"args": [
"master-password",
"old-database",
]
}
});
let actual = to_value(
Drop {
passwd: "master-password".into(),
db_name: "old-database".into(),
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn drop_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": true
});
let response: JsonRpcResponse<DropResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn dump_zip() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "dump",
"args": [
"master-password",
"old-database",
"zip",
]
}
});
let actual = to_value(
Dump {
passwd: "master-password".into(),
db_name: "old-database".into(),
format: DumpFormat::Zip,
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn dump_dump() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "dump",
"args": [
"master-password",
"old-database",
"dump",
]
}
});
let actual = to_value(
Dump {
passwd: "master-password".into(),
db_name: "old-database".into(),
format: DumpFormat::Dump,
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn dump_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": "base64-data-will-be-here"
});
let response: JsonRpcResponse<DumpResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn restore_move() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "restore",
"args": [
"master-password",
"base64-data-would-be-here",
false,
]
}
});
let actual = to_value(
Restore {
passwd: "master-password".into(),
b64_data: "base64-data-would-be-here".into(),
restore_type: RestoreType::Move,
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn restore_copy() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "restore",
"args": [
"master-password",
"base64-data-would-be-here",
true,
]
}
});
let actual = to_value(
Restore {
passwd: "master-password".into(),
b64_data: "base64-data-would-be-here".into(),
restore_type: RestoreType::Copy,
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn restore_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": true
});
let response: JsonRpcResponse<RestoreResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn rename() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "rename",
"args": [
"master-password",
"old-database",
"new-database"
]
}
});
let actual = to_value(
Rename {
passwd: "master-password".into(),
old_name: "old-database".into(),
new_name: "new-database".into(),
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn rename_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": true
});
let response: JsonRpcResponse<RenameResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn change_admin_password() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "change_admin_password",
"args": [
"master-password",
"new-master-password",
]
}
});
let actual = to_value(
ChangeAdminPassword {
passwd: "master-password".into(),
new_passwd: "new-master-password".into(),
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn change_admin_password_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": true
});
let response: JsonRpcResponse<ChangeAdminPasswordResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn migrate_databases() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "migrate_databases",
"args": [
"master-password",
[
"new-database",
"new-database2",
]
]
}
});
let actual = to_value(
MigrateDatabases {
passwd: "master-password".into(),
databases: vec!["new-database".into(), "new-database2".into()],
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn migrate_databases_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": true
});
let response: JsonRpcResponse<MigrateDatabasesResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn db_exist() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "db_exist",
"args": [
"new-database"
]
}
});
let actual = to_value(
DbExist {
db_name: "new-database".into(),
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn db_exist_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": true
});
let response: JsonRpcResponse<DbExistResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn list() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "list",
"args": [
false
]
}
});
let actual = to_value(List { document: false }.build(1000))?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn list_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": [
"old-database",
"new-database",
"new-database2"
]
});
let response: JsonRpcResponse<ListResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn list_lang() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "list_lang",
"args": []
}
});
let actual = to_value(ListLang {}.build(1000))?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn list_lang_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": [
[
"sq_AL",
"Albanian / Shqip"
],
[
"am_ET",
"Amharic / አምሃርኛ"
],
[
"ar_SY",
"Arabic (Syria) / الْعَرَبيّة"
],
]
});
let response: JsonRpcResponse<ListLangResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn list_countries() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "list_countries",
"args": [
"master-password"
]
}
});
let actual = to_value(
ListCountries {
passwd: "master-password".into(),
}
.build(1000),
)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn list_countries_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": [
[
"af",
"Afghanistan"
],
[
"al",
"Albania"
],
[
"dz",
"Algeria"
],
]
});
let response: JsonRpcResponse<ListCountriesResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
#[test]
fn server_version() -> Result<()> {
let expected = json!({
"jsonrpc": "2.0",
"method": "call",
"id": 1000,
"params": {
"service": "db",
"method": "server_version",
"args": []
}
});
let actual = to_value(ServerVersion {}.build(1000))?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn server_version_response() -> Result<()> {
let payload = json!({
"jsonrpc": "2.0",
"id": 1000,
"result": "14.0+e"
});
let response: JsonRpcResponse<ServerVersionResponse> = from_value(payload)?;
match response {
JsonRpcResponse::Error(e) => Err(e.error.into()),
JsonRpcResponse::Success(_) => Ok(()),
}
}
}