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