|
| 1 | +// Copyright Kamu Data, Inc. and contributors. All rights reserved. |
| 2 | +// |
| 3 | +// Use of this software is governed by the Business Source License |
| 4 | +// included in the LICENSE file. |
| 5 | +// |
| 6 | +// As of the Change Date specified in that file, in accordance with |
| 7 | +// the Business Source License, use of this software will be governed |
| 8 | +// by the Apache License, Version 2.0. |
| 9 | + |
| 10 | +use axum::Extension; |
| 11 | +use axum::response::IntoResponse; |
| 12 | +use database_common_macros::transactional_handler; |
| 13 | +use dill::Catalog; |
| 14 | +use internal_error::InternalError; |
| 15 | +use kamu_adapter_graphql::data_loader::{account_entity_data_loader, dataset_handle_data_loader}; |
| 16 | + |
| 17 | +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 18 | + |
| 19 | +/// An error type that wraps a GraphQL response containing errors. |
| 20 | +/// When returned as `Err`, it triggers transaction rollback while still |
| 21 | +/// returning the GraphQL response (with errors) to the client. |
| 22 | +pub struct GqlResponseError(async_graphql_axum::GraphQLResponse); |
| 23 | + |
| 24 | +impl std::fmt::Display for GqlResponseError { |
| 25 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 26 | + write!(f, "GraphQL response contains errors") |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +impl std::fmt::Debug for GqlResponseError { |
| 31 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 32 | + write!(f, "GqlResponseError") |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +impl std::error::Error for GqlResponseError {} |
| 37 | + |
| 38 | +impl From<InternalError> for GqlResponseError { |
| 39 | + fn from(e: InternalError) -> Self { |
| 40 | + let response = async_graphql::Response::from_errors(vec![async_graphql::ServerError::new( |
| 41 | + e.to_string(), |
| 42 | + None, |
| 43 | + )]); |
| 44 | + GqlResponseError(response.into()) |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +impl IntoResponse for GqlResponseError { |
| 49 | + fn into_response(self) -> axum::response::Response { |
| 50 | + self.0.into_response() |
| 51 | + } |
| 52 | +} |
| 53 | + |
| 54 | +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 55 | + |
| 56 | +#[transactional_handler] |
| 57 | +pub async fn graphql_handler( |
| 58 | + Extension(schema): Extension<kamu_adapter_graphql::Schema>, |
| 59 | + Extension(catalog): Extension<Catalog>, |
| 60 | + req: async_graphql_axum::GraphQLRequest, |
| 61 | +) -> Result<async_graphql_axum::GraphQLResponse, GqlResponseError> { |
| 62 | + let graphql_request = req |
| 63 | + .into_inner() |
| 64 | + .data(account_entity_data_loader(&catalog)) |
| 65 | + .data(dataset_handle_data_loader(&catalog)) |
| 66 | + .data(catalog); |
| 67 | + let graphql_response: async_graphql_axum::GraphQLResponse = |
| 68 | + schema.execute(graphql_request).await.into(); |
| 69 | + |
| 70 | + // Check if the response contains errors - if so, return Err to trigger |
| 71 | + // transaction rollback while still returning the GraphQL response to the client |
| 72 | + if !graphql_response.0.is_ok() { |
| 73 | + Err(GqlResponseError(graphql_response)) |
| 74 | + } else { |
| 75 | + Ok(graphql_response) |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
0 commit comments