1use crate::aws::aws_request_utils::{AwsRequestBuilderExt, AwsSignConfig};
2use crate::aws::credential_provider::AwsCredentialProvider;
3use alien_client_core::{ErrorData, Result};
4use alien_error::{Context, ContextError, IntoAlienError};
5use bon::Builder;
6#[cfg(feature = "test-utils")]
7use mockall::automock;
8use reqwest::{Client, StatusCode};
9use serde::de::DeserializeOwned;
10use serde::{Deserialize, Serialize};
11
12#[cfg_attr(feature = "test-utils", automock)]
13#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
14#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
15pub trait CodeBuildApi: Send + Sync + std::fmt::Debug {
16 async fn create_project(&self, request: CreateProjectRequest) -> Result<CreateProjectResponse>;
17 async fn delete_project(&self, request: DeleteProjectRequest) -> Result<DeleteProjectResponse>;
18 async fn update_project(&self, request: UpdateProjectRequest) -> Result<UpdateProjectResponse>;
19 async fn batch_get_projects(
20 &self,
21 request: BatchGetProjectsRequest,
22 ) -> Result<BatchGetProjectsResponse>;
23 async fn start_build(&self, request: StartBuildRequest) -> Result<StartBuildResponse>;
24 async fn stop_build(&self, request: StopBuildRequest) -> Result<StopBuildResponse>;
25 async fn batch_get_builds(
26 &self,
27 request: BatchGetBuildsRequest,
28 ) -> Result<BatchGetBuildsResponse>;
29 async fn batch_delete_builds(
30 &self,
31 request: BatchDeleteBuildsRequest,
32 ) -> Result<BatchDeleteBuildsResponse>;
33 async fn retry_build(&self, request: RetryBuildRequest) -> Result<RetryBuildResponse>;
34}
35
36#[derive(Debug, Clone)]
40pub struct CodeBuildClient {
41 client: Client,
42 credentials: AwsCredentialProvider,
43}
44
45impl CodeBuildClient {
46 pub fn new(client: Client, credentials: AwsCredentialProvider) -> Self {
47 Self {
48 client,
49 credentials,
50 }
51 }
52
53 pub fn region(&self) -> &str {
55 self.credentials.region()
56 }
57
58 fn sign_config(&self) -> AwsSignConfig {
59 AwsSignConfig {
60 service_name: "codebuild".into(),
61 region: self.credentials.region().to_string(),
62 credentials: self.credentials.get_credentials(),
63 signing_region: None,
64 }
65 }
66
67 fn get_base_url(&self) -> String {
68 if let Some(override_url) = self.credentials.get_service_endpoint_option("codebuild") {
69 override_url.to_string()
70 } else {
71 format!(
72 "https://codebuild.{}.amazonaws.com",
73 self.credentials.region()
74 )
75 }
76 }
77
78 async fn post_json<T: DeserializeOwned + Send + 'static>(
81 &self,
82 action: &str,
83 body: String,
84 resource: &str,
85 ) -> Result<T> {
86 self.credentials.ensure_fresh().await?;
87 let base_url = self.get_base_url();
88 let url = format!("{}/", base_url.trim_end_matches('/'));
89
90 let builder = self
91 .client
92 .post(&url)
93 .host(&format!(
94 "codebuild.{}.amazonaws.com",
95 self.credentials.region()
96 ))
97 .header("X-Amz-Target", format!("CodeBuild_20161006.{}", action))
98 .header("Content-Type", "application/x-amz-json-1.1")
99 .body(body.clone());
100
101 let result =
102 crate::aws::aws_request_utils::sign_send_json(builder, &self.sign_config()).await;
103
104 Self::map_result(result, action, resource, Some(&body))
105 }
106
107 async fn post_empty_response(&self, action: &str, body: String, resource: &str) -> Result<()> {
108 self.credentials.ensure_fresh().await?;
109 let base_url = self.get_base_url();
110 let url = format!("{}/", base_url.trim_end_matches('/'));
111
112 let builder = self
113 .client
114 .post(&url)
115 .host(&format!(
116 "codebuild.{}.amazonaws.com",
117 self.credentials.region()
118 ))
119 .header("X-Amz-Target", format!("CodeBuild_20161006.{}", action))
120 .header("Content-Type", "application/x-amz-json-1.1")
121 .body(body.clone());
122
123 let result =
124 crate::aws::aws_request_utils::sign_send_no_response(builder, &self.sign_config())
125 .await;
126
127 Self::map_result(result, action, resource, Some(&body))
128 }
129
130 fn map_result<T>(
131 result: Result<T>,
132 operation: &str,
133 resource: &str,
134 request_body: Option<&str>,
135 ) -> Result<T> {
136 match result {
137 Ok(v) => Ok(v),
138 Err(e) => {
139 if let Some(ErrorData::HttpResponseError {
140 http_status,
141 http_response_text: Some(ref text),
142 ..
143 }) = &e.error
144 {
145 let status = StatusCode::from_u16(*http_status)
146 .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
147 if let Some(mapped) =
148 Self::map_codebuild_error(status, text, operation, resource, request_body)
149 {
150 Err(e.context(mapped))
151 } else {
152 Err(e)
154 }
155 } else {
156 Err(e)
157 }
158 }
159 }
160 }
161
162 fn map_codebuild_error(
163 status: StatusCode,
164 body: &str,
165 _operation: &str,
166 resource: &str,
167 request_body: Option<&str>,
168 ) -> Option<ErrorData> {
169 let parsed: std::result::Result<CodeBuildErrorResponse, _> = serde_json::from_str(body);
170 let (code, message) = match parsed {
171 Ok(e) => {
172 let c = e.type_field.unwrap_or_else(|| "UnknownErrorCode".into());
173 let m = e.message.unwrap_or_else(|| "Unknown error".into());
174 (c, m)
175 }
176 Err(_) => {
177 return None;
179 }
180 };
181
182 Some(match code.as_str() {
183 "AccessDeniedException" => ErrorData::RemoteAccessDenied {
184 resource_type: "Project".into(),
185 resource_name: resource.into(),
186 },
187 "AccountLimitExceededException" => ErrorData::QuotaExceeded { message },
188 "ThrottlingException" | "TooManyRequestsException" => {
189 ErrorData::RateLimitExceeded { message }
190 }
191 "ServiceUnavailable" | "InternalFailure" | "ServiceException" => {
192 ErrorData::RemoteServiceUnavailable { message }
193 }
194 "RequestTimeoutException" => ErrorData::Timeout { message },
195 "ResourceNotFoundException" => ErrorData::RemoteResourceNotFound {
196 resource_type: "Project".into(),
197 resource_name: resource.into(),
198 },
199 "ResourceAlreadyExistsException" => ErrorData::RemoteResourceConflict {
200 message,
201 resource_type: "Project".into(),
202 resource_name: resource.into(),
203 },
204 "InvalidInputException" | "NotAuthorized" | "ValidationError" => {
205 if message.contains("sts:AssumeRole") || message.contains("not authorized") {
209 ErrorData::RemoteServiceUnavailable { message }
210 } else {
211 ErrorData::InvalidInput {
212 message,
213 field_name: None,
214 }
215 }
216 }
217 _ => match status {
218 StatusCode::NOT_FOUND => ErrorData::RemoteResourceNotFound {
219 resource_type: "Project".into(),
220 resource_name: resource.into(),
221 },
222 StatusCode::CONFLICT => ErrorData::RemoteResourceConflict {
223 message,
224 resource_type: "Project".into(),
225 resource_name: resource.into(),
226 },
227 StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED => ErrorData::RemoteAccessDenied {
228 resource_type: "Project".into(),
229 resource_name: resource.into(),
230 },
231 StatusCode::TOO_MANY_REQUESTS => ErrorData::RateLimitExceeded { message },
232 StatusCode::SERVICE_UNAVAILABLE
233 | StatusCode::BAD_GATEWAY
234 | StatusCode::GATEWAY_TIMEOUT => ErrorData::RemoteServiceUnavailable { message },
235 _ => ErrorData::HttpResponseError {
236 message: format!("CodeBuild operation failed: {}", message),
237 url: format!("codebuild.amazonaws.com"),
238 http_status: status.as_u16(),
239 http_response_text: Some(body.into()),
240 http_request_text: request_body.map(|s| s.to_string()),
241 },
242 },
243 })
244 }
245}
246
247#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
248#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
249impl CodeBuildApi for CodeBuildClient {
250 async fn create_project(&self, request: CreateProjectRequest) -> Result<CreateProjectResponse> {
251 let body = serde_json::to_string(&request).into_alien_error().context(
252 ErrorData::SerializationError {
253 message: format!(
254 "Failed to serialize CreateProjectRequest for project '{}'",
255 request.name
256 ),
257 },
258 )?;
259 self.post_json("CreateProject", body, &request.name).await
260 }
261
262 async fn delete_project(&self, request: DeleteProjectRequest) -> Result<DeleteProjectResponse> {
263 let body = serde_json::to_string(&request).into_alien_error().context(
264 ErrorData::SerializationError {
265 message: format!(
266 "Failed to serialize DeleteProjectRequest for project '{}'",
267 request.name
268 ),
269 },
270 )?;
271 self.post_empty_response("DeleteProject", body, &request.name)
272 .await?;
273 Ok(DeleteProjectResponse {})
274 }
275
276 async fn update_project(&self, request: UpdateProjectRequest) -> Result<UpdateProjectResponse> {
277 let body = serde_json::to_string(&request).into_alien_error().context(
278 ErrorData::SerializationError {
279 message: format!(
280 "Failed to serialize UpdateProjectRequest for project '{}'",
281 request.name
282 ),
283 },
284 )?;
285 self.post_json("UpdateProject", body, &request.name).await
286 }
287
288 async fn batch_get_projects(
289 &self,
290 request: BatchGetProjectsRequest,
291 ) -> Result<BatchGetProjectsResponse> {
292 let body = serde_json::to_string(&request).into_alien_error().context(
293 ErrorData::SerializationError {
294 message: format!("Failed to serialize BatchGetProjectsRequest"),
295 },
296 )?;
297 self.post_json("BatchGetProjects", body, "").await
298 }
299
300 async fn start_build(&self, request: StartBuildRequest) -> Result<StartBuildResponse> {
301 let body = serde_json::to_string(&request).into_alien_error().context(
302 ErrorData::SerializationError {
303 message: format!(
304 "Failed to serialize StartBuildRequest for project '{}'",
305 request.project_name
306 ),
307 },
308 )?;
309 self.post_json("StartBuild", body, &request.project_name)
310 .await
311 }
312
313 async fn stop_build(&self, request: StopBuildRequest) -> Result<StopBuildResponse> {
314 let body = serde_json::to_string(&request).into_alien_error().context(
315 ErrorData::SerializationError {
316 message: format!(
317 "Failed to serialize StopBuildRequest for build '{}'",
318 request.id
319 ),
320 },
321 )?;
322 self.post_json("StopBuild", body, &request.id).await
323 }
324
325 async fn batch_get_builds(
326 &self,
327 request: BatchGetBuildsRequest,
328 ) -> Result<BatchGetBuildsResponse> {
329 let body = serde_json::to_string(&request).into_alien_error().context(
330 ErrorData::SerializationError {
331 message: format!("Failed to serialize BatchGetBuildsRequest"),
332 },
333 )?;
334 self.post_json("BatchGetBuilds", body, "").await
335 }
336
337 async fn batch_delete_builds(
338 &self,
339 request: BatchDeleteBuildsRequest,
340 ) -> Result<BatchDeleteBuildsResponse> {
341 let body = serde_json::to_string(&request).into_alien_error().context(
342 ErrorData::SerializationError {
343 message: format!("Failed to serialize BatchDeleteBuildsRequest"),
344 },
345 )?;
346 self.post_json("BatchDeleteBuilds", body, "").await
347 }
348
349 async fn retry_build(&self, request: RetryBuildRequest) -> Result<RetryBuildResponse> {
350 let body = serde_json::to_string(&request).into_alien_error().context(
351 ErrorData::SerializationError {
352 message: format!("Failed to serialize RetryBuildRequest"),
353 },
354 )?;
355 self.post_json("RetryBuild", body, &request.id.unwrap_or_default())
356 .await
357 }
358}
359
360#[derive(Debug, Deserialize)]
365struct CodeBuildErrorResponse {
366 #[serde(rename = "__type")]
367 type_field: Option<String>,
368 #[serde(rename = "message")]
369 message: Option<String>,
370}
371
372#[derive(Debug, Clone, Serialize, Builder)]
373#[serde(rename_all = "camelCase")]
374pub struct CreateProjectRequest {
375 pub name: String,
376 pub source: ProjectSource,
377 pub artifacts: ProjectArtifacts,
378 pub environment: ProjectEnvironment,
379 pub service_role: String,
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub description: Option<String>,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub logs_config: Option<LogsConfig>,
384 #[serde(skip_serializing_if = "Option::is_none")]
385 pub tags: Option<Vec<Tag>>,
386}
387
388#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
389#[serde(rename_all = "camelCase")]
390pub struct ProjectSource {
391 pub r#type: String,
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub location: Option<String>,
394 #[serde(skip_serializing_if = "Option::is_none")]
395 pub buildspec: Option<String>,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 pub git_clone_depth: Option<i32>,
398 #[serde(skip_serializing_if = "Option::is_none")]
399 pub insecure_ssl: Option<bool>,
400}
401
402#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
403#[serde(rename_all = "camelCase")]
404pub struct ProjectArtifacts {
405 pub r#type: String,
406 #[serde(skip_serializing_if = "Option::is_none")]
407 pub location: Option<String>,
408 #[serde(skip_serializing_if = "Option::is_none")]
409 pub path: Option<String>,
410 #[serde(skip_serializing_if = "Option::is_none")]
411 pub namespace_type: Option<String>,
412 #[serde(skip_serializing_if = "Option::is_none")]
413 pub name: Option<String>,
414 #[serde(skip_serializing_if = "Option::is_none")]
415 pub packaging: Option<String>,
416 #[serde(skip_serializing_if = "Option::is_none")]
417 pub encryption_disabled: Option<bool>,
418 #[serde(skip_serializing_if = "Option::is_none")]
419 pub artifact_identifier: Option<String>,
420 #[serde(skip_serializing_if = "Option::is_none")]
421 pub bucket_owner_access: Option<String>,
422}
423
424#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
425#[serde(rename_all = "camelCase")]
426pub struct ProjectEnvironment {
427 pub r#type: String,
428 pub image: String,
429 pub compute_type: String,
430 #[serde(skip_serializing_if = "Option::is_none")]
431 pub environment_variables: Option<Vec<EnvironmentVariable>>,
432 #[serde(skip_serializing_if = "Option::is_none")]
433 pub privileged_mode: Option<bool>,
434 #[serde(skip_serializing_if = "Option::is_none")]
435 pub image_pull_credentials_type: Option<String>,
436}
437
438#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
439#[serde(rename_all = "camelCase")]
440pub struct EnvironmentVariable {
441 pub name: String,
442 pub value: String,
443 pub r#type: Option<String>,
444}
445
446#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
447#[serde(rename_all = "camelCase")]
448pub struct Tag {
449 pub key: String,
450 pub value: String,
451}
452
453#[derive(Debug, Deserialize)]
454#[serde(rename_all = "camelCase")]
455pub struct CreateProjectResponse {
456 pub project: Project,
457}
458
459#[derive(Debug, Deserialize, Clone, bon::Builder)]
460#[serde(rename_all = "camelCase")]
461pub struct Project {
462 pub name: Option<String>,
463 pub arn: Option<String>,
464 #[serde(skip_serializing_if = "Option::is_none")]
465 pub description: Option<String>,
466 #[serde(skip_serializing_if = "Option::is_none")]
467 pub source: Option<ProjectSource>,
468 #[serde(skip_serializing_if = "Option::is_none")]
469 pub artifacts: Option<ProjectArtifacts>,
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub environment: Option<ProjectEnvironment>,
472 #[serde(skip_serializing_if = "Option::is_none")]
473 pub service_role: Option<String>,
474 #[serde(skip_serializing_if = "Option::is_none")]
475 pub tags: Option<Vec<Tag>>,
476 #[serde(skip_serializing_if = "Option::is_none")]
477 pub vpc_config: Option<VpcConfig>,
478 #[serde(skip_serializing_if = "Option::is_none")]
479 pub webhook: Option<Webhook>,
480 #[serde(skip_serializing_if = "Option::is_none")]
481 pub logs_config: Option<LogsConfig>,
482 #[serde(skip_serializing_if = "Option::is_none")]
483 pub cache: Option<ProjectCache>,
484 #[serde(skip_serializing_if = "Option::is_none")]
485 pub timeout_in_minutes: Option<i32>,
486 #[serde(skip_serializing_if = "Option::is_none")]
487 pub queued_timeout_in_minutes: Option<i32>,
488 #[serde(skip_serializing_if = "Option::is_none")]
489 pub encryption_key: Option<String>,
490 #[serde(skip_serializing_if = "Option::is_none")]
491 pub secondary_sources: Option<Vec<ProjectSource>>,
492 #[serde(skip_serializing_if = "Option::is_none")]
493 pub secondary_artifacts: Option<Vec<ProjectArtifacts>>,
494 #[serde(skip_serializing_if = "Option::is_none")]
495 pub created: Option<f64>,
496 #[serde(skip_serializing_if = "Option::is_none")]
497 pub last_modified: Option<f64>,
498}
499
500#[derive(Debug, Clone, Serialize, Builder)]
501#[serde(rename_all = "camelCase")]
502pub struct DeleteProjectRequest {
503 pub name: String,
504}
505
506#[derive(Debug, Deserialize)]
507pub struct DeleteProjectResponse {}
508
509#[derive(Debug, Clone, Serialize, Builder)]
510#[serde(rename_all = "camelCase")]
511pub struct UpdateProjectRequest {
512 pub name: String,
513 #[serde(skip_serializing_if = "Option::is_none")]
514 pub description: Option<String>,
515 #[serde(skip_serializing_if = "Option::is_none")]
516 pub source: Option<ProjectSource>,
517 #[serde(skip_serializing_if = "Option::is_none")]
518 pub artifacts: Option<ProjectArtifacts>,
519 #[serde(skip_serializing_if = "Option::is_none")]
520 pub environment: Option<ProjectEnvironment>,
521 #[serde(skip_serializing_if = "Option::is_none")]
522 pub service_role: Option<String>,
523 #[serde(skip_serializing_if = "Option::is_none")]
524 pub tags: Option<Vec<Tag>>,
525 #[serde(skip_serializing_if = "Option::is_none")]
526 pub vpc_config: Option<VpcConfig>,
527 #[serde(skip_serializing_if = "Option::is_none")]
528 pub webhook: Option<Webhook>,
529 #[serde(skip_serializing_if = "Option::is_none")]
530 pub logs_config: Option<LogsConfig>,
531 #[serde(skip_serializing_if = "Option::is_none")]
532 pub cache: Option<ProjectCache>,
533 #[serde(skip_serializing_if = "Option::is_none")]
534 pub timeout_in_minutes: Option<i32>,
535 #[serde(skip_serializing_if = "Option::is_none")]
536 pub queued_timeout_in_minutes: Option<i32>,
537 #[serde(skip_serializing_if = "Option::is_none")]
538 pub encryption_key: Option<String>,
539}
540
541#[derive(Debug, Deserialize)]
542#[serde(rename_all = "camelCase")]
543pub struct UpdateProjectResponse {
544 pub project: Project,
545}
546
547#[derive(Debug, Clone, Serialize, Builder)]
548#[serde(rename_all = "camelCase")]
549pub struct StartBuildRequest {
550 pub project_name: String,
551 #[serde(skip_serializing_if = "Option::is_none")]
552 pub source_version: Option<String>,
553 #[serde(skip_serializing_if = "Option::is_none")]
554 pub buildspec_override: Option<String>,
555 #[serde(skip_serializing_if = "Option::is_none")]
556 pub environment_variables_override: Option<Vec<EnvironmentVariable>>,
557 #[serde(skip_serializing_if = "Option::is_none")]
558 pub compute_type_override: Option<String>,
559 #[serde(skip_serializing_if = "Option::is_none")]
560 pub environment_type_override: Option<String>,
561 #[serde(skip_serializing_if = "Option::is_none")]
562 pub image_override: Option<String>,
563 #[serde(skip_serializing_if = "Option::is_none")]
564 pub timeout_in_minutes_override: Option<i32>,
565}
566
567#[derive(Debug, Deserialize)]
568#[serde(rename_all = "camelCase")]
569pub struct StartBuildResponse {
570 pub build: Build,
571}
572
573#[derive(Debug, Deserialize, Clone)]
574#[serde(rename_all = "camelCase")]
575pub struct Build {
576 pub id: Option<String>,
577 pub arn: Option<String>,
578 pub build_number: Option<i64>,
579 pub build_status: Option<String>,
580 pub start_time: Option<f64>,
581 pub end_time: Option<f64>,
582 pub current_phase: Option<String>,
583 pub build_complete: bool,
584 pub initiator: Option<String>,
585 pub source_version: Option<String>,
586 pub project_name: Option<String>,
587 pub phases: Option<Vec<BuildPhase>>,
588 pub source: Option<ProjectSource>,
589 pub secondary_sources: Option<Vec<ProjectSource>>,
590 pub secondary_artifacts: Option<Vec<BuildArtifacts>>,
591 pub artifacts: Option<BuildArtifacts>,
592 pub logs: Option<LogsLocation>,
593 pub vpc_config: Option<VpcConfig>,
594 pub environment: Option<ProjectEnvironment>,
595}
596
597#[derive(Debug, Clone, Serialize, Builder)]
598#[serde(rename_all = "camelCase")]
599pub struct StopBuildRequest {
600 pub id: String,
601}
602
603#[derive(Debug, Deserialize, Clone)]
604#[serde(rename_all = "camelCase")]
605pub struct StopBuildResponse {
606 pub build: Build,
607}
608
609#[derive(Debug, Clone, Serialize, Builder)]
610#[serde(rename_all = "camelCase")]
611pub struct BatchGetBuildsRequest {
612 pub ids: Vec<String>,
613}
614
615#[derive(Debug, Deserialize)]
616#[serde(rename_all = "camelCase")]
617pub struct BatchGetBuildsResponse {
618 pub builds: Option<Vec<Build>>,
619 pub builds_not_found: Option<Vec<String>>,
620}
621
622#[derive(Debug, Clone, Serialize, Builder)]
623#[serde(rename_all = "camelCase")]
624pub struct BatchDeleteBuildsRequest {
625 pub ids: Vec<String>,
626}
627
628#[derive(Debug, Deserialize)]
629#[serde(rename_all = "camelCase")]
630pub struct BatchDeleteBuildsResponse {
631 pub builds_deleted: Option<Vec<String>>,
632 pub builds_not_deleted: Option<Vec<BuildNotDeleted>>,
633}
634
635#[derive(Debug, Deserialize)]
636#[serde(rename_all = "camelCase")]
637pub struct BuildNotDeleted {
638 pub id: Option<String>,
639 pub status_code: Option<String>,
640}
641
642#[derive(Debug, Clone, Serialize, Builder)]
643#[serde(rename_all = "camelCase")]
644pub struct RetryBuildRequest {
645 #[serde(skip_serializing_if = "Option::is_none")]
646 pub id: Option<String>,
647 #[serde(skip_serializing_if = "Option::is_none")]
648 pub idempotency_token: Option<String>,
649}
650
651#[derive(Debug, Deserialize)]
652#[serde(rename_all = "camelCase")]
653pub struct RetryBuildResponse {
654 pub build: Build,
655}
656
657#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
658#[serde(rename_all = "camelCase")]
659pub struct VpcConfig {
660 #[serde(skip_serializing_if = "Option::is_none")]
661 pub vpc_id: Option<String>,
662 #[serde(skip_serializing_if = "Option::is_none")]
663 pub subnets: Option<Vec<String>>,
664 #[serde(skip_serializing_if = "Option::is_none")]
665 pub security_group_ids: Option<Vec<String>>,
666}
667
668#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
669#[serde(rename_all = "camelCase")]
670pub struct Webhook {
671 #[serde(skip_serializing_if = "Option::is_none")]
672 pub url: Option<String>,
673 #[serde(skip_serializing_if = "Option::is_none")]
674 pub payload_url: Option<String>,
675 #[serde(skip_serializing_if = "Option::is_none")]
676 pub secret: Option<String>,
677 #[serde(skip_serializing_if = "Option::is_none")]
678 pub branch_filter: Option<String>,
679 #[serde(skip_serializing_if = "Option::is_none")]
680 pub filter_groups: Option<Vec<Vec<WebhookFilter>>>,
681 #[serde(skip_serializing_if = "Option::is_none")]
682 pub build_type: Option<String>,
683}
684
685#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
686#[serde(rename_all = "camelCase")]
687pub struct WebhookFilter {
688 pub r#type: String,
689 pub pattern: String,
690 #[serde(skip_serializing_if = "Option::is_none")]
691 pub exclude_matched_pattern: Option<bool>,
692}
693
694#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
695#[serde(rename_all = "camelCase")]
696pub struct LogsConfig {
697 #[serde(skip_serializing_if = "Option::is_none")]
698 pub cloud_watch_logs: Option<CloudWatchLogsConfig>,
699 #[serde(skip_serializing_if = "Option::is_none")]
700 pub s3_logs: Option<S3LogsConfig>,
701}
702
703#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
704#[serde(rename_all = "camelCase")]
705pub struct CloudWatchLogsConfig {
706 pub status: String,
707 #[serde(skip_serializing_if = "Option::is_none")]
708 pub group_name: Option<String>,
709 #[serde(skip_serializing_if = "Option::is_none")]
710 pub stream_name: Option<String>,
711}
712
713#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
714#[serde(rename_all = "camelCase")]
715pub struct S3LogsConfig {
716 pub status: String,
717 #[serde(skip_serializing_if = "Option::is_none")]
718 pub location: Option<String>,
719 #[serde(skip_serializing_if = "Option::is_none")]
720 pub encryption_disabled: Option<bool>,
721 #[serde(skip_serializing_if = "Option::is_none")]
722 pub bucket_owner_access: Option<String>,
723}
724
725#[derive(Debug, Clone, Serialize, Builder, Deserialize)]
726#[serde(rename_all = "camelCase")]
727pub struct ProjectCache {
728 pub r#type: String,
729 #[serde(skip_serializing_if = "Option::is_none")]
730 pub location: Option<String>,
731 #[serde(skip_serializing_if = "Option::is_none")]
732 pub modes: Option<Vec<String>>,
733}
734
735#[derive(Debug, Deserialize, Clone)]
736#[serde(rename_all = "camelCase")]
737pub struct BuildPhase {
738 pub phase_type: Option<String>,
739 pub phase_status: Option<String>,
740 pub start_time: Option<f64>,
741 pub end_time: Option<f64>,
742 pub duration_in_seconds: Option<i64>,
743}
744
745#[derive(Debug, Deserialize, Clone)]
746#[serde(rename_all = "camelCase")]
747pub struct BuildArtifacts {
748 #[serde(skip_serializing_if = "Option::is_none")]
749 pub location: Option<String>,
750 #[serde(skip_serializing_if = "Option::is_none")]
751 pub sha256sum: Option<String>,
752 #[serde(skip_serializing_if = "Option::is_none")]
753 pub md5sum: Option<String>,
754 #[serde(skip_serializing_if = "Option::is_none")]
755 pub override_artifact_name: Option<bool>,
756 #[serde(skip_serializing_if = "Option::is_none")]
757 pub encryption_disabled: Option<bool>,
758 #[serde(skip_serializing_if = "Option::is_none")]
759 pub artifact_identifier: Option<String>,
760}
761
762#[derive(Debug, Deserialize, Clone)]
763#[serde(rename_all = "camelCase")]
764pub struct LogsLocation {
765 #[serde(skip_serializing_if = "Option::is_none")]
766 pub group_name: Option<String>,
767 #[serde(skip_serializing_if = "Option::is_none")]
768 pub stream_name: Option<String>,
769 #[serde(skip_serializing_if = "Option::is_none")]
770 pub deep_link: Option<String>,
771}
772
773#[derive(Debug, Clone, Serialize, Builder)]
774#[serde(rename_all = "camelCase")]
775pub struct BatchGetProjectsRequest {
776 pub names: Vec<String>,
777}
778
779#[derive(Debug, Deserialize)]
780#[serde(rename_all = "camelCase")]
781pub struct BatchGetProjectsResponse {
782 #[serde(skip_serializing_if = "Option::is_none")]
783 pub projects: Option<Vec<Project>>,
784 #[serde(skip_serializing_if = "Option::is_none")]
785 pub projects_not_found: Option<Vec<String>>,
786}