odoo-api 0.2.5

Type-safe and full-coverage implementation of the Odoo JSON-RPC API, including ORM and Web methods. Supports sessioning, multi-database, async and blocking via reqwest, and bring-your-own requests.
Documentation
//! The Odoo "Web" pseudo-service
//!
//! This isn't actually a service, but a set of JSON-RPC compatible endpoints
//! that Odoo exposes. Generally these are used by the webclient, and offer
//! functionality that can be achieved with `execute` and `execute_kw`

use crate::jsonrpc::OdooWebMethod;
use crate::service::common::ServerVersionInfo;
use crate::{self as odoo_api, OdooId};
use odoo_api_macros::odoo_web;
use serde::ser::SerializeTuple;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};

//TODO: /web/session/get_lang_list (only v15+?)
//TODO: /web/session/check
//TODO: /web/session/change_password
//TODO: /web/session/get_session_info
//TODO: /web/session/modules
//TODO: /web/session/modules
//TODO: /web/session/destroy
//TODO: /web/session/logout
//TODO: /web/dataset/resequence
//TODO: /web/dataset/call
//TODO: /web/dataset/call_kw
//TODO: /web/dataset/load
//TODO: /web/dataset/search_read

/// Authenticate to an Odoo database
///
/// This method performs a bona-fide Odoo "authentication"; it checks the user
/// name/password, and creates a new `session_id` (which is returned via the
/// `Set-Cookie` header).
///
/// Note that by itself, this function isn't able to parse the `session_id` token,
/// so it probably isn't very useful.
///
/// See [`authenticate`](crate::client::OdooClient::authenticate) if you'd like to
/// authenticate an `OdooClient`.
///
/// Reference: [web/controllers/session.py](https://github.com/odoo/odoo/blob/b6e195ccb3a6c37b0d980af159e546bdc67b1e42/addons/web/controllers/session.py#L29-L43)
#[odoo_web(
    path = "/web/session/authenticate",
    name = "web_session_authenticate",
    auth = false
)]
#[derive(Debug, Serialize)]
pub struct SessionAuthenticate {
    pub(crate) db: String,
    pub(crate) login: String,
    pub(crate) password: String,
}

/// Represents the response to an Odoo [`SessionAuthenticate`] call
///
/// Note that the generated `session_id` is not returned here. The response
/// data contains some information about the Odoo session.
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SessionAuthenticateResponse {
    pub data: Value,
}

/// List the available databases
///
/// Reference: [web/controller/database.py](https://github.com/odoo/odoo/blob/b6e195ccb3a6c37b0d980af159e546bdc67b1e42/addons/web/controllers/database.py#L176-L183)
#[odoo_web(path = "/web/database/list", name = "web_database_list", auth = false)]
#[derive(Debug)]
pub struct DatabaseList {}

// DatabaseList has no fields, but needs to output in JSON: `[]`
impl Serialize for DatabaseList {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let state = serializer.serialize_tuple(0)?;
        state.end()
    }
}

/// Represents the response to an Odoo [`DatabaseList`] call
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct DatabaseListResponse {
    pub databases: Vec<String>,
}

/// Fetch session information
///
/// Reference: [web/controllers/session.py](https://github.com/odoo/odoo/blob/b6e195ccb3a6c37b0d980af159e546bdc67b1e42/addons/web/controllers/session.py#L23-L27)
#[odoo_web(
    path = "/web/session/get_session_info",
    name = "web_get_session_info",
    auth = false
)]
#[derive(Debug)]
pub struct GetSessionInfo {}

// GetSessionInfo has no fields, but needs to output in JSON: `[]`
impl Serialize for GetSessionInfo {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let state = serializer.serialize_tuple(0)?;
        state.end()
    }
}

