dinio_sdk/api/geo_bans.rs
1//! `/api/v1/geo-bans` (`../core/src/api/geo_bans.ts`). Bans are read nested in their record
2//! group (`Records::get(.., with_geo_bans = true)`); there is no list route.
3
4use reqwest::Method;
5use serde_json::json;
6use uuid::Uuid;
7
8use crate::client::Client;
9use crate::error::Result;
10use crate::models::GeoBan;
11use crate::ops::GeoArea;
12
13/// Geo ban routes, from [`Client::geo_bans`].
14///
15/// A ban stops Relay from answering with one record value to clients in an area. Access
16/// follows the value's zone. To work by area names instead of uuids, see
17/// [`ops::add_geo_bans`](crate::ops::add_geo_bans) and
18/// [`ops::select_geo_bans`](crate::ops::select_geo_bans).
19pub struct GeoBans<'a> {
20 client: &'a Client,
21}
22
23impl<'a> GeoBans<'a> {
24 pub(crate) fn new(client: &'a Client) -> Self {
25 Self { client }
26 }
27
28 /// Bans `area` for the record value `record_id`. Core does not refuse a duplicate;
29 /// [`ops::add_geo_bans`](crate::ops::add_geo_bans) skips areas already banned.
30 ///
31 /// `POST /api/v1/geo-bans`
32 ///
33 /// # Errors
34 ///
35 /// [`ErrorCode::NotFound`](crate::ErrorCode::NotFound) if there is no such value.
36 pub async fn create(&self, record_id: Uuid, area: GeoArea) -> Result<GeoBan> {
37 let request = self
38 .client
39 .request(Method::POST, &["geo-bans"])?
40 .json(&json!({
41 "record_id": record_id,
42 "level": area.level.as_wire(),
43 "code": area.code,
44 }));
45 Ok(self.client.send(request).await?.0)
46 }
47
48 /// Moves a ban to another area.
49 ///
50 /// `PATCH /api/v1/geo-bans/:uuid`
51 ///
52 /// # Errors
53 ///
54 /// [`ErrorCode::NotFound`](crate::ErrorCode::NotFound) if there is no such ban.
55 pub async fn update(&self, uuid: Uuid, area: GeoArea) -> Result<GeoBan> {
56 let request = self
57 .client
58 .request(Method::PATCH, &["geo-bans", &uuid.to_string()])?
59 .json(&json!({ "level": area.level.as_wire(), "code": area.code }));
60 Ok(self.client.send(request).await?.0)
61 }
62
63 /// Removes a ban. `record_id`, when given, must be the ban's value (Core checks it), a
64 /// guard against removing a ban of the wrong value.
65 ///
66 /// `DELETE /api/v1/geo-bans/:uuid[?record_id=...]`
67 ///
68 /// # Errors
69 ///
70 /// [`ErrorCode::NotFound`](crate::ErrorCode::NotFound) if there is no such ban, or it
71 /// belongs to another value than `record_id`.
72 pub async fn delete(&self, uuid: Uuid, record_id: Option<Uuid>) -> Result<()> {
73 let mut request = self
74 .client
75 .request(Method::DELETE, &["geo-bans", &uuid.to_string()])?;
76 if let Some(record_id) = record_id {
77 request = request.query(&[("record_id", record_id.to_string())]);
78 }
79 self.client.send_empty(request).await
80 }
81}