1use 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#[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#[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#[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#[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#[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#[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#[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#[cfg_attr(feature = "tracing", tracing::instrument)]
170pub 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)]
192pub 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)]
214pub 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)]
245pub 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)]
267pub 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}