Allow specifying bool exp for relationship (#739)

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

## Description

When doing relationships across bool exps, by default we use the target
model's `where` clause bool exp. However, we allow the user to provide a
different bool exp instead, which we now pay attention to.

Behind a feature flag so a user-facing no-op.

V3_GIT_ORIGIN_REV_ID: b31157c455d56375c35db28f2418da9d41b68c46
This commit is contained in:
Daniel Harvey 2024-06-21 12:04:07 +01:00 committed by hasura-bot
parent 99954495d5
commit b658c97a07
2 changed files with 100 additions and 55 deletions

View File

@ -140,7 +140,10 @@
} }
], ],
"comparableRelationships": [ "comparableRelationships": [
{ "relationshipName": "Artist" }, {
"relationshipName": "Artist",
"booleanExpressionType": "artist_bool_exp"
},
{ "relationshipName": "Tracks" } { "relationshipName": "Tracks" }
] ]
} }
@ -391,6 +394,7 @@
"dataConnectorName": "db", "dataConnectorName": "db",
"collection": "Artist" "collection": "Artist"
}, },
"filterExpressionType": "artist_bool_exp",
"graphql": { "graphql": {
"selectUniques": [ "selectUniques": [
{ {
@ -403,7 +407,6 @@
}, },
"orderByExpressionType": "Artist_Order_By" "orderByExpressionType": "Artist_Order_By"
}, },
"filterExpressionType": "artist_bool_exp",
"orderableFields": [ "orderableFields": [
{ {
"fieldName": "ArtistId", "fieldName": "ArtistId",

View File

@ -23,6 +23,47 @@ use crate::GDS;
use crate::Error; use crate::Error;
/// There are two types of BooleanExpressionType now, we try to build with both
pub fn build_boolean_expression_input_schema(
gds: &GDS,
builder: &mut gql_schema::Builder<GDS>,
type_name: &ast::TypeName,
gds_type_name: &Qualified<CustomTypeName>,
) -> Result<gql_schema::TypeInfo<GDS>, Error> {
match gds
.metadata
.object_boolean_expression_types
.get(gds_type_name)
{
Some(object_boolean_expression_type) => build_schema_with_object_boolean_expression_type(
object_boolean_expression_type,
gds,
builder,
type_name,
gds_type_name,
),
None => {
match gds
.metadata
.boolean_expression_types
.objects
.get(gds_type_name)
{
Some(boolean_expression_object_type) => build_schema_with_boolean_expression_type(
boolean_expression_object_type,
gds,
builder,
type_name,
gds_type_name,
),
None => Err(Error::InternalTypeNotFound {
type_name: gds_type_name.clone(),
}),
}
}
}
}
// add input fields for `_and`, `_or`, etc // add input fields for `_and`, `_or`, etc
fn build_builtin_operator_schema( fn build_builtin_operator_schema(
boolean_expression_info: &BooleanExpressionGraphqlConfig, boolean_expression_info: &BooleanExpressionGraphqlConfig,
@ -205,7 +246,7 @@ fn build_new_comparable_relationships_schema(
) -> Result<BTreeMap<ast::Name, gql_schema::Namespaced<GDS, gql_schema::InputField<GDS>>>, Error> { ) -> Result<BTreeMap<ast::Name, gql_schema::Namespaced<GDS, gql_schema::InputField<GDS>>>, Error> {
let mut input_fields = BTreeMap::new(); let mut input_fields = BTreeMap::new();
for (relationship_name, relationship) in relationship_fields { for (relationship_name, comparable_relationship) in relationship_fields {
let field_name = mk_name(&relationship_name.0)?; let field_name = mk_name(&relationship_name.0)?;
// lookup the relationship used in the underlying object type // lookup the relationship used in the underlying object type
@ -213,7 +254,7 @@ fn build_new_comparable_relationships_schema(
.relationship_fields .relationship_fields
.get(&field_name) .get(&field_name)
.ok_or_else(|| Error::InternalRelationshipNotFound { .ok_or_else(|| Error::InternalRelationshipNotFound {
relationship_name: relationship.relationship_name.clone(), relationship_name: comparable_relationship.relationship_name.clone(),
})?; })?;
// we haven't thought about Command relationship targets yet // we haven't thought about Command relationship targets yet
@ -231,6 +272,43 @@ fn build_new_comparable_relationships_schema(
} }
})?; })?;
// if we have specified a boolean expression type to use for this relationship, look it up
// (if none is specified, we use whichever is specified on the model)
let target_boolean_expression_graphql =
match &comparable_relationship.boolean_expression_type {
Some(target_boolean_expression_type_name) => {
let target_boolean_expression_graphql = gds
.metadata
.boolean_expression_types
.objects
.get(target_boolean_expression_type_name)
.and_then(|boolean_expression| boolean_expression.graphql.clone());
// if we find a type, make sure it's added to the schema
if let Some(graphql) = &target_boolean_expression_graphql {
let _registered_type_name =
builder.register_type(TypeId::InputObjectBooleanExpressionType {
graphql_type_name: graphql.type_name.clone(),
gds_type_name: target_boolean_expression_type_name.clone(),
});
}
target_boolean_expression_graphql
}
None => {
// no specific type is provided by the relationship, so
// lookup filter expression graphql for target model
match &target_model.model.filter_expression_type {
Some(ModelExpressionType::BooleanExpressionType(
target_boolean_expression_type,
)) => target_boolean_expression_type.graphql.clone(),
Some(ModelExpressionType::ObjectBooleanExpressionType(
target_model_filter_expression,
)) => target_model_filter_expression.graphql.clone(),
None => None,
}
}
};
// lookup type underlying target model // lookup type underlying target model
let target_object_type_representation = let target_object_type_representation =
get_object_type_representation(gds, &target_model.model.data_type)?; get_object_type_representation(gds, &target_model.model.data_type)?;
@ -240,6 +318,7 @@ fn build_new_comparable_relationships_schema(
if let Some((name, schema)) = build_model_relationship_schema( if let Some((name, schema)) = build_model_relationship_schema(
object_type_representation, object_type_representation,
target_object_type_representation, target_object_type_representation,
&target_boolean_expression_graphql,
target_model, target_model,
relationship, relationship,
relationship_type, relationship_type,
@ -282,10 +361,23 @@ fn build_comparable_relationships_schema(
let target_object_type_representation = let target_object_type_representation =
get_object_type_representation(gds, &target_model.model.data_type)?; get_object_type_representation(gds, &target_model.model.data_type)?;
// lookup filter expression graphql for target model
let target_boolean_expression_graphql = match &target_model.model.filter_expression_type
{
None => None,
Some(ModelExpressionType::BooleanExpressionType(
target_boolean_expression_type,
)) => target_boolean_expression_type.graphql.clone(),
Some(ModelExpressionType::ObjectBooleanExpressionType(
target_model_filter_expression,
)) => target_model_filter_expression.graphql.clone(),
};
// try and create a new input field // try and create a new input field
if let Some((name, schema)) = build_model_relationship_schema( if let Some((name, schema)) = build_model_relationship_schema(
object_type_representation, object_type_representation,
target_object_type_representation, target_object_type_representation,
&target_boolean_expression_graphql,
target_model, target_model,
relationship, relationship,
relationship_type, relationship_type,
@ -309,6 +401,7 @@ type InputField = (
fn build_model_relationship_schema( fn build_model_relationship_schema(
source_object_type_representation: &ObjectTypeWithRelationships, source_object_type_representation: &ObjectTypeWithRelationships,
target_object_type_representation: &ObjectTypeWithRelationships, target_object_type_representation: &ObjectTypeWithRelationships,
target_filter_expression_graphql: &Option<BooleanExpressionGraphqlConfig>,
target_model: &ModelWithPermissions, target_model: &ModelWithPermissions,
relationship: &RelationshipField, relationship: &RelationshipField,
relationship_type: &RelationshipType, relationship_type: &RelationshipType,
@ -316,17 +409,6 @@ fn build_model_relationship_schema(
gds: &GDS, gds: &GDS,
builder: &mut gql_schema::Builder<GDS>, builder: &mut gql_schema::Builder<GDS>,
) -> Result<Option<InputField>, Error> { ) -> Result<Option<InputField>, Error> {
// lookup filter expression graphql for target model
let target_filter_expression_graphql = match &target_model.model.filter_expression_type {
None => None,
Some(ModelExpressionType::BooleanExpressionType(target_boolean_expression_type)) => {
target_boolean_expression_type.graphql.clone()
}
Some(ModelExpressionType::ObjectBooleanExpressionType(target_model_filter_expression)) => {
target_model_filter_expression.graphql.clone()
}
};
// Build relationship field in filter expression only when // Build relationship field in filter expression only when
// the target_model is backed by a source // the target_model is backed by a source
if let (Some(target_source), Some(target_model_filter_expression_graphql)) = if let (Some(target_source), Some(target_model_filter_expression_graphql)) =
@ -377,46 +459,6 @@ fn build_model_relationship_schema(
Ok(None) Ok(None)
} }
/// There are two types of BooleanExpressionType now, we try to build with both
pub fn build_boolean_expression_input_schema(
gds: &GDS,
builder: &mut gql_schema::Builder<GDS>,
type_name: &ast::TypeName,
gds_type_name: &Qualified<CustomTypeName>,
) -> Result<gql_schema::TypeInfo<GDS>, Error> {
match gds
.metadata
.object_boolean_expression_types
.get(gds_type_name)
{
Some(object_boolean_expression_type) => build_schema_with_object_boolean_expression_type(
object_boolean_expression_type,
gds,
builder,
type_name,
gds_type_name,
),
None => {
match gds
.metadata
.boolean_expression_types
.objects
.get(gds_type_name)
{
Some(boolean_expression_object_type) => build_schema_with_boolean_expression_type(
boolean_expression_object_type,
gds,
builder,
type_name,
gds_type_name,
),
None => Err(Error::InternalTypeNotFound {
type_name: gds_type_name.clone(),
}),
}
}
}
}
// build the schema using the old `ObjectBooleanExpressionType` metadata kind // build the schema using the old `ObjectBooleanExpressionType` metadata kind
fn build_schema_with_object_boolean_expression_type( fn build_schema_with_object_boolean_expression_type(