/// The response to a `GetSessionInfo` request
///
/// Reference: [web/controllers/session.py](https://github.com/odoo/odoo/blob/b6e195ccb3a6c37b0d980af159e546bdc67b1e42/addons/web/controllers/session.py#L27)
/// See also: [web/models/ir_http.py](https://github.com/odoo/odoo/blob/b6e195ccb3a6c37b0d980af159e546bdc67b1e42/addons/web/models/ir_http.py#L67-L146)
#[derive(Debug, Serialize, Deserialize)]
pub struct GetSessionInfoResponse {
    /// The authenticated users' ID
    uid: OdooId,

    /// Is the "system" user logged-in?
    ///
    /// This is equivalent to `res.users._is_system()`
    is_system: bool,

    /// Is the "admin" user logged-in?
    ///
    /// This is equivalent to `res.users._is_admin()`
    is_admin: bool,

    //TODO: more docs!
    /// The "user context"
    user_context: Map<String, Value>,

    /// The database that is currently bound
    db: String,

    /// Server version information (basic)
    ///
    /// Example: `14.0+e`
    server_version: String,

    /// Server version information (extended)
    ///
    /// See the `ServerVersionInfo` struct for more information
    server_version_info: ServerVersionInfo,

    /// The logged-in users' name
    ///
    /// Note that this corresponds with the `res.users.name` field, which
    /// is related to `res.users.partner_id.name` (e.g., it is the "pretty"
    /// name for the user, **not** the username/login)
    name: String,

    /// The logged-in users' username/login
    username: String,

    /// The logged-in users' display name
    ///
    /// Note that this corresponds with the related `res.users.partner_id.display_name`
    /// field.
    partner_display_name: String,

    /// The logged-in users' primary company
    ///
    /// **Note**: This is NOT the primary active company - it literally corresponds
    /// with the `res.users.company_id` field (or "Primary Company" in the user
    /// config). This field probably isn't very useful.
    company_id: OdooId,

    /// The logged-in users' related partner
    ///
    /// Every Odoo user has a "related partner"; the mapping is one2one - each
    /// user has exactly 1 partner, and each partner has at-most 1 user.
    ///
    /// Some "pretty" information is stored on the partner rather than the user:
    ///  - Display Name
    ///  - Timezone
    ///  - Email
    ///  - Address info
    partner_id: OdooId,

    /// The `web.base.url` config parameter
    #[serde(rename = "web.base.url")]
    web_base_url: String,

    //TODO: store this as a u32, convert on deserialize
    /// The `web.active_ids_limit` config parameter
    active_ids_limit: String,

    //TODO: store this as a u32/i32?
    /// The `web.max_file_upload_size` config parameter
    ///
    /// If the paramter is not defined, then 128MiB is used as the default
    max_file_upload_size: String,

    /// Multi-company information
    ///
    /// Only present when authenticated as an internal user (e.g., a user with
    /// the `base.group_user` group).
    user_companies: Option<MultiCompanyInfo>,

    /// TBC
    ///
    /// Only present when authenticated as an internal user (e.g., a user with
    /// the `base.group_user` group).
    show_effect: Option<bool>,

    /// Should the switch-company menu be shown?
    ///
    /// E.g., `len(allowed_company_ids) > 1`
    ///
    /// Only present when authenticated as an internal user (e.g., a user with
    /// the `base.group_user` group).
    display_switch_company_menu: Option<bool>,

    ///
    /// Only present when authenticated as an internal user (e.g., a user with
    /// the `base.group_user` group).
    cache_hashes: Option<Value>,
}

/// Multi-company information
#[derive(Debug, Serialize, Deserialize)]
pub struct MultiCompanyInfo {
    current_company: MultiCompanyInfoCurrent,
    allowed_companies: Vec<MultiCompanyInfoAllowed>,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum MultiCompanyInfoCurrent {
    NameAndId((OdooId, String)),
    Id(OdooId),
}

#[derive(Debug, Serialize, Deserialize)]
pub struct MultiCompanyInfoAllowed {
    id: OdooId,
    name: String,
    sequence: Option<u32>,
}