mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 08:02:15 +03:00
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:
parent
0624a7553d
commit
82c0c65bd0
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)?;
|
||||
|
@ -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,
|
||||
|
@ -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) => {
|
||||
|
@ -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>,
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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>,
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user