Skip to main content

gcloud_artifact_registry/
client.rs

1use crate::grpc::apiv1::artifact_registry_client::Client as ArtifactRegistryGrpcClient;
2use google_cloud_gax::conn::{ConnectionManager, ConnectionOptions, Environment, Error};
3use std::ops::{Deref, DerefMut};
4use std::time::Duration;
5use token_source::{NoopTokenSourceProvider, TokenSourceProvider};
6
7use crate::grpc::apiv1::{ARTIFACT_REGISTRY, AUDIENCE, SCOPES};
8
9use google_cloud_googleapis::devtools::artifact_registry::v1::artifact_registry_client::ArtifactRegistryClient;
10use google_cloud_longrunning::autogen::operations_client::OperationsClient;
11
12#[derive(Debug)]
13pub struct ClientConfig {
14    pub artifact_registry_endpoint: String,
15    pub token_source_provider: Box<dyn TokenSourceProvider>,
16    pub timeout: Option<Duration>,
17    pub connect_timeout: Option<Duration>,
18    pub http2_keep_alive_interval: Option<Duration>,
19    pub keep_alive_timeout: Option<Duration>,
20    pub keep_alive_while_idle: Option<bool>,
21}
22
23#[cfg(feature = "auth")]
24pub use google_cloud_auth;
25
26#[cfg(feature = "auth")]
27impl ClientConfig {
28    pub async fn with_auth(self) -> Result<Self, google_cloud_auth::error::Error> {
29        let ts = google_cloud_auth::token::DefaultTokenSourceProvider::new(Self::auth_config()).await?;
30        Ok(self.with_token_source(ts).await)
31    }
32
33    pub async fn with_credentials(
34        self,
35        credentials: google_cloud_auth::credentials::CredentialsFile,
36    ) -> Result<Self, google_cloud_auth::error::Error> {
37        let ts = google_cloud_auth::token::DefaultTokenSourceProvider::new_with_credentials(
38            Self::auth_config(),
39            Box::new(credentials),
40        )
41        .await?;
42        Ok(self.with_token_source(ts).await)
43    }
44
45    async fn with_token_source(mut self, ts: google_cloud_auth::token::DefaultTokenSourceProvider) -> Self {
46        self.token_source_provider = Box::new(ts);
47        self
48    }
49
50    fn auth_config() -> google_cloud_auth::project::Config<'static> {
51        google_cloud_auth::project::Config::default().with_scopes(&SCOPES)
52    }
53}
54
55impl Default for ClientConfig {
56    fn default() -> Self {
57        Self {
58            artifact_registry_endpoint: ARTIFACT_REGISTRY.to_string(),
59            token_source_provider: Box::new(NoopTokenSourceProvider {}),
60            timeout: Some(Duration::from_secs(30)),
61            connect_timeout: Some(Duration::from_secs(30)),
62            http2_keep_alive_interval: None,
63            keep_alive_timeout: None,
64            keep_alive_while_idle: None,
65        }
66    }
67}
68
69#[derive(Clone)]
70pub struct Client {
71    artifact_registry_client: ArtifactRegistryGrpcClient,
72}
73
74impl Client {
75    pub async fn new(config: ClientConfig) -> Result<Self, Error> {
76        let conn_options = ConnectionOptions {
77            timeout: config.timeout,
78            connect_timeout: config.connect_timeout,
79            http2_keep_alive_interval: config.http2_keep_alive_interval,
80            keep_alive_timeout: config.keep_alive_timeout,
81            keep_alive_while_idle: config.keep_alive_while_idle,
82        };
83        let conn_pool = ConnectionManager::new(
84            1,
85            config.artifact_registry_endpoint,
86            AUDIENCE,
87            &Environment::GoogleCloud(config.token_source_provider),
88            &conn_options,
89        )
90        .await?;
91        let conn = conn_pool.conn();
92        let lro_client = OperationsClient::new(conn_pool.conn()).await.unwrap();
93
94        Ok(Self {
95            artifact_registry_client: ArtifactRegistryGrpcClient::new(ArtifactRegistryClient::new(conn), lro_client),
96        })
97    }
98}
99
100impl Deref for Client {
101    type Target = ArtifactRegistryGrpcClient;
102
103    fn deref(&self) -> &Self::Target {
104        &self.artifact_registry_client
105    }
106}
107
108impl DerefMut for Client {
109    fn deref_mut(&mut self) -> &mut Self::Target {
110        &mut self.artifact_registry_client
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use crate::client::{Client, ClientConfig};
117
118    use google_cloud_googleapis::devtools::artifact_registry::v1::repository::Format;
119    use google_cloud_googleapis::devtools::artifact_registry::v1::{
120        CreateRepositoryRequest, DeleteRepositoryRequest, GetRepositoryRequest, ListRepositoriesRequest, Repository,
121        UpdateRepositoryRequest,
122    };
123    use prost_types::FieldMask;
124    use serial_test::serial;
125    use std::time::{SystemTime, UNIX_EPOCH};
126
127    async fn new_client() -> (Client, String) {
128        let cred = google_cloud_auth::credentials::CredentialsFile::new().await.unwrap();
129        let project = cred.project_id.clone().unwrap();
130        let config = ClientConfig::default().with_credentials(cred).await.unwrap();
131        (Client::new(config).await.unwrap(), project)
132    }
133
134    #[ctor::ctor]
135    fn init() {
136        let _ = tracing_subscriber::fmt().try_init();
137    }
138
139    #[tokio::test]
140    #[serial]
141    async fn test_crud_repository() {
142        let (mut client, project) = new_client().await;
143        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
144        let repository_id = format!("gcrar{now}");
145
146        // create
147        let create_request = CreateRepositoryRequest {
148            parent: format!("projects/{project}/locations/us-central1"),
149            repository_id,
150            repository: Some(Repository {
151                name: "".to_string(),
152                format: Format::Docker as i32,
153                description: "test repository".to_string(),
154                labels: Default::default(),
155                create_time: None,
156                update_time: None,
157                kms_key_name: "".to_string(),
158                mode: 0,
159                cleanup_policies: Default::default(),
160                size_bytes: 0,
161                satisfies_pzs: false,
162                cleanup_policy_dry_run: false,
163                format_config: None,
164                mode_config: None,
165                vulnerability_scanning_config: None,
166                disallow_unspecified_mode: false,
167                satisfies_pzi: false,
168                registry_uri: "".to_string(),
169            }),
170        };
171        let mut created_repository = client.create_repository(create_request.clone(), None).await.unwrap();
172        let result = created_repository.wait(None).await.unwrap().unwrap();
173        assert_eq!(
174            format!("{}/repositories/{}", create_request.parent, create_request.repository_id),
175            result.name
176        );
177
178        // get
179        let get_request = GetRepositoryRequest {
180            name: result.name.to_string(),
181        };
182        let get_repository = client.get_repository(get_request.clone(), None).await.unwrap();
183        assert_eq!(get_repository.name, get_request.name);
184
185        // update
186        let update_request = UpdateRepositoryRequest {
187            repository: Some(Repository {
188                description: "update test".to_string(),
189                ..get_repository.clone()
190            }),
191            update_mask: Some(FieldMask {
192                paths: vec!["description".to_string()],
193            }),
194        };
195        let update_repository = client.update_repository(update_request.clone(), None).await.unwrap();
196        assert_eq!(update_repository.description, update_request.repository.unwrap().description);
197
198        // list
199        let list_request = ListRepositoriesRequest {
200            parent: create_request.parent.to_string(),
201            page_size: 0,
202            page_token: "".to_string(),
203            order_by: "".to_string(),
204            filter: "".to_string(),
205        };
206        let list_result = client.list_repositories(list_request, None).await.unwrap();
207        assert!(!list_result.repositories.is_empty());
208
209        // delete
210        let delete_request = DeleteRepositoryRequest {
211            name: get_repository.name.to_string(),
212        };
213        client.delete_repository(delete_request, None).await.unwrap();
214    }
215}