ruma_client_api/push/
set_pushrule.rs

1//! `PUT /_matrix/client/*/pushrules/global/{kind}/{ruleId}`
2//!
3//! This endpoint allows the creation and modification of push rules for this user ID.
4
5pub mod v3 {
6    //! `/v3/` ([spec])
7    //!
8    //! [spec]: https://spec.matrix.org/latest/client-server-api/#put_matrixclientv3pushrulesglobalkindruleid
9
10    use ruma_common::{
11        api::{auth_scheme::AccessToken, response, Metadata},
12        metadata,
13        push::{Action, NewPushRule, PushCondition},
14    };
15    use serde::{Deserialize, Serialize};
16
17    metadata! {
18        method: PUT,
19        rate_limited: true,
20        authentication: AccessToken,
21        history: {
22            1.0 => "/_matrix/client/r0/pushrules/global/{kind}/{rule_id}",
23            1.1 => "/_matrix/client/v3/pushrules/global/{kind}/{rule_id}",
24        }
25    }
26
27    /// Request type for the `set_pushrule` endpoint.
28    #[derive(Clone, Debug)]
29    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
30    pub struct Request {
31        /// The rule.
32        pub rule: NewPushRule,
33
34        /// Use 'before' with a rule_id as its value to make the new rule the next-most important
35        /// rule with respect to the given user defined rule.
36        pub before: Option<String>,
37
38        /// This makes the new rule the next-less important rule relative to the given user defined
39        /// rule.
40        pub after: Option<String>,
41    }
42
43    /// Response type for the `set_pushrule` endpoint.
44    #[response(error = crate::Error)]
45    #[derive(Default)]
46    pub struct Response {}
47
48    impl Request {
49        /// Creates a new `Request` with the given rule.
50        pub fn new(rule: NewPushRule) -> Self {
51            Self { rule, before: None, after: None }
52        }
53    }
54
55    impl Response {
56        /// Creates an empty `Response`.
57        pub fn new() -> Self {
58            Self {}
59        }
60    }
61
62    #[cfg(feature = "client")]
63    impl ruma_common::api::OutgoingRequest for Request {
64        type EndpointError = crate::Error;
65        type IncomingResponse = Response;
66
67        fn try_into_http_request<T: Default + bytes::BufMut + AsRef<[u8]>>(
68            self,
69            base_url: &str,
70            access_token: ruma_common::api::auth_scheme::SendAccessToken<'_>,
71            considering: std::borrow::Cow<'_, ruma_common::api::SupportedVersions>,
72        ) -> Result<http::Request<T>, ruma_common::api::error::IntoHttpError> {
73            use ruma_common::api::auth_scheme::AuthScheme;
74
75            let query_string = serde_html_form::to_string(RequestQuery {
76                before: self.before,
77                after: self.after,
78            })?;
79
80            let url = Self::make_endpoint_url(
81                considering,
82                base_url,
83                &[&self.rule.kind(), &self.rule.rule_id()],
84                &query_string,
85            )?;
86
87            let body: RequestBody = self.rule.into();
88
89            let mut http_request = http::Request::builder()
90                .method(Self::METHOD)
91                .uri(url)
92                .header(http::header::CONTENT_TYPE, ruma_common::http_headers::APPLICATION_JSON)
93                .body(ruma_common::serde::json_to_buf(&body)?)?;
94
95            Self::Authentication::add_authentication(&mut http_request, access_token).map_err(
96                |error| ruma_common::api::error::IntoHttpError::Authentication(error.into()),
97            )?;
98
99            Ok(http_request)
100        }
101    }
102
103    #[cfg(feature = "server")]
104    impl ruma_common::api::IncomingRequest for Request {
105        type EndpointError = crate::Error;
106        type OutgoingResponse = Response;
107
108        fn try_from_http_request<B, S>(
109            request: http::Request<B>,
110            path_args: &[S],
111        ) -> Result<Self, ruma_common::api::error::FromHttpRequestError>
112        where
113            B: AsRef<[u8]>,
114            S: AsRef<str>,
115        {
116            use ruma_common::push::{
117                NewConditionalPushRule, NewPatternedPushRule, NewSimplePushRule,
118            };
119
120            // Exhaustive enum to fail deserialization on unknown variants.
121            #[derive(Debug, Deserialize)]
122            #[serde(rename_all = "lowercase")]
123            enum RuleKind {
124                Override,
125                Underride,
126                Sender,
127                Room,
128                Content,
129            }
130
131            #[derive(Deserialize)]
132            struct IncomingRequestQuery {
133                before: Option<String>,
134                after: Option<String>,
135            }
136
137            Self::check_request_method(request.method())?;
138
139            let (kind, rule_id): (RuleKind, String) =
140                Deserialize::deserialize(serde::de::value::SeqDeserializer::<
141                    _,
142                    serde::de::value::Error,
143                >::new(
144                    path_args.iter().map(::std::convert::AsRef::as_ref)
145                ))?;
146
147            let IncomingRequestQuery { before, after } =
148                serde_html_form::from_str(request.uri().query().unwrap_or(""))?;
149
150            let rule = match kind {
151                RuleKind::Override => {
152                    let ConditionalRequestBody { actions, conditions } =
153                        serde_json::from_slice(request.body().as_ref())?;
154                    NewPushRule::Override(NewConditionalPushRule::new(rule_id, conditions, actions))
155                }
156                RuleKind::Underride => {
157                    let ConditionalRequestBody { actions, conditions } =
158                        serde_json::from_slice(request.body().as_ref())?;
159                    NewPushRule::Underride(NewConditionalPushRule::new(
160                        rule_id, conditions, actions,
161                    ))
162                }
163                RuleKind::Sender => {
164                    let SimpleRequestBody { actions } =
165                        serde_json::from_slice(request.body().as_ref())?;
166                    let rule_id = rule_id.try_into()?;
167                    NewPushRule::Sender(NewSimplePushRule::new(rule_id, actions))
168                }
169                RuleKind::Room => {
170                    let SimpleRequestBody { actions } =
171                        serde_json::from_slice(request.body().as_ref())?;
172                    let rule_id = rule_id.try_into()?;
173                    NewPushRule::Room(NewSimplePushRule::new(rule_id, actions))
174                }
175                RuleKind::Content => {
176                    let PatternedRequestBody { actions, pattern } =
177                        serde_json::from_slice(request.body().as_ref())?;
178                    NewPushRule::Content(NewPatternedPushRule::new(rule_id, pattern, actions))
179                }
180            };
181
182            Ok(Self { rule, before, after })
183        }
184    }
185
186    #[derive(Debug, Serialize)]
187    struct RequestQuery {
188        #[serde(skip_serializing_if = "Option::is_none")]
189        before: Option<String>,
190
191        #[serde(skip_serializing_if = "Option::is_none")]
192        after: Option<String>,
193    }
194
195    #[derive(Debug, Serialize)]
196    #[serde(untagged)]
197    enum RequestBody {
198        Simple(SimpleRequestBody),
199
200        Patterned(PatternedRequestBody),
201
202        Conditional(ConditionalRequestBody),
203    }
204
205    #[derive(Debug, Serialize, Deserialize)]
206    struct SimpleRequestBody {
207        actions: Vec<Action>,
208    }
209
210    #[derive(Debug, Serialize, Deserialize)]
211    struct PatternedRequestBody {
212        actions: Vec<Action>,
213
214        pattern: String,
215    }
216
217    #[derive(Debug, Serialize, Deserialize)]
218    struct ConditionalRequestBody {
219        actions: Vec<Action>,
220
221        conditions: Vec<PushCondition>,
222    }
223
224    impl From<NewPushRule> for RequestBody {
225        fn from(rule: NewPushRule) -> Self {
226            match rule {
227                NewPushRule::Override(r) => RequestBody::Conditional(ConditionalRequestBody {
228                    actions: r.actions,
229                    conditions: r.conditions,
230                }),
231                NewPushRule::Content(r) => RequestBody::Patterned(PatternedRequestBody {
232                    actions: r.actions,
233                    pattern: r.pattern,
234                }),
235                NewPushRule::Room(r) => {
236                    RequestBody::Simple(SimpleRequestBody { actions: r.actions })
237                }
238                NewPushRule::Sender(r) => {
239                    RequestBody::Simple(SimpleRequestBody { actions: r.actions })
240                }
241                NewPushRule::Underride(r) => RequestBody::Conditional(ConditionalRequestBody {
242                    actions: r.actions,
243                    conditions: r.conditions,
244                }),
245                #[cfg(not(ruma_unstable_exhaustive_types))]
246                _ => unreachable!("variant added to NewPushRule not covered by RequestBody"),
247            }
248        }
249    }
250}