Enable aggregate relationships by default (#765)

NOTE: This PR is stacked on #756 and should be shipped after that is
merged.

This PR enables the existing aggregate relationships work (see #725,
#731, #756) by default by removing the experimental flag it used to be
disabled behind.

The new OpenDD schema changes that were added are also unhidden so that
they are visible in the OpenDD JSON Schema.

V3_GIT_ORIGIN_REV_ID: cfd86d8a9ea61887ccf0f1a5d08bdcc3dda59cdc
This commit is contained in:
Daniel Chambers 2024-06-26 20:48:25 +10:00 committed by hasura-bot
parent 0624a7553d
commit 82c0c65bd0
11 changed files with 105 additions and 35 deletions

View File

@ -4,6 +4,37 @@
### Added
#### Aggregates of Array Relationships
Aggregates of array relationships can now be defined by specifying an
`aggregate` in the `Relationship`'s target. Note that this is only supported
when the target of the relationship is a `Model`. You must also specify the
`aggregateFieldName` under the `graphql` section.
```yaml
kind: Relationship
version: v1
definition:
name: invoices
sourceType: Customer
target:
model:
name: Invoice
relationshipType: Array
aggregate: # New!
aggregateExpression: Invoice_aggregate_exp
description: Aggregate of the customer's invoices
mapping:
- source:
fieldPath:
- fieldName: customerId
target:
modelField:
- fieldName: customerId
graphql: # New!
aggregateFieldName: invoicesAggregate
```
### Changed
### Fixed

View File

@ -11,7 +11,6 @@ use serde::Deserialize;
#[serde(rename_all = "snake_case")]
pub enum UnstableFeature {
EnableBooleanExpressionTypes,
EnableAggregateRelationships,
}
pub fn resolve_unstable_features(
@ -24,9 +23,6 @@ pub fn resolve_unstable_features(
UnstableFeature::EnableBooleanExpressionTypes => {
metadata_resolve_flags.enable_boolean_expression_types = true;
}
UnstableFeature::EnableAggregateRelationships => {
metadata_resolve_flags.enable_aggregate_relationships = true;
}
}
}

View File

@ -79,7 +79,6 @@ pub fn test_execution_expectation_legacy(
let metadata_resolve_flags = metadata_resolve::MetadataResolveFlagsInternal {
enable_boolean_expression_types: true,
enable_aggregate_relationships: true,
};
let gds = GDS::new(metadata, metadata_resolve_flags)?;
@ -169,7 +168,6 @@ pub(crate) fn test_introspection_expectation(
let metadata_resolve_flags = metadata_resolve::MetadataResolveFlagsInternal {
enable_boolean_expression_types: true,
enable_aggregate_relationships: true,
};
let gds = GDS::new(metadata, metadata_resolve_flags)?;
@ -279,7 +277,6 @@ pub fn test_execution_expectation(
// This is where we'll want to enable pre-release features in tests
let metadata_resolve_flags = metadata_resolve::MetadataResolveFlagsInternal {
enable_boolean_expression_types: true,
enable_aggregate_relationships: true,
};
let gds = GDS::new(metadata, metadata_resolve_flags)?;

View File

@ -143,7 +143,6 @@ pub fn resolve(
let object_types_with_relationships = relationships::resolve(
&metadata_accessor,
flags,
&data_connectors,
&data_connector_scalars,
&object_types_with_permissions,

View File

@ -21,7 +21,6 @@ use crate::stages::{
object_types, type_permissions,
};
use crate::types::error::{Error, GraphqlConfigError, RelationshipError};
use crate::types::internal_flags::MetadataResolveFlagsInternal;
use crate::types::subgraph::Qualified;
pub use types::{
@ -35,7 +34,6 @@ pub use types::{
/// returns updated `types` value
pub fn resolve(
metadata_accessor: &open_dds::accessor::MetadataAccessor,
flags: MetadataResolveFlagsInternal,
data_connectors: &data_connectors::DataConnectors,
data_connector_scalars: &BTreeMap<
Qualified<DataConnectorName>,
@ -100,7 +98,6 @@ pub fn resolve(
object_types_with_permissions,
graphql_config,
&object_representation.object_type,
flags,
)?;
for resolved_relationship in resolved_relationships {
@ -427,22 +424,12 @@ fn resolve_aggregate_relationship_field(
>,
object_types: &BTreeMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
graphql_config: &graphql_config::GraphqlConfig,
flags: MetadataResolveFlagsInternal,
) -> Result<Option<RelationshipField>, Error> {
// If an aggregate has been specified
let aggregate_expression_name_and_description = model_relationship_target
.aggregate
.as_ref()
.map(|aggregate| -> Result<_, Error> {
// Check if the feature is enabled or not
if !flags.enable_aggregate_relationships {
return Err(RelationshipError::AggregateRelationshipsDisabled {
type_name: source_type_name.clone(),
relationship_name: relationship.name.clone(),
}
.into());
}
// Ensure the relationship is an array relationship
if model_relationship_target.relationship_type != RelationshipType::Array {
return Err(
@ -536,7 +523,6 @@ fn resolve_model_relationship_fields(
>,
object_types: &BTreeMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
graphql_config: &graphql_config::GraphqlConfig,
flags: MetadataResolveFlagsInternal,
) -> Result<Vec<RelationshipField>, Error> {
let qualified_target_model_name = Qualified::new(
target_model.subgraph().unwrap_or(subgraph).to_string(),
@ -581,7 +567,6 @@ fn resolve_model_relationship_fields(
aggregate_expressions,
object_types,
graphql_config,
flags,
)?;
let regular_relationship_field = RelationshipField {
@ -689,7 +674,6 @@ pub fn resolve_relationships(
object_types: &BTreeMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
graphql_config: &graphql_config::GraphqlConfig,
source_type: &object_types::ObjectTypeRepresentation,
flags: MetadataResolveFlagsInternal,
) -> Result<Vec<RelationshipField>, Error> {
let source_type_name = Qualified::new(subgraph.to_string(), relationship.source_type.clone());
match &relationship.target {
@ -706,7 +690,6 @@ pub fn resolve_relationships(
aggregate_expressions,
object_types,
graphql_config,
flags,
)
}
relationships::RelationshipTarget::Command(target_command) => {

View File

@ -790,11 +790,6 @@ pub enum RelationshipError {
relationship_name: RelationshipName,
data_connector_name: Qualified<DataConnectorName>,
},
#[error("The relationship {relationship_name} on type {type_name} defines an aggregate, but the aggregate relationships feature is disabled")]
AggregateRelationshipsDisabled {
type_name: Qualified<CustomTypeName>,
relationship_name: RelationshipName,
},
#[error("The relationship {relationship_name} on type {type_name} defines an aggregate, but aggregates can only be used with array relationships, not object relationships")]
AggregateIsOnlyAllowedOnArrayRelationships {
type_name: Qualified<CustomTypeName>,

View File

@ -2,5 +2,4 @@
/// internal feature flags used in metadata resolve steps
pub struct MetadataResolveFlagsInternal {
pub enable_boolean_expression_types: bool,
pub enable_aggregate_relationships: bool,
}

View File

@ -13,7 +13,6 @@ fn test_passing_metadata() {
}, {
let metadata_resolve_flags_internal = MetadataResolveFlagsInternal {
enable_boolean_expression_types: true,
enable_aggregate_relationships: true,
};
let metadata_json_text = std::fs::read_to_string(path)
@ -43,7 +42,6 @@ fn test_failing_metadata() {
}, {
let metadata_resolve_flags_internal = MetadataResolveFlagsInternal {
enable_boolean_expression_types: true,
enable_aggregate_relationships: true,
};
let metadata_json_text = std::fs::read_to_string(path)

View File

@ -3682,6 +3682,17 @@
"type": "null"
}
]
},
"graphql": {
"description": "Configuration for how this relationship should appear in the GraphQL schema.",
"anyOf": [
{
"$ref": "#/definitions/RelationshipGraphQlDefinition"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
@ -3758,6 +3769,17 @@
"$ref": "#/definitions/RelationshipType"
}
]
},
"aggregate": {
"description": "How to aggregate over the relationship. Only valid for array relationships",
"anyOf": [
{
"$ref": "#/definitions/ModelRelationshipTargetAggregate"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
@ -3783,6 +3805,33 @@
}
]
},
"ModelRelationshipTargetAggregate": {
"$id": "https://hasura.io/jsonschemas/metadata/ModelRelationshipTargetAggregate",
"title": "ModelRelationshipTargetAggregate",
"description": "Which aggregate expression to use to aggregate the array relationship.",
"type": "object",
"required": [
"aggregateExpression"
],
"properties": {
"aggregateExpression": {
"description": "The name of the aggregate expression that defines how to aggregate across the relationship.",
"allOf": [
{
"$ref": "#/definitions/AggregateExpressionName"
}
]
},
"description": {
"description": "The description of the relationship aggregate. Gets added to the description of the aggregate field in the GraphQL schema",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
},
"CommandRelationshipTarget": {
"$id": "https://hasura.io/jsonschemas/metadata/CommandRelationshipTarget",
"title": "CommandRelationshipTarget",
@ -3957,6 +4006,26 @@
},
"additionalProperties": false
},
"RelationshipGraphQlDefinition": {
"$id": "https://hasura.io/jsonschemas/metadata/RelationshipGraphQlDefinition",
"title": "RelationshipGraphQlDefinition",
"description": "The definition of how a relationship appears in the GraphQL API",
"type": "object",
"properties": {
"aggregateFieldName": {
"description": "The field name to use for the field that represents an aggregate over the relationship",
"anyOf": [
{
"$ref": "#/definitions/FieldName"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"TypePermissionsV1": {
"$id": "https://hasura.io/jsonschemas/metadata/TypePermissionsV1",
"title": "TypePermissionsV1",

View File

@ -52,7 +52,6 @@ pub struct ModelRelationshipTarget {
/// Type of the relationship - object or array.
pub relationship_type: RelationshipType,
/// How to aggregate over the relationship. Only valid for array relationships
#[opendd(hidden)]
#[serde(skip_serializing_if = "Option::is_none")]
pub aggregate: Option<ModelRelationshipTargetAggregate>,
}
@ -289,6 +288,5 @@ pub struct RelationshipV1 {
/// If set, the deprecation status is added to the relationship field's graphql schema.
pub deprecated: Option<Deprecated>,
/// Configuration for how this relationship should appear in the GraphQL schema.
#[opendd(hidden)]
pub graphql: Option<RelationshipGraphQlDefinition>,
}

View File

@ -325,6 +325,11 @@ enums.
trait implemented. The default JSON value will be inferred using
`serde_json::json!(Default::default())`.
- `[opendd(hidden = true)]`
Hide the field from the json schema, useful for keeping work in progress out
of the public API.
## Notes
- Please make sure the following crates/modules are accessible in the module