Skip to main content

zixcel_github/
execution.rs

1use crate::boundary::{digest, github_name, identifier, reference};
2use crate::{
3    API_RECEIPT_SCHEMA, API_REQUEST_SCHEMA, ConnectorError, GitHubApiReceipt, GitHubApiRequest,
4    GitHubBackend, GitHubBackendReceipt, RequestValidationReport,
5};
6
7const MAX_DOCUMENT_BYTES: usize = 1_048_576;
8
9impl GitHubApiRequest {
10    pub fn validate(&self) -> Result<(), ConnectorError> {
11        if self.schema != API_REQUEST_SCHEMA {
12            return Err(ConnectorError::new(
13                "schema",
14                "unexpected action request schema",
15            ));
16        }
17        identifier("request_id", &self.request_id)?;
18        identifier("operation_id", &self.operation_id)?;
19        reference("connection_ref", &self.connection_ref)?;
20        github_name("owner", &self.owner)?;
21        github_name("repository", &self.repository)?;
22        self.action.validate()
23    }
24}
25
26pub fn parse_api_request_json(source: &[u8]) -> Result<GitHubApiRequest, ConnectorError> {
27    if source.len() > MAX_DOCUMENT_BYTES {
28        return Err(ConnectorError::new("request", "document exceeds 1 MiB"));
29    }
30    let value: GitHubApiRequest = serde_json::from_slice(source)
31        .map_err(|_| ConnectorError::new("request", "invalid closed JSON"))?;
32    value.validate()?;
33    Ok(value)
34}
35
36pub fn validate_api_request(
37    request: &GitHubApiRequest,
38) -> Result<RequestValidationReport, ConnectorError> {
39    request.validate()?;
40    let required = request.action.required_permission();
41    Ok(RequestValidationReport {
42        schema: "zixcel://github/request-validation-result/v1",
43        request_id: request.request_id.clone(),
44        operation_id: request.operation_id.clone(),
45        valid: true,
46        required_permission: required,
47    })
48}
49
50pub fn execute_api_request<B: GitHubBackend>(
51    request: &GitHubApiRequest,
52    backend: &mut B,
53) -> Result<GitHubApiReceipt, ConnectorError> {
54    let report = validate_api_request(request)?;
55    let value = backend.perform(request)?;
56    validate_backend_receipt(&value)?;
57    Ok(GitHubApiReceipt {
58        schema: API_RECEIPT_SCHEMA,
59        request_id: request.request_id.clone(),
60        operation_id: request.operation_id.clone(),
61        owner: request.owner.clone(),
62        repository: request.repository.clone(),
63        permission: report.required_permission,
64        provider_request_id: value.provider_request_id,
65        remote_reference: value.remote_reference,
66        remote_digest_sha256: value.remote_digest_sha256,
67    })
68}
69
70fn validate_backend_receipt(value: &GitHubBackendReceipt) -> Result<(), ConnectorError> {
71    identifier("provider_request_id", &value.provider_request_id)?;
72    reference("remote_reference", &value.remote_reference)?;
73    if let Some(value) = &value.remote_digest_sha256 {
74        digest("remote_digest_sha256", value)?;
75    }
76    Ok(())
77}
78
79/// Execute only after the explicit local connection/target/action ceiling passes.
80pub fn execute_configured_request<B: GitHubBackend>(
81    config: &crate::ConnectorConfig,
82    request: &GitHubApiRequest,
83    backend: &mut B,
84) -> Result<GitHubApiReceipt, ConnectorError> {
85    config.authorize_request(request)?;
86    execute_api_request(request, backend)
87}