Defer local relationship check until IR creation (#716)

<!-- Thank you for submitting this PR! :) -->

## Description

Making the schema work for boolean expressions has been challenging as
they are no longer tied to a single data connector, so we cannot do any
checks of whether a relationships is local or remote. We defer this to
the IR step when the source data connector for a relationship is known,
so that we can generate schema for boolean expressions decoupled from
any concept of data connector.

Functional no-op.

V3_GIT_ORIGIN_REV_ID: d2923acedf92b031ba092bb83c515812c4d346f0
This commit is contained in:
Daniel Harvey 2024-06-13 16:25:39 +01:00 committed by hasura-bot
parent e8edc5e596
commit 0f245de566
2 changed files with 117 additions and 114 deletions

View File

@ -201,49 +201,72 @@ fn build_filter_expression_from_boolean_expression<'s>(
// Add the target model being used in the usage counts
count_model(target_model_name, usage_counts);
let ndc_relationship_name = NDCRelationshipName::new(source_type, relationship_name)?;
relationships.insert(
ndc_relationship_name.clone(),
LocalModelRelationshipInfo {
relationship_name,
relationship_type,
source_type,
source_data_connector: data_connector_link,
source_type_mappings: type_mappings,
target_source,
target_type,
mappings,
},
);
// filter expression with relationships is currently only supported for local relationships
// this is the first point at which we know the source data connector, so we must
// ensure only a local relationship is used
match metadata_resolve::relationship_execution_category(
data_connector_link,
&target_source.model.data_connector,
&target_source.capabilities,
) {
metadata_resolve::RelationshipExecutionCategory::RemoteForEach => {
Err(error::Error::Internal(error::InternalError::Engine(
error::InternalEngineError::InternalGeneric {
description:
"Remote relationships are not supported in boolean expressions"
.to_string(),
},
)))
}
// This map contains the relationships or the columns of the
// relationship that needs to be used for ordering.
let filter_object = field.value.as_object()?;
metadata_resolve::RelationshipExecutionCategory::Local => {
let ndc_relationship_name =
NDCRelationshipName::new(source_type, relationship_name)?;
let mut expressions = Vec::new();
relationships.insert(
ndc_relationship_name.clone(),
LocalModelRelationshipInfo {
relationship_name,
relationship_type,
source_type,
source_data_connector: data_connector_link,
source_type_mappings: type_mappings,
target_source,
target_type,
mappings,
},
);
for field in filter_object.values() {
let field_filter_expression = build_filter_expression(
field,
relationships,
&target_source.model.data_connector,
&target_source.model.type_mappings,
usage_counts,
)?;
expressions.push(field_filter_expression);
// This map contains the relationships or the columns of the
// relationship that needs to be used for ordering.
let filter_object = field.value.as_object()?;
let mut expressions = Vec::new();
for field in filter_object.values() {
let field_filter_expression = build_filter_expression(
field,
relationships,
&target_source.model.data_connector,
&target_source.model.type_mappings,
usage_counts,
)?;
expressions.push(field_filter_expression);
}
// Using exists clause to build the filter expression for relationship fields.
let exists_filter_clause = ndc_models::Expression::And { expressions };
let exists_in_relationship = ndc_models::ExistsInCollection::Related {
relationship: ndc_relationship_name.0,
arguments: BTreeMap::new(),
};
Ok(ndc_models::Expression::Exists {
in_collection: exists_in_relationship,
predicate: Some(Box::new(exists_filter_clause)),
})
}
}
// Using exists clause to build the filter expression for relationship fields.
let exists_filter_clause = ndc_models::Expression::And { expressions };
let exists_in_relationship = ndc_models::ExistsInCollection::Related {
relationship: ndc_relationship_name.0,
arguments: BTreeMap::new(),
};
Ok(ndc_models::Expression::Exists {
in_collection: exists_in_relationship,
predicate: Some(Box::new(exists_filter_clause)),
})
}
other_boolean_annotation => Err(error::InternalEngineError::UnexpectedAnnotation {
annotation: schema::Annotation::Input(InputAnnotation::BooleanExpression(

View File

@ -9,8 +9,8 @@ use super::types::output_type::relationship::FilterRelationshipAnnotation;
use super::types::{BooleanExpressionAnnotation, InputAnnotation, TypeId};
use metadata_resolve::{
mk_name, BooleanExpressionGraphqlConfig, ModelExpressionType, ModelWithPermissions,
ObjectBooleanExpressionDataConnector, ObjectBooleanExpressionType, ObjectTypeWithRelationships,
Qualified, Relationship, RelationshipModelMapping, ResolvedObjectBooleanExpressionType,
ObjectBooleanExpressionType, ObjectTypeWithRelationships, Qualified, Relationship,
RelationshipModelMapping, ResolvedObjectBooleanExpressionType,
};
use crate::permissions;
@ -187,7 +187,6 @@ fn build_comparable_fields_schema(
fn build_comparable_relationships_schema(
gds: &GDS,
object_type_representation: &ObjectTypeWithRelationships,
object_boolean_expression_data_connector: &Option<ObjectBooleanExpressionDataConnector>,
builder: &mut gql_schema::Builder<GDS>,
) -> Result<BTreeMap<ast::Name, gql_schema::Namespaced<GDS, gql_schema::InputField<GDS>>>, Error> {
let mut input_fields = BTreeMap::new();
@ -214,7 +213,6 @@ fn build_comparable_relationships_schema(
// try and create a new input field
if let Some((name, schema)) = build_model_relationship_schema(
object_type_representation,
object_boolean_expression_data_connector,
target_object_type_representation,
target_model,
relationship,
@ -238,7 +236,6 @@ type InputField = (
// build comparable relationships input fields
fn build_model_relationship_schema(
source_object_type_representation: &ObjectTypeWithRelationships,
source_object_boolean_expression_data_connector: &Option<ObjectBooleanExpressionDataConnector>,
target_object_type_representation: &ObjectTypeWithRelationships,
target_model: &ModelWithPermissions,
relationship: &Relationship,
@ -251,82 +248,66 @@ fn build_model_relationship_schema(
// both the source boolean expression and target_model are backed by a source
// We'll need to find a way of getting this information for BooleanExpressionTypes
// that are uncoupled from their data connector source
if let (Some(source_data_connector), Some(target_source)) = (
&source_object_boolean_expression_data_connector,
&target_model.model.source,
) {
if let Some(target_source) = &target_model.model.source {
let target_model_source =
metadata_resolve::ModelTargetSource::from_model_source(target_source, relationship)?;
// filter expression with relationships is currently only supported for local relationships
if let metadata_resolve::RelationshipExecutionCategory::Local =
metadata_resolve::relationship_execution_category(
&source_data_connector.link,
&target_source.data_connector,
&target_model_source.capabilities,
)
{
if target_source.data_connector.name == source_data_connector.name {
match &target_model.model.filter_expression_type {
None => {}
Some(ModelExpressionType::BooleanExpressionType(_)) => {
todo!("relationships with BooleanExpressionType")
}
Some(ModelExpressionType::ObjectBooleanExpressionType(
target_model_filter_expression,
)) => {
// If the relationship target model does not have filterExpressionType do not include
// it in the source model filter expression input type.
if let Some(ref target_model_filter_expression_graphql) =
&target_model_filter_expression.graphql
{
let annotation = FilterRelationshipAnnotation {
source_type: relationship.source.clone(),
relationship_name: relationship.name.clone(),
target_source: target_model_source.clone(),
target_type: target_model.model.data_type.clone(),
target_model_name: target_model.model.name.clone(),
relationship_type: relationship_type.clone(),
mappings: relationship_model_mappings.to_vec(),
};
match &target_model.model.filter_expression_type {
None => {}
Some(ModelExpressionType::BooleanExpressionType(_)) => {
todo!("relationships with BooleanExpressionType")
}
Some(ModelExpressionType::ObjectBooleanExpressionType(
target_model_filter_expression,
)) => {
// If the relationship target model does not have filterExpressionType do not include
// it in the source model filter expression input type.
if let Some(ref target_model_filter_expression_graphql) =
&target_model_filter_expression.graphql
{
let annotation = FilterRelationshipAnnotation {
source_type: relationship.source.clone(),
relationship_name: relationship.name.clone(),
target_source: target_model_source.clone(),
target_type: target_model.model.data_type.clone(),
target_model_name: target_model.model.name.clone(),
relationship_type: relationship_type.clone(),
mappings: relationship_model_mappings.to_vec(),
};
let namespace_annotations =
permissions::get_model_relationship_namespace_annotations(
target_model,
source_object_type_representation,
target_object_type_representation,
relationship_model_mappings,
&gds.metadata.object_types,
)?;
let namespace_annotations =
permissions::get_model_relationship_namespace_annotations(
target_model,
source_object_type_representation,
target_object_type_representation,
relationship_model_mappings,
&gds.metadata.object_types,
)?;
return Ok(Some((
relationship.field_name.clone(),
builder.conditional_namespaced(
gql_schema::InputField::<GDS>::new(
relationship.field_name.clone(),
None,
types::Annotation::Input(InputAnnotation::BooleanExpression(
BooleanExpressionAnnotation
::BooleanExpressionArgument {
field:
types::ModelFilterArgument::RelationshipField(
annotation,
),
},
)),
ast::TypeContainer::named_null(
gql_schema::RegisteredTypeName::new(
target_model_filter_expression_graphql.type_name.0.clone(),
),
),
None,
gql_schema::DeprecationStatus::NotDeprecated,
return Ok(Some((
relationship.field_name.clone(),
builder.conditional_namespaced(
gql_schema::InputField::<GDS>::new(
relationship.field_name.clone(),
None,
types::Annotation::Input(InputAnnotation::BooleanExpression(
BooleanExpressionAnnotation::BooleanExpressionArgument {
field: types::ModelFilterArgument::RelationshipField(
annotation,
),
namespace_annotations
},
)),
ast::TypeContainer::named_null(
gql_schema::RegisteredTypeName::new(
target_model_filter_expression_graphql.type_name.0.clone(),
),
)));
}
}
),
None,
gql_schema::DeprecationStatus::NotDeprecated,
),
namespace_annotations,
),
)));
}
}
}
@ -409,7 +390,6 @@ fn build_schema_with_object_boolean_expression_type(
input_fields.extend(build_comparable_relationships_schema(
gds,
object_type_representation,
&object_boolean_expression_type.data_connector,
builder,
)?);