Skip to main content

unitycatalog_server/api/
providers.rs

1use itertools::Itertools;
2
3use unitycatalog_common::models::ObjectLabel;
4use unitycatalog_common::models::providers::v1::*;
5use unitycatalog_common::models::{ResourceIdent, ResourceName, ResourceRef};
6
7use super::{RequestContext, SecuredAction};
8use crate::Result;
9pub use crate::codegen::providers::ProviderHandler;
10use crate::policy::{Permission, Policy, process_resources};
11use crate::store::ResourceStore;
12
13#[async_trait::async_trait]
14impl<T: ResourceStore + Policy<RequestContext>> ProviderHandler<RequestContext> for T {
15    #[tracing::instrument(skip(self, context), fields(resource_name))]
16    async fn create_provider(
17        &self,
18        request: CreateProviderRequest,
19        context: RequestContext,
20    ) -> Result<Provider> {
21        tracing::Span::current().record("resource_name", &request.name);
22        self.check_required(&request, &context).await?;
23        let resource = Provider {
24            name: request.name,
25            authentication_type: request.authentication_type,
26            owner: request.owner,
27            comment: request.comment,
28            recipient_profile_str: request.recipient_profile_str,
29            properties: request.properties,
30            ..Default::default()
31        };
32        let info = self.create(resource.into()).await?.0.try_into()?;
33        Ok(info)
34    }
35
36    #[tracing::instrument(skip(self, context), fields(resource_name))]
37    async fn delete_provider(
38        &self,
39        request: DeleteProviderRequest,
40        context: RequestContext,
41    ) -> Result<()> {
42        tracing::Span::current().record("resource_name", &request.name);
43        self.check_required(&request, &context).await?;
44        Ok(self.delete(&request.resource()).await?)
45    }
46
47    #[tracing::instrument(skip(self, context), fields(resource_name))]
48    async fn get_provider(
49        &self,
50        request: GetProviderRequest,
51        context: RequestContext,
52    ) -> Result<Provider> {
53        tracing::Span::current().record("resource_name", &request.name);
54        self.check_required(&request, &context).await?;
55        Ok(self.get(&request.resource()).await?.0.try_into()?)
56    }
57
58    #[tracing::instrument(skip(self, context))]
59    async fn list_providers(
60        &self,
61        request: ListProvidersRequest,
62        context: RequestContext,
63    ) -> Result<ListProvidersResponse> {
64        self.check_required(&request, &context).await?;
65        let (mut resources, next_page_token) = self
66            .list(
67                &ObjectLabel::Provider,
68                None,
69                request.max_results.map(|v| v as usize),
70                request.page_token,
71            )
72            .await?;
73        process_resources(self, &context, &Permission::Read, &mut resources).await?;
74        Ok(ListProvidersResponse {
75            providers: resources.into_iter().map(|r| r.try_into()).try_collect()?,
76            next_page_token,
77            ..Default::default()
78        })
79    }
80
81    #[tracing::instrument(skip(self, context), fields(resource_name))]
82    async fn update_provider(
83        &self,
84        request: UpdateProviderRequest,
85        context: RequestContext,
86    ) -> Result<Provider> {
87        tracing::Span::current().record("resource_name", &request.name);
88        self.check_required(&request, &context).await?;
89        let ident = request.resource();
90        let current: Provider = self.get(&ident).await?.0.try_into()?;
91
92        let updated = Provider {
93            name: request.new_name.unwrap_or(request.name),
94            owner: request.owner.or(current.owner),
95            comment: request.comment.or(current.comment),
96            recipient_profile_str: request
97                .recipient_profile_str
98                .or(current.recipient_profile_str),
99            properties: if request.properties.is_empty() {
100                current.properties
101            } else {
102                request.properties
103            },
104            // Preserve all fields managed by the store.
105            ..current
106        };
107
108        Ok(self.update(&ident, updated.into()).await?.0.try_into()?)
109    }
110}
111
112impl SecuredAction for CreateProviderRequest {
113    fn resource(&self) -> ResourceIdent {
114        ResourceIdent::provider(ResourceName::new([self.name.as_str()]))
115    }
116
117    fn permission(&self) -> &'static Permission {
118        &Permission::Create
119    }
120}
121
122impl SecuredAction for ListProvidersRequest {
123    fn resource(&self) -> ResourceIdent {
124        ResourceIdent::provider(ResourceRef::Undefined)
125    }
126
127    fn permission(&self) -> &'static Permission {
128        &Permission::Read
129    }
130}
131
132impl SecuredAction for GetProviderRequest {
133    fn resource(&self) -> ResourceIdent {
134        ResourceIdent::provider(ResourceName::new([self.name.as_str()]))
135    }
136
137    fn permission(&self) -> &'static Permission {
138        &Permission::Read
139    }
140}
141
142impl SecuredAction for UpdateProviderRequest {
143    fn resource(&self) -> ResourceIdent {
144        ResourceIdent::provider(ResourceName::new([self.name.as_str()]))
145    }
146
147    fn permission(&self) -> &'static Permission {
148        &Permission::Manage
149    }
150}
151
152impl SecuredAction for DeleteProviderRequest {
153    fn resource(&self) -> ResourceIdent {
154        ResourceIdent::provider(ResourceName::new([self.name.as_str()]))
155    }
156
157    fn permission(&self) -> &'static Permission {
158        &Permission::Manage
159    }
160}