clevercloud_sdk/v2/
addon.rs

1//! # Addon module
2//!
3//! This module expose structures and helpers to interact with the addon api
4//! version 2
5
6use std::{collections::BTreeMap, fmt::Debug};
7
8#[cfg(feature = "logging")]
9use log::{Level, debug, log_enabled};
10use oauth10a::client::{ClientError, RestClient};
11#[cfg(feature = "jsonschemas")]
12use schemars::JsonSchema;
13use serde::{Deserialize, Serialize};
14
15use crate::{Client, v4::addon_provider::config_provider::addon::environment::Variable};
16
17// -----------------------------------------------------------------------------
18// Provider structure
19
20#[cfg_attr(feature = "jsonschemas", derive(JsonSchema))]
21#[derive(Serialize, Deserialize, PartialEq, PartialOrd, Clone, Debug)]
22pub struct Provider {
23    #[serde(rename = "id")]
24    pub id: String,
25    #[serde(rename = "name")]
26    pub name: String,
27    #[serde(rename = "website")]
28    pub website: String,
29    #[serde(rename = "supportEmail")]
30    pub support_email: String,
31    #[serde(rename = "googlePlusName")]
32    pub google_plus_name: String,
33    #[serde(rename = "twitterName")]
34    pub twitter_name: String,
35    #[serde(rename = "analyticsId")]
36    pub analytics_id: String,
37    #[serde(rename = "shortDesc")]
38    pub short_description: String,
39    #[serde(rename = "longDesc")]
40    pub long_description: String,
41    #[serde(rename = "logoUrl")]
42    pub logo_url: String,
43    #[serde(rename = "status")]
44    pub status: String,
45    #[serde(rename = "openInNewTab")]
46    pub open_in_new_tab: bool,
47    #[serde(rename = "canUpgrade")]
48    pub can_upgrade: bool,
49    #[serde(rename = "regions")]
50    pub regions: Vec<String>,
51    #[serde(default, rename = "plans")]
52    pub plans: Vec<Plan>,
53}
54
55// -----------------------------------------------------------------------------
56// Feature structure
57
58#[cfg_attr(feature = "jsonschemas", derive(JsonSchema))]
59#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Clone, Debug)]
60pub struct Feature {
61    #[serde(rename = "name")]
62    pub name: String,
63    #[serde(rename = "type")]
64    pub kind: String,
65    #[serde(rename = "value")]
66    pub value: String,
67    #[serde(rename = "computable_value")]
68    pub computable_value: Option<String>,
69    #[serde(rename = "name_code")]
70    pub name_code: Option<String>,
71}
72
73// -----------------------------------------------------------------------------
74// Plan structure
75
76#[cfg_attr(feature = "jsonschemas", derive(JsonSchema))]
77#[derive(Serialize, Deserialize, PartialEq, PartialOrd, Clone, Debug)]
78pub struct Plan {
79    #[serde(rename = "id")]
80    pub id: String,
81    #[serde(rename = "name")]
82    pub name: String,
83    #[serde(rename = "slug")]
84    pub slug: String,
85    #[serde(rename = "price")]
86    pub price: f32,
87    #[serde(rename = "price_id")]
88    pub price_id: Option<String>,
89    #[serde(rename = "features")]
90    pub features: Vec<Feature>,
91    #[serde(rename = "zones")]
92    pub zones: Vec<String>,
93}
94
95// -----------------------------------------------------------------------------
96// Addon structure
97
98#[cfg_attr(feature = "jsonschemas", derive(JsonSchema))]
99#[derive(Serialize, Deserialize, PartialEq, PartialOrd, Clone, Debug)]
100pub struct Addon {
101    #[serde(rename = "id")]
102    pub id: String,
103    #[serde(rename = "name")]
104    pub name: Option<String>,
105    #[serde(rename = "realId")]
106    pub real_id: String,
107    #[serde(rename = "region")]
108    pub region: String,
109    #[serde(rename = "provider")]
110    pub provider: Provider,
111    #[serde(rename = "plan")]
112    pub plan: Plan,
113    #[serde(rename = "creationDate")]
114    pub creation_date: u64,
115    #[serde(rename = "configKeys")]
116    pub config_keys: Vec<String>,
117}
118
119// -----------------------------------------------------------------------------
120// Opts enum
121
122#[cfg_attr(feature = "jsonschemas", derive(JsonSchema))]
123#[derive(Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord, Clone, Debug, Default)]
124pub struct Opts {
125    #[serde(rename = "version", skip_serializing_if = "Option::is_none")]
126    pub version: Option<String>,
127    #[serde(rename = "encryption", skip_serializing_if = "Option::is_none")]
128    pub encryption: Option<String>,
129    #[serde(rename = "services", skip_serializing_if = "Option::is_none")]
130    pub services: Option<String>,
131}
132
133// -----------------------------------------------------------------------------
134// CreateOpts structure
135
136#[cfg_attr(feature = "jsonschemas", derive(JsonSchema))]
137#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Clone, Debug)]
138pub struct CreateOpts {
139    #[serde(rename = "name")]
140    pub name: String,
141    #[serde(rename = "region")]
142    pub region: String,
143    #[serde(rename = "providerId")]
144    pub provider_id: String,
145    #[serde(rename = "plan")]
146    pub plan: String,
147    #[serde(rename = "options")]
148    pub options: Opts,
149}
150
151// -----------------------------------------------------------------------------
152// Error enumerations
153
154#[derive(thiserror::Error, Debug)]
155pub enum Error {
156    #[error("failed to list addons of organisation '{0}', {1}")]
157    List(String, ClientError),
158    #[error("failed to get addon '{0}' of organisation '{1}', {2}")]
159    Get(String, String, ClientError),
160    #[error("failed to get addon '{0}' environment of organisation '{1}', {2}")]
161    Environment(String, String, ClientError),
162    #[error("failed to create addon for organisation '{0}', {1}")]
163    Create(String, ClientError),
164    #[error("failed to delete addon '{0}' for organisation '{1}', {2}")]
165    Delete(String, String, ClientError),
166}
167
168// -----------------------------------------------------------------------------
169// Helpers functions
170
171#[cfg_attr(feature = "tracing", tracing::instrument)]
172/// returns the list of addons for the given organisation
173pub async fn list(client: &Client, organisation_id: &str) -> Result<Vec<Addon>, Error> {
174    let path = format!(
175        "{}/v2/organisations/{}/addons",
176        client.endpoint, organisation_id,
177    );
178
179    #[cfg(feature = "logging")]
180    if log_enabled!(Level::Debug) {
181        debug!(
182            "execute a request to get the list of addons, path: '{}', organisation: '{}'",
183            &path, organisation_id
184        );
185    }
186
187    client
188        .get(&path)
189        .await
190        .map_err(|err| Error::List(organisation_id.to_owned(), err))
191}
192
193#[cfg_attr(feature = "tracing", tracing::instrument)]
194/// returns the addon for the given the organisation and identifier
195pub async fn get(client: &Client, organisation_id: &str, id: &str) -> Result<Addon, Error> {
196    let path = format!(
197        "{}/v2/organisations/{}/addons/{}",
198        client.endpoint, organisation_id, id
199    );
200
201    #[cfg(feature = "logging")]
202    if log_enabled!(Level::Debug) {
203        debug!(
204            "execute a request to get information about an addon, path: '{}', organisation: '{}', id: '{}'",
205            &path, organisation_id, id
206        );
207    }
208
209    client
210        .get(&path)
211        .await
212        .map_err(|err| Error::Get(id.to_owned(), organisation_id.to_owned(), err))
213}
214
215#[cfg_attr(feature = "tracing", tracing::instrument)]
216/// create the addon and returns it
217pub async fn create(
218    client: &Client,
219    organisation_id: &str,
220    opts: &CreateOpts,
221) -> Result<Addon, Error> {
222    let path = format!(
223        "{}/v2/organisations/{}/addons",
224        client.endpoint, organisation_id
225    );
226
227    #[cfg(feature = "logging")]
228    if log_enabled!(Level::Debug) {
229        debug!(
230            "execute a request to create an addon, path: '{}', organisation: '{}', name: '{}', region: '{}', plan: '{}', provider-id: '{}'",
231            &path,
232            organisation_id,
233            &opts.name,
234            &opts.region,
235            &opts.plan,
236            &opts.provider_id.to_string()
237        );
238    }
239
240    client
241        .post(&path, opts)
242        .await
243        .map_err(|err| Error::Create(organisation_id.to_owned(), err))
244}
245
246#[cfg_attr(feature = "tracing", tracing::instrument)]
247/// delete the given addon
248pub async fn delete(client: &Client, organisation_id: &str, id: &str) -> Result<(), Error> {
249    let path = format!(
250        "{}/v2/organisations/{}/addons/{}",
251        client.endpoint, organisation_id, id
252    );
253
254    #[cfg(feature = "logging")]
255    if log_enabled!(Level::Debug) {
256        debug!(
257            "execute a request to delete an addon, path: '{}', organisation: '{}', id: '{}'",
258            &path, organisation_id, id
259        );
260    }
261
262    client
263        .delete(&path)
264        .await
265        .map_err(|err| Error::Delete(id.to_owned(), organisation_id.to_owned(), err))
266}
267
268#[cfg_attr(feature = "tracing", tracing::instrument)]
269/// returns environment variables for an addon
270pub async fn environment(
271    client: &Client,
272    organisation_id: &str,
273    id: &str,
274) -> Result<BTreeMap<String, String>, Error> {
275    let path = format!(
276        "{}/v2/organisations/{}/addons/{}/env",
277        client.endpoint, organisation_id, id
278    );
279
280    #[cfg(feature = "logging")]
281    if log_enabled!(Level::Debug) {
282        debug!(
283            "execute a request to get secret of a addon, path: '{}', organisation: '{}', id: '{}'",
284            &path, organisation_id, id
285        );
286    }
287
288    let env: Vec<Variable> = client
289        .get(&path)
290        .await
291        .map_err(|err| Error::Environment(id.to_owned(), organisation_id.to_owned(), err))?;
292
293    Ok(env.iter().fold(BTreeMap::new(), |mut acc, var| {
294        acc.insert(var.name.to_owned(), var.value.to_owned());
295        acc
296    }))
297}