unitycatalog_server/api/
shares.rs1use itertools::Itertools;
2use std::collections::HashMap;
3
4use unitycatalog_common::models::ObjectLabel;
5use unitycatalog_common::models::shares::v1::*;
6use unitycatalog_common::models::{ResourceIdent, ResourceName, ResourceRef};
7
8use super::{RequestContext, SecuredAction};
9pub use crate::codegen::shares::ShareHandler;
10use crate::policy::{Permission, Policy, process_resources};
11use crate::store::ResourceStore;
12use crate::{Error, Result};
13
14#[async_trait::async_trait]
15impl<T: ResourceStore + Policy<RequestContext>> ShareHandler<RequestContext> for T {
16 #[tracing::instrument(skip(self, context), fields(resource_name))]
17 async fn create_share(
18 &self,
19 request: CreateShareRequest,
20 context: RequestContext,
21 ) -> Result<Share> {
22 tracing::Span::current().record("resource_name", &request.name);
23 self.check_required(&request, &context).await?;
24 let resource = Share {
25 name: request.name,
26 comment: request.comment,
27 ..Default::default()
28 };
29 Ok(self.create(resource.into()).await?.0.try_into()?)
33 }
34
35 #[tracing::instrument(skip(self, context), fields(resource_name))]
36 async fn delete_share(
37 &self,
38 request: DeleteShareRequest,
39 context: RequestContext,
40 ) -> Result<()> {
41 tracing::Span::current().record("resource_name", &request.name);
42 self.check_required(&request, &context).await?;
43 Ok(self.delete(&request.resource()).await?)
44 }
45
46 #[tracing::instrument(skip(self, context))]
47 async fn list_shares(
48 &self,
49 request: ListSharesRequest,
50 context: RequestContext,
51 ) -> Result<ListSharesResponse> {
52 self.check_required(&request, &context).await?;
53 let (mut resources, next_page_token) = self
54 .list(
55 &ObjectLabel::Share,
56 None,
57 request.max_results.map(|v| v as usize),
58 request.page_token,
59 )
60 .await?;
61 process_resources(self, &context, &Permission::Read, &mut resources).await?;
62 Ok(ListSharesResponse {
63 shares: resources.into_iter().map(|r| r.try_into()).try_collect()?,
64 next_page_token,
65 ..Default::default()
66 })
67 }
68
69 #[tracing::instrument(skip(self, context), fields(resource_name))]
70 async fn get_share(&self, request: GetShareRequest, context: RequestContext) -> Result<Share> {
71 tracing::Span::current().record("resource_name", &request.name);
72 self.check_required(&request, &context).await?;
73 Ok(self.get(&request.resource()).await?.0.try_into()?)
74 }
75
76 #[tracing::instrument(skip(self, context), fields(resource_name))]
77 async fn update_share(
78 &self,
79 request: UpdateShareRequest,
80 context: RequestContext,
81 ) -> Result<Share> {
82 tracing::Span::current().record("resource_name", &request.name);
83 self.check_required(&request, &context).await?;
84 let ident = request.resource();
85 let current: Share = self.get(&ident).await?.0.try_into()?;
86
87 let mut data_objects: HashMap<String, DataObject> = current
89 .objects
90 .into_iter()
91 .map(|d| (d.name.clone(), d))
92 .collect();
93 for update in request.updates.iter() {
94 match update.action.as_known().unwrap_or_default() {
95 Action::ADD => {
96 if let Some(obj) = update.data_object.as_option() {
97 if data_objects.contains_key(&obj.name) {
98 return Err(Error::AlreadyExists);
99 }
100 data_objects.insert(obj.name.clone(), obj.clone());
101 }
102 }
103 Action::REMOVE => {
104 if let Some(obj) = update.data_object.as_option() {
105 data_objects.remove(&obj.name);
106 }
107 }
108 Action::UPDATE => {
109 if let Some(obj) = update.data_object.as_option() {
110 if let Some(existing) = data_objects.get_mut(&obj.name) {
111 *existing = obj.clone();
112 } else {
113 return Err(Error::NotFound);
114 }
115 }
116 }
117 Action::ACTION_UNSPECIFIED => {
118 return Err(Error::InvalidArgument("Unspecified action".to_string()));
119 }
120 }
121 }
122
123 let resource = Share {
124 name: request.new_name.unwrap_or_else(|| request.name.clone()),
125 comment: request.comment.or(current.comment),
126 owner: request.owner.or(current.owner),
127 objects: data_objects.into_values().collect(),
128 ..Default::default()
129 };
130 Ok(self.update(&ident, resource.into()).await?.0.try_into()?)
133 }
134
135 #[tracing::instrument(skip(self, context), fields(resource_name))]
136 async fn get_permissions(
137 &self,
138 request: GetPermissionsRequest,
139 context: RequestContext,
140 ) -> Result<GetPermissionsResponse> {
141 tracing::Span::current().record("resource_name", &request.name);
142 self.check_required(&request, &context).await?;
143 let _ = self.get(&request.resource()).await?; Ok(GetPermissionsResponse {
158 privilege_assignments: vec![],
159 next_page_token: None,
160 ..Default::default()
161 })
162 }
163
164 #[tracing::instrument(skip(self, context), fields(resource_name))]
165 async fn update_permissions(
166 &self,
167 request: UpdatePermissionsRequest,
168 context: RequestContext,
169 ) -> Result<UpdatePermissionsResponse> {
170 tracing::Span::current().record("resource_name", &request.name);
171 self.check_required(&request, &context).await?;
172 let _ = self.get(&request.resource()).await?; Ok(UpdatePermissionsResponse {
186 privilege_assignments: vec![],
187 ..Default::default()
188 })
189 }
190}
191
192impl SecuredAction for CreateShareRequest {
193 fn resource(&self) -> ResourceIdent {
194 ResourceIdent::share(ResourceName::new([self.name.as_str()]))
195 }
196
197 fn permission(&self) -> &'static Permission {
198 &Permission::Create
199 }
200}
201
202impl SecuredAction for ListSharesRequest {
203 fn resource(&self) -> ResourceIdent {
204 ResourceIdent::share(ResourceRef::Undefined)
205 }
206
207 fn permission(&self) -> &'static Permission {
208 &Permission::Read
209 }
210}
211
212impl SecuredAction for GetShareRequest {
213 fn resource(&self) -> ResourceIdent {
214 ResourceIdent::share(ResourceName::new([self.name.as_str()]))
215 }
216
217 fn permission(&self) -> &'static Permission {
218 &Permission::Read
219 }
220}
221
222impl SecuredAction for UpdateShareRequest {
223 fn resource(&self) -> ResourceIdent {
224 ResourceIdent::share(ResourceName::new([self.name.as_str()]))
225 }
226
227 fn permission(&self) -> &'static Permission {
228 &Permission::Manage
229 }
230}
231
232impl SecuredAction for DeleteShareRequest {
233 fn resource(&self) -> ResourceIdent {
234 ResourceIdent::share(ResourceName::new([self.name.as_str()]))
235 }
236
237 fn permission(&self) -> &'static Permission {
238 &Permission::Manage
239 }
240}
241
242impl SecuredAction for GetPermissionsRequest {
243 fn resource(&self) -> ResourceIdent {
244 ResourceIdent::share(ResourceName::new([self.name.as_str()]))
245 }
246
247 fn permission(&self) -> &'static Permission {
248 &Permission::Read
249 }
250}
251
252impl SecuredAction for UpdatePermissionsRequest {
253 fn resource(&self) -> ResourceIdent {
254 ResourceIdent::share(ResourceName::new([self.name.as_str()]))
255 }
256
257 fn permission(&self) -> &'static Permission {
258 &Permission::Manage
259 }
260}