seaplane_cli/api/
locks.rs1use reqwest::Url;
2use seaplane::{
3 api::{
4 identity::v0::AccessToken,
5 locks::v1::{
6 HeldLock as HeldLockModel, LockId, LockInfo as LockInfoModel, LockInfoRange, LockName,
7 LocksRequest, LocksRequestBuilder,
8 },
9 shared::v1::{Directory, RangeQueryContext},
10 ApiErrorKind,
11 },
12 error::SeaplaneError,
13};
14
15use crate::{
16 api::request_token,
17 context::Ctx,
18 error::{CliError, Result},
19};
20
21#[derive(Debug)]
24pub struct LocksReq {
25 api_key: String,
26 lock_id: Option<String>,
27 name: Option<LockName>,
28 token: Option<AccessToken>,
29 inner: Option<LocksRequest>,
30 identity_url: Option<Url>,
31 locks_url: Option<Url>,
32 insecure_urls: bool,
33 invalid_certs: bool,
34}
35
36impl LocksReq {
37 pub fn new(ctx: &Ctx) -> Result<Self> {
38 Ok(Self {
39 api_key: ctx.args.api_key()?.into(),
40 lock_id: None,
41 name: None,
42 token: None,
43 inner: None,
44 identity_url: ctx.identity_url.clone(),
45 locks_url: ctx.locks_url.clone(),
46 #[cfg(feature = "allow_insecure_urls")]
47 insecure_urls: ctx.insecure_urls,
48 #[cfg(not(feature = "allow_insecure_urls"))]
49 insecure_urls: false,
50 #[cfg(feature = "allow_invalid_certs")]
51 invalid_certs: ctx.invalid_certs,
52 #[cfg(not(feature = "allow_invalid_certs"))]
53 invalid_certs: false,
54 })
55 }
56
57 pub fn set_identifiers<S: Into<String>>(
58 &mut self,
59 name: Option<LockName>,
60 lock_id: Option<S>,
61 ) -> Result<()> {
62 self.name = name;
63 self.lock_id = lock_id.map(|s| s.into());
64 self.refresh_inner()
65 }
66
67 pub fn set_name(&mut self, name: LockName) -> Result<()> {
68 self.name = Some(name);
69 self.refresh_inner()
70 }
71
72 pub fn refresh_token(&mut self) -> Result<()> {
74 self.token = Some(request_token(
75 &self.api_key,
76 self.identity_url.as_ref(),
77 self.insecure_urls,
78 self.invalid_certs,
79 )?);
80 Ok(())
81 }
82
83 fn refresh_inner(&mut self) -> Result<()> {
87 let mut builder = LocksRequest::builder().token(self.token_or_refresh()?);
88
89 #[cfg(feature = "allow_insecure_urls")]
90 {
91 builder = builder.allow_http(self.insecure_urls);
92 }
93 #[cfg(feature = "allow_invalid_certs")]
94 {
95 builder = builder.allow_invalid_certs(self.invalid_certs);
96 }
97 if self.name.is_none() {
98 panic!("all LocksRequests must have a name")
99 }
100
101 match &self.lock_id {
102 Some(lock_id) => {
103 let default_sequencer_value = 0u32;
104 builder = builder.held_lock(HeldLockModel::new(
105 self.name.clone().unwrap(),
106 LockId::from_encoded(lock_id),
107 default_sequencer_value,
108 ));
109 }
110 None => builder = builder.lock_name(self.name.clone().unwrap()),
111 }
112
113 if let Some(url) = &self.locks_url {
114 builder = builder.base_url(url);
115 }
116
117 self.inner = Some(builder.build().map_err(CliError::from)?);
118 Ok(())
119 }
120
121 pub fn token_or_refresh(&mut self) -> Result<&str> {
123 if self.token.is_none() {
124 self.refresh_token()?;
125 }
126 Ok(&self.token.as_ref().unwrap().token)
127 }
128
129 pub fn get_page(
132 &mut self,
133 next_key: Option<LockName>,
134 dir: Option<LockName>,
135 ) -> Result<LockInfoRange> {
136 let mut range = RangeQueryContext::new();
139 if let Some(k) = next_key {
140 range.set_from(k);
141 }
142
143 if let Some(d) = dir {
144 range.set_directory(Directory::from_encoded(d.encoded()));
145 }
146
147 let mut builder = LocksRequestBuilder::new()
148 .token(self.token_or_refresh()?)
149 .range(range.clone());
150 #[cfg(feature = "allow_insecure_urls")]
151 {
152 builder = builder.allow_http(self.insecure_urls);
153 }
154 #[cfg(feature = "allow_invalid_certs")]
155 {
156 builder = builder.allow_http(self.invalid_certs);
157 }
158
159 if let Some(url) = &self.locks_url {
160 builder = builder.base_url(url);
161 }
162
163 let req = builder.build().unwrap();
164
165 match req.get_page() {
166 Err(SeaplaneError::ApiResponse(ae)) if ae.kind == ApiErrorKind::Unauthorized => {
167 self.token = Some(request_token(
168 &self.api_key,
169 self.identity_url.as_ref(),
170 self.insecure_urls,
171 self.invalid_certs,
172 )?);
173 let next_req = LocksRequestBuilder::new()
174 .token(self.token_or_refresh()?)
175 .range(range)
176 .build()
177 .unwrap();
178
179 Ok(next_req.get_page()?)
180 }
181 result => result.map_err(CliError::from),
182 }
183 }
184}
185
186impl LocksReq {
189 pub fn acquire(&mut self, ttl: u32, client_id: &str) -> Result<HeldLockModel> {
190 maybe_retry!(self.acquire(ttl, client_id))
191 }
192 pub fn release(&mut self) -> Result<()> { maybe_retry!(self.release()) }
193 pub fn renew(&mut self, ttl: u32) -> Result<()> { maybe_retry!(self.renew(ttl)) }
194 pub fn get_lock_info(&mut self) -> Result<LockInfoModel> { maybe_retry!(self.get_lock_info()) }
195}