Skip to main content

couchbase_core/mgmtx/
user_helper.rs

1/*
2 *
3 *  * Copyright (c) 2025 Couchbase, Inc.
4 *  *
5 *  * Licensed under the Apache License, Version 2.0 (the "License");
6 *  * you may not use this file except in compliance with the License.
7 *  * You may obtain a copy of the License at
8 *  *
9 *  *    http://www.apache.org/licenses/LICENSE-2.0
10 *  *
11 *  * Unless required by applicable law or agreed to in writing, software
12 *  * distributed under the License is distributed on an "AS IS" BASIS,
13 *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  * See the License for the specific language governing permissions and
15 *  * limitations under the License.
16 *
17 */
18
19use crate::httpx::client::Client;
20use crate::httpx::request::OnBehalfOfInfo;
21use crate::mgmtx::error;
22use crate::mgmtx::mgmt::Management;
23use crate::mgmtx::node_target::NodeTarget;
24use crate::mgmtx::options::{EnsureUserPollOptions, GetUserOptions};
25use std::sync::Arc;
26
27#[derive(Debug, Clone)]
28pub struct EnsureUserHelper<'a> {
29    user_agent: &'a str,
30    username: &'a str,
31    auth_domain: &'a str,
32    want_missing: bool,
33
34    on_behalf_of_info: Option<&'a OnBehalfOfInfo>,
35
36    confirmed_endpoints: Vec<&'a str>,
37}
38
39impl<'a> EnsureUserHelper<'a> {
40    pub fn new(
41        user_agent: &'a str,
42        username: &'a str,
43        auth_domain: &'a str,
44        want_missing: bool,
45        on_behalf_of_info: Option<&'a OnBehalfOfInfo>,
46    ) -> Self {
47        Self {
48            user_agent,
49            username,
50            auth_domain,
51            want_missing,
52            on_behalf_of_info,
53            confirmed_endpoints: vec![],
54        }
55    }
56
57    async fn poll_one<C: Client>(
58        &self,
59        client: Arc<C>,
60        target: &NodeTarget,
61    ) -> error::Result<bool> {
62        let resp = Management {
63            http_client: client,
64            user_agent: self.user_agent.to_string(),
65            endpoint: target.endpoint.to_string(),
66            canonical_endpoint: target.canonical_endpoint.to_string(),
67            auth: target.auth.clone(),
68            tracing: Default::default(),
69        }
70        .get_user(&GetUserOptions {
71            on_behalf_of_info: self.on_behalf_of_info,
72            username: self.username,
73            auth_domain: self.auth_domain,
74        })
75        .await;
76
77        match resp {
78            Ok(_) => Ok(!self.want_missing),
79            Err(e) => {
80                if let error::ErrorKind::Server(e) = e.kind() {
81                    if e.kind() == &error::ServerErrorKind::UserNotFound {
82                        if self.want_missing {
83                            return Ok(true);
84                        }
85
86                        return Ok(false);
87                    }
88                }
89
90                Err(e)
91            }
92        }
93    }
94
95    pub async fn poll<C: Client>(
96        &mut self,
97        opts: &'a EnsureUserPollOptions<C>,
98    ) -> error::Result<bool> {
99        let mut filtered_targets = vec![];
100        for target in &opts.targets {
101            if !self.confirmed_endpoints.contains(&target.endpoint.as_str()) {
102                filtered_targets.push(target);
103            }
104        }
105
106        let mut success_endpoints = vec![];
107        for target in &opts.targets {
108            if self.poll_one(opts.client.clone(), target).await? {
109                success_endpoints.push(target.endpoint.as_str());
110            }
111        }
112
113        self.confirmed_endpoints
114            .extend_from_slice(success_endpoints.as_slice());
115
116        Ok(success_endpoints.len() == filtered_targets.len())
117    }
118}