mas_handlers/graphql/mutations/
compat_session.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7use anyhow::Context as _;
8use async_graphql::{Context, Enum, ID, InputObject, Object};
9use mas_storage::{
10    RepositoryAccess,
11    compat::CompatSessionRepository,
12    queue::{QueueJobRepositoryExt as _, SyncDevicesJob},
13};
14
15use crate::graphql::{
16    model::{CompatSession, NodeType},
17    state::ContextExt,
18};
19
20#[derive(Default)]
21pub struct CompatSessionMutations {
22    _private: (),
23}
24
25/// The input of the `endCompatSession` mutation.
26#[derive(InputObject)]
27pub struct EndCompatSessionInput {
28    /// The ID of the session to end.
29    compat_session_id: ID,
30}
31
32/// The payload of the `endCompatSession` mutation.
33pub enum EndCompatSessionPayload {
34    NotFound,
35    Ended(Box<mas_data_model::CompatSession>),
36}
37
38/// The status of the `endCompatSession` mutation.
39#[derive(Enum, Copy, Clone, PartialEq, Eq, Debug)]
40enum EndCompatSessionStatus {
41    /// The session was ended.
42    Ended,
43
44    /// The session was not found.
45    NotFound,
46}
47
48#[Object]
49impl EndCompatSessionPayload {
50    /// The status of the mutation.
51    async fn status(&self) -> EndCompatSessionStatus {
52        match self {
53            Self::Ended(_) => EndCompatSessionStatus::Ended,
54            Self::NotFound => EndCompatSessionStatus::NotFound,
55        }
56    }
57
58    /// Returns the ended session.
59    async fn compat_session(&self) -> Option<CompatSession> {
60        match self {
61            Self::Ended(session) => Some(CompatSession::new(*session.clone())),
62            Self::NotFound => None,
63        }
64    }
65}
66
67#[Object]
68impl CompatSessionMutations {
69    async fn end_compat_session(
70        &self,
71        ctx: &Context<'_>,
72        input: EndCompatSessionInput,
73    ) -> Result<EndCompatSessionPayload, async_graphql::Error> {
74        let state = ctx.state();
75        let mut rng = state.rng();
76        let compat_session_id = NodeType::CompatSession.extract_ulid(&input.compat_session_id)?;
77        let requester = ctx.requester();
78
79        let mut repo = state.repository().await?;
80        let clock = state.clock();
81
82        let session = repo.compat_session().lookup(compat_session_id).await?;
83        let Some(session) = session else {
84            return Ok(EndCompatSessionPayload::NotFound);
85        };
86
87        if !requester.is_owner_or_admin(&session) {
88            return Ok(EndCompatSessionPayload::NotFound);
89        }
90
91        let user = repo
92            .user()
93            .lookup(session.user_id)
94            .await?
95            .context("Could not load user")?;
96
97        // Schedule a job to sync the devices of the user with the homeserver
98        repo.queue_job()
99            .schedule_job(&mut rng, &clock, SyncDevicesJob::new(&user))
100            .await?;
101
102        let session = repo.compat_session().finish(&clock, session).await?;
103
104        repo.save().await?;
105
106        Ok(EndCompatSessionPayload::Ended(Box::new(session)))
107    }
108}