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, 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#[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#[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#[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#[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#[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#[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#[cfg_attr(feature = "tracing", tracing::instrument)]
172pub 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)]
194pub 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)]
216pub 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)]
247pub 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)]
269pub 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}