mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
Separate relationships
resolve stage (#522)
<!-- Thank you for submitting this PR! :) --> ## Description This separates out the stage that resolves relationships. The most important thing here is that we no longer have a `relationships` field in `ObjectTypeRepresentation` that may or may not be populated, and instead add a new wrapper type `ObjectTypeWithRelationships`, which is used downstream of this stage. Stacked on top of https://github.com/hasura/v3-engine/pull/521 Functional no-op. V3_GIT_ORIGIN_REV_ID: 1e6ca41e55b8cc470385c35bbd7999fa7a2bce6e
This commit is contained in:
parent
b1149c26de
commit
847f81ad96
@ -23,10 +23,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::execute::model_tracking::{count_model, UsagesCounts};
|
use crate::execute::model_tracking::{count_model, UsagesCounts};
|
||||||
use crate::metadata::resolved::{
|
use crate::metadata::resolved::subgraph::serialize_qualified_btreemap;
|
||||||
relationship::{relationship_execution_category, RelationshipExecutionCategory},
|
|
||||||
subgraph::serialize_qualified_btreemap,
|
|
||||||
};
|
|
||||||
use crate::schema::types::output_type::relationship::{
|
use crate::schema::types::output_type::relationship::{
|
||||||
ModelRelationshipAnnotation, ModelTargetSource,
|
ModelRelationshipAnnotation, ModelTargetSource,
|
||||||
};
|
};
|
||||||
@ -54,7 +51,7 @@ pub(crate) struct LocalModelRelationshipInfo<'s> {
|
|||||||
pub source_type_mappings: &'s BTreeMap<Qualified<CustomTypeName>, resolved::TypeMapping>,
|
pub source_type_mappings: &'s BTreeMap<Qualified<CustomTypeName>, resolved::TypeMapping>,
|
||||||
pub target_source: &'s ModelTargetSource,
|
pub target_source: &'s ModelTargetSource,
|
||||||
pub target_type: &'s Qualified<CustomTypeName>,
|
pub target_type: &'s Qualified<CustomTypeName>,
|
||||||
pub mappings: &'s Vec<resolved::relationship::RelationshipModelMapping>,
|
pub mappings: &'s Vec<resolved::RelationshipModelMapping>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
@ -100,19 +97,19 @@ pub(crate) fn process_model_relationship_definition(
|
|||||||
} = relationship_info;
|
} = relationship_info;
|
||||||
|
|
||||||
let mut column_mapping = BTreeMap::new();
|
let mut column_mapping = BTreeMap::new();
|
||||||
for resolved::relationship::RelationshipModelMapping {
|
for resolved::RelationshipModelMapping {
|
||||||
source_field: source_field_path,
|
source_field: source_field_path,
|
||||||
target_field: _,
|
target_field: _,
|
||||||
target_ndc_column,
|
target_ndc_column,
|
||||||
} in mappings.iter()
|
} in mappings.iter()
|
||||||
{
|
{
|
||||||
if !matches!(
|
if !matches!(
|
||||||
relationship_execution_category(
|
resolved::relationship_execution_category(
|
||||||
source_data_connector,
|
source_data_connector,
|
||||||
&target_source.model.data_connector,
|
&target_source.model.data_connector,
|
||||||
&target_source.capabilities
|
&target_source.capabilities
|
||||||
),
|
),
|
||||||
RelationshipExecutionCategory::Local
|
resolved::RelationshipExecutionCategory::Local
|
||||||
) {
|
) {
|
||||||
Err(error::InternalEngineError::RemoteRelationshipsAreNotSupported)?
|
Err(error::InternalEngineError::RemoteRelationshipsAreNotSupported)?
|
||||||
} else {
|
} else {
|
||||||
@ -165,18 +162,18 @@ pub(crate) fn process_command_relationship_definition(
|
|||||||
} = relationship_info;
|
} = relationship_info;
|
||||||
|
|
||||||
let mut arguments = BTreeMap::new();
|
let mut arguments = BTreeMap::new();
|
||||||
for resolved::relationship::RelationshipCommandMapping {
|
for resolved::RelationshipCommandMapping {
|
||||||
source_field: source_field_path,
|
source_field: source_field_path,
|
||||||
argument_name: target_argument,
|
argument_name: target_argument,
|
||||||
} in annotation.mappings.iter()
|
} in annotation.mappings.iter()
|
||||||
{
|
{
|
||||||
if !matches!(
|
if !matches!(
|
||||||
relationship_execution_category(
|
resolved::relationship_execution_category(
|
||||||
source_data_connector,
|
source_data_connector,
|
||||||
&target_source.details.data_connector,
|
&target_source.details.data_connector,
|
||||||
&target_source.capabilities
|
&target_source.capabilities
|
||||||
),
|
),
|
||||||
RelationshipExecutionCategory::Local
|
resolved::RelationshipExecutionCategory::Local
|
||||||
) {
|
) {
|
||||||
Err(error::InternalEngineError::RemoteRelationshipsAreNotSupported)?
|
Err(error::InternalEngineError::RemoteRelationshipsAreNotSupported)?
|
||||||
} else {
|
} else {
|
||||||
@ -293,12 +290,12 @@ pub(crate) fn generate_model_relationship_ir<'s>(
|
|||||||
}
|
}
|
||||||
None => error::Error::from(normalized_ast::Error::NoTypenameFound),
|
None => error::Error::from(normalized_ast::Error::NoTypenameFound),
|
||||||
})?;
|
})?;
|
||||||
match relationship_execution_category(
|
match resolved::relationship_execution_category(
|
||||||
source_data_connector,
|
source_data_connector,
|
||||||
&target_source.model.data_connector,
|
&target_source.model.data_connector,
|
||||||
&target_source.capabilities,
|
&target_source.capabilities,
|
||||||
) {
|
) {
|
||||||
RelationshipExecutionCategory::Local => build_local_model_relationship(
|
resolved::RelationshipExecutionCategory::Local => build_local_model_relationship(
|
||||||
field,
|
field,
|
||||||
field_call,
|
field_call,
|
||||||
annotation,
|
annotation,
|
||||||
@ -312,7 +309,7 @@ pub(crate) fn generate_model_relationship_ir<'s>(
|
|||||||
session_variables,
|
session_variables,
|
||||||
usage_counts,
|
usage_counts,
|
||||||
),
|
),
|
||||||
RelationshipExecutionCategory::RemoteForEach => build_remote_relationship(
|
resolved::RelationshipExecutionCategory::RemoteForEach => build_remote_relationship(
|
||||||
field,
|
field,
|
||||||
field_call,
|
field_call,
|
||||||
annotation,
|
annotation,
|
||||||
@ -353,12 +350,12 @@ pub(crate) fn generate_command_relationship_ir<'s>(
|
|||||||
None => error::Error::from(normalized_ast::Error::NoTypenameFound),
|
None => error::Error::from(normalized_ast::Error::NoTypenameFound),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
match relationship_execution_category(
|
match resolved::relationship_execution_category(
|
||||||
source_data_connector,
|
source_data_connector,
|
||||||
&target_source.details.data_connector,
|
&target_source.details.data_connector,
|
||||||
&target_source.capabilities,
|
&target_source.capabilities,
|
||||||
) {
|
) {
|
||||||
RelationshipExecutionCategory::Local => build_local_command_relationship(
|
resolved::RelationshipExecutionCategory::Local => build_local_command_relationship(
|
||||||
field,
|
field,
|
||||||
field_call,
|
field_call,
|
||||||
annotation,
|
annotation,
|
||||||
@ -367,14 +364,16 @@ pub(crate) fn generate_command_relationship_ir<'s>(
|
|||||||
target_source,
|
target_source,
|
||||||
session_variables,
|
session_variables,
|
||||||
),
|
),
|
||||||
RelationshipExecutionCategory::RemoteForEach => build_remote_command_relationship(
|
resolved::RelationshipExecutionCategory::RemoteForEach => {
|
||||||
|
build_remote_command_relationship(
|
||||||
field,
|
field,
|
||||||
field_call,
|
field_call,
|
||||||
annotation,
|
annotation,
|
||||||
type_mappings,
|
type_mappings,
|
||||||
target_source,
|
target_source,
|
||||||
session_variables,
|
session_variables,
|
||||||
),
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,7 +480,7 @@ pub(crate) fn build_remote_relationship<'n, 's>(
|
|||||||
usage_counts: &mut UsagesCounts,
|
usage_counts: &mut UsagesCounts,
|
||||||
) -> Result<FieldSelection<'s>, error::Error> {
|
) -> Result<FieldSelection<'s>, error::Error> {
|
||||||
let mut join_mapping: Vec<(SourceField, TargetField)> = vec![];
|
let mut join_mapping: Vec<(SourceField, TargetField)> = vec![];
|
||||||
for resolved::relationship::RelationshipModelMapping {
|
for resolved::RelationshipModelMapping {
|
||||||
source_field: source_field_path,
|
source_field: source_field_path,
|
||||||
target_field: target_field_path,
|
target_field: target_field_path,
|
||||||
target_ndc_column,
|
target_ndc_column,
|
||||||
@ -561,7 +560,7 @@ pub(crate) fn build_remote_command_relationship<'n, 's>(
|
|||||||
session_variables: &SessionVariables,
|
session_variables: &SessionVariables,
|
||||||
) -> Result<FieldSelection<'s>, error::Error> {
|
) -> Result<FieldSelection<'s>, error::Error> {
|
||||||
let mut join_mapping: Vec<(SourceField, ArgumentName)> = vec![];
|
let mut join_mapping: Vec<(SourceField, ArgumentName)> = vec![];
|
||||||
for resolved::relationship::RelationshipCommandMapping {
|
for resolved::RelationshipCommandMapping {
|
||||||
source_field: source_field_path,
|
source_field: source_field_path,
|
||||||
argument_name: target_argument_name,
|
argument_name: target_argument_name,
|
||||||
} in annotation.mappings.iter()
|
} in annotation.mappings.iter()
|
||||||
|
@ -5,7 +5,6 @@ pub mod metadata;
|
|||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod ndc_validation;
|
pub mod ndc_validation;
|
||||||
pub mod permission;
|
pub mod permission;
|
||||||
pub mod relationship;
|
|
||||||
pub mod stages;
|
pub mod stages;
|
||||||
pub mod subgraph;
|
pub mod subgraph;
|
||||||
mod typecheck;
|
mod typecheck;
|
||||||
@ -23,4 +22,11 @@ pub use stages::models::{
|
|||||||
FilterPermission, Model, ModelOrderByExpression, ModelPredicate, ModelSource,
|
FilterPermission, Model, ModelOrderByExpression, ModelPredicate, ModelSource,
|
||||||
SelectManyGraphQlDefinition, SelectUniqueGraphQlDefinition,
|
SelectManyGraphQlDefinition, SelectUniqueGraphQlDefinition,
|
||||||
};
|
};
|
||||||
|
/// we seem to be exporting functions. perhaps these would be better served as methods on the data
|
||||||
|
/// types we export?
|
||||||
|
pub use stages::relationships::{
|
||||||
|
relationship_execution_category, ObjectTypeWithRelationships, Relationship,
|
||||||
|
RelationshipCapabilities, RelationshipCommandMapping, RelationshipExecutionCategory,
|
||||||
|
RelationshipModelMapping, RelationshipTarget,
|
||||||
|
};
|
||||||
pub use stages::resolve;
|
pub use stages::resolve;
|
||||||
|
@ -6,7 +6,7 @@ use crate::metadata::resolved::ndc_validation;
|
|||||||
use crate::metadata::resolved::permission::ValueExpression;
|
use crate::metadata::resolved::permission::ValueExpression;
|
||||||
use crate::metadata::resolved::stages::{
|
use crate::metadata::resolved::stages::{
|
||||||
boolean_expressions, data_connector_scalar_types, data_connector_type_mappings, models,
|
boolean_expressions, data_connector_scalar_types, data_connector_type_mappings, models,
|
||||||
scalar_types, type_permissions,
|
relationships, scalar_types, type_permissions,
|
||||||
};
|
};
|
||||||
use crate::metadata::resolved::subgraph::{ArgumentInfo, Qualified};
|
use crate::metadata::resolved::subgraph::{ArgumentInfo, Qualified};
|
||||||
use crate::metadata::resolved::subgraph::{QualifiedBaseType, QualifiedTypeReference};
|
use crate::metadata::resolved::subgraph::{QualifiedBaseType, QualifiedTypeReference};
|
||||||
@ -165,7 +165,7 @@ pub(crate) fn resolve_value_expression_for_argument(
|
|||||||
value_expression: &open_dds::permissions::ValueExpression,
|
value_expression: &open_dds::permissions::ValueExpression,
|
||||||
argument_type: &QualifiedTypeReference,
|
argument_type: &QualifiedTypeReference,
|
||||||
subgraph: &str,
|
subgraph: &str,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
|
||||||
boolean_expression_types: &HashMap<
|
boolean_expression_types: &HashMap<
|
||||||
Qualified<CustomTypeName>,
|
Qualified<CustomTypeName>,
|
||||||
boolean_expressions::ObjectBooleanExpressionType,
|
boolean_expressions::ObjectBooleanExpressionType,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::stages::{
|
use super::stages::{
|
||||||
boolean_expressions, commands, data_connector_scalar_types, data_connector_type_mappings,
|
boolean_expressions, commands, data_connector_scalar_types, data_connector_type_mappings,
|
||||||
type_permissions,
|
relationships,
|
||||||
};
|
};
|
||||||
use crate::metadata::resolved::argument::resolve_value_expression_for_argument;
|
use crate::metadata::resolved::argument::resolve_value_expression_for_argument;
|
||||||
use crate::metadata::resolved::error::Error;
|
use crate::metadata::resolved::error::Error;
|
||||||
@ -17,7 +17,7 @@ use super::typecheck;
|
|||||||
pub fn resolve_command_permissions(
|
pub fn resolve_command_permissions(
|
||||||
command: &commands::Command,
|
command: &commands::Command,
|
||||||
permissions: &CommandPermissionsV1,
|
permissions: &CommandPermissionsV1,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
|
||||||
boolean_expression_types: &HashMap<
|
boolean_expression_types: &HashMap<
|
||||||
Qualified<CustomTypeName>,
|
Qualified<CustomTypeName>,
|
||||||
boolean_expressions::ObjectBooleanExpressionType,
|
boolean_expressions::ObjectBooleanExpressionType,
|
||||||
|
@ -9,19 +9,18 @@ use open_dds::{commands::CommandName, models::ModelName, types::CustomTypeName};
|
|||||||
use crate::metadata::resolved::command;
|
use crate::metadata::resolved::command;
|
||||||
use crate::metadata::resolved::error::Error;
|
use crate::metadata::resolved::error::Error;
|
||||||
use crate::metadata::resolved::model::resolve_model_select_permissions;
|
use crate::metadata::resolved::model::resolve_model_select_permissions;
|
||||||
use crate::metadata::resolved::relationship::resolve_relationship;
|
|
||||||
use crate::metadata::resolved::subgraph::Qualified;
|
use crate::metadata::resolved::subgraph::Qualified;
|
||||||
|
|
||||||
use crate::metadata::resolved::stages::{
|
use crate::metadata::resolved::stages::{
|
||||||
boolean_expressions, commands, data_connector_scalar_types, data_connector_type_mappings,
|
boolean_expressions, commands, data_connector_scalar_types, data_connector_type_mappings,
|
||||||
graphql_config, models, roles, scalar_types, type_permissions,
|
graphql_config, models, relationships, roles, scalar_types,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Resolved and validated metadata for a project. Used internally in the v3 server.
|
/// Resolved and validated metadata for a project. Used internally in the v3 server.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
pub object_types:
|
pub object_types:
|
||||||
HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
|
||||||
pub scalar_types: HashMap<Qualified<CustomTypeName>, scalar_types::ScalarTypeRepresentation>,
|
pub scalar_types: HashMap<Qualified<CustomTypeName>, scalar_types::ScalarTypeRepresentation>,
|
||||||
pub models: IndexMap<Qualified<ModelName>, models::Model>,
|
pub models: IndexMap<Qualified<ModelName>, models::Model>,
|
||||||
pub commands: IndexMap<Qualified<CommandName>, commands::Command>,
|
pub commands: IndexMap<Qualified<CommandName>, commands::Command>,
|
||||||
@ -38,7 +37,7 @@ pub fn resolve_metadata(
|
|||||||
metadata_accessor: &open_dds::accessor::MetadataAccessor,
|
metadata_accessor: &open_dds::accessor::MetadataAccessor,
|
||||||
graphql_config: &graphql_config::GraphqlConfig,
|
graphql_config: &graphql_config::GraphqlConfig,
|
||||||
data_connector_type_mappings: &data_connector_type_mappings::DataConnectorTypeMappings,
|
data_connector_type_mappings: &data_connector_type_mappings::DataConnectorTypeMappings,
|
||||||
object_types: HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
|
||||||
scalar_types: &HashMap<Qualified<CustomTypeName>, scalar_types::ScalarTypeRepresentation>,
|
scalar_types: &HashMap<Qualified<CustomTypeName>, scalar_types::ScalarTypeRepresentation>,
|
||||||
boolean_expression_types: &HashMap<
|
boolean_expression_types: &HashMap<
|
||||||
Qualified<CustomTypeName>,
|
Qualified<CustomTypeName>,
|
||||||
@ -48,15 +47,6 @@ pub fn resolve_metadata(
|
|||||||
mut models: IndexMap<Qualified<ModelName>, models::Model>,
|
mut models: IndexMap<Qualified<ModelName>, models::Model>,
|
||||||
mut commands: IndexMap<Qualified<CommandName>, commands::Command>,
|
mut commands: IndexMap<Qualified<CommandName>, commands::Command>,
|
||||||
) -> Result<Metadata, Error> {
|
) -> Result<Metadata, Error> {
|
||||||
// resolve relationships
|
|
||||||
let object_types = resolve_relationships(
|
|
||||||
metadata_accessor,
|
|
||||||
data_connectors,
|
|
||||||
object_types,
|
|
||||||
&models,
|
|
||||||
&commands,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// resolve command permissions
|
// resolve command permissions
|
||||||
// TODO: make this return values rather than blindly mutating it's inputs
|
// TODO: make this return values rather than blindly mutating it's inputs
|
||||||
resolve_command_permissions(
|
resolve_command_permissions(
|
||||||
@ -95,68 +85,13 @@ pub fn resolve_metadata(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// resolve relationships
|
|
||||||
/// returns updated `types` value
|
|
||||||
fn resolve_relationships(
|
|
||||||
metadata_accessor: &open_dds::accessor::MetadataAccessor,
|
|
||||||
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
|
||||||
mut object_types: HashMap<
|
|
||||||
Qualified<CustomTypeName>,
|
|
||||||
type_permissions::ObjectTypeWithPermissions,
|
|
||||||
>,
|
|
||||||
models: &IndexMap<Qualified<ModelName>, models::Model>,
|
|
||||||
commands: &IndexMap<Qualified<CommandName>, commands::Command>,
|
|
||||||
) -> Result<HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>, Error>
|
|
||||||
{
|
|
||||||
for open_dds::accessor::QualifiedObject {
|
|
||||||
subgraph,
|
|
||||||
object: relationship,
|
|
||||||
} in &metadata_accessor.relationships
|
|
||||||
{
|
|
||||||
let qualified_relationship_source_type_name =
|
|
||||||
Qualified::new(subgraph.to_string(), relationship.source.to_owned());
|
|
||||||
let object_representation = object_types
|
|
||||||
.get_mut(&qualified_relationship_source_type_name)
|
|
||||||
.ok_or_else(|| Error::RelationshipDefinedOnUnknownType {
|
|
||||||
relationship_name: relationship.name.clone(),
|
|
||||||
type_name: qualified_relationship_source_type_name.clone(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let resolved_relationship = resolve_relationship(
|
|
||||||
relationship,
|
|
||||||
subgraph,
|
|
||||||
models,
|
|
||||||
commands,
|
|
||||||
data_connectors,
|
|
||||||
object_representation,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if object_representation
|
|
||||||
.object_type
|
|
||||||
.relationships
|
|
||||||
.insert(
|
|
||||||
resolved_relationship.field_name.clone(),
|
|
||||||
resolved_relationship,
|
|
||||||
)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
return Err(Error::DuplicateRelationshipInSourceType {
|
|
||||||
type_name: qualified_relationship_source_type_name,
|
|
||||||
relationship_name: relationship.name.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(object_types)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// resolve command permissions
|
/// resolve command permissions
|
||||||
/// this currently works by mutating `commands`, let's change it to
|
/// this currently works by mutating `commands`, let's change it to
|
||||||
/// return new values instead where possible
|
/// return new values instead where possible
|
||||||
fn resolve_command_permissions(
|
fn resolve_command_permissions(
|
||||||
metadata_accessor: &open_dds::accessor::MetadataAccessor,
|
metadata_accessor: &open_dds::accessor::MetadataAccessor,
|
||||||
commands: &mut IndexMap<Qualified<CommandName>, commands::Command>,
|
commands: &mut IndexMap<Qualified<CommandName>, commands::Command>,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
|
||||||
boolean_expression_types: &HashMap<
|
boolean_expression_types: &HashMap<
|
||||||
Qualified<CustomTypeName>,
|
Qualified<CustomTypeName>,
|
||||||
boolean_expressions::ObjectBooleanExpressionType,
|
boolean_expressions::ObjectBooleanExpressionType,
|
||||||
@ -201,7 +136,7 @@ fn resolve_command_permissions(
|
|||||||
fn resolve_model_permissions(
|
fn resolve_model_permissions(
|
||||||
metadata_accessor: &open_dds::accessor::MetadataAccessor,
|
metadata_accessor: &open_dds::accessor::MetadataAccessor,
|
||||||
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
|
||||||
models: &mut IndexMap<Qualified<ModelName>, models::Model>,
|
models: &mut IndexMap<Qualified<ModelName>, models::Model>,
|
||||||
boolean_expression_types: &HashMap<
|
boolean_expression_types: &HashMap<
|
||||||
Qualified<CustomTypeName>,
|
Qualified<CustomTypeName>,
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use super::permission::ValueExpression;
|
use super::permission::ValueExpression;
|
||||||
use super::relationship::RelationshipTarget;
|
|
||||||
use super::stages::{
|
use super::stages::{
|
||||||
boolean_expressions, data_connector_scalar_types, data_connector_type_mappings, models,
|
boolean_expressions, data_connector_scalar_types, data_connector_type_mappings, models,
|
||||||
type_permissions,
|
relationships,
|
||||||
};
|
};
|
||||||
use super::typecheck;
|
use super::typecheck;
|
||||||
|
|
||||||
@ -84,9 +83,8 @@ fn resolve_model_predicate(
|
|||||||
subgraph: &str,
|
subgraph: &str,
|
||||||
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
||||||
fields: &IndexMap<FieldName, data_connector_type_mappings::FieldDefinition>,
|
fields: &IndexMap<FieldName, data_connector_type_mappings::FieldDefinition>,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
|
||||||
models: &IndexMap<Qualified<ModelName>, models::Model>,
|
models: &IndexMap<Qualified<ModelName>, models::Model>,
|
||||||
// type_representation: &TypeRepresentation,
|
|
||||||
) -> Result<models::ModelPredicate, Error> {
|
) -> Result<models::ModelPredicate, Error> {
|
||||||
match model_predicate {
|
match model_predicate {
|
||||||
permissions::ModelPredicate::FieldComparison(permissions::FieldComparisonPredicate {
|
permissions::ModelPredicate::FieldComparison(permissions::FieldComparisonPredicate {
|
||||||
@ -213,7 +211,6 @@ fn resolve_model_predicate(
|
|||||||
)?;
|
)?;
|
||||||
let relationship_field_name = mk_name(&name.0)?;
|
let relationship_field_name = mk_name(&name.0)?;
|
||||||
let relationship = &object_type_representation
|
let relationship = &object_type_representation
|
||||||
.object_type
|
|
||||||
.relationships
|
.relationships
|
||||||
.get(&relationship_field_name)
|
.get(&relationship_field_name)
|
||||||
.ok_or_else(|| Error::UnknownRelationshipInSelectPermissionsPredicate {
|
.ok_or_else(|| Error::UnknownRelationshipInSelectPermissionsPredicate {
|
||||||
@ -223,11 +220,13 @@ fn resolve_model_predicate(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
match &relationship.target {
|
match &relationship.target {
|
||||||
RelationshipTarget::Command { .. } => Err(Error::UnsupportedFeature {
|
relationships::RelationshipTarget::Command { .. } => {
|
||||||
|
Err(Error::UnsupportedFeature {
|
||||||
message: "Predicate cannot be built using command relationships"
|
message: "Predicate cannot be built using command relationships"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
}),
|
})
|
||||||
RelationshipTarget::Model {
|
}
|
||||||
|
relationships::RelationshipTarget::Model {
|
||||||
model_name,
|
model_name,
|
||||||
relationship_type,
|
relationship_type,
|
||||||
target_typename,
|
target_typename,
|
||||||
@ -364,7 +363,7 @@ pub fn resolve_model_select_permissions(
|
|||||||
subgraph: &str,
|
subgraph: &str,
|
||||||
model_permissions: &ModelPermissionsV1,
|
model_permissions: &ModelPermissionsV1,
|
||||||
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
|
||||||
models: &IndexMap<Qualified<ModelName>, models::Model>,
|
models: &IndexMap<Qualified<ModelName>, models::Model>,
|
||||||
boolean_expression_types: &HashMap<
|
boolean_expression_types: &HashMap<
|
||||||
Qualified<CustomTypeName>,
|
Qualified<CustomTypeName>,
|
||||||
@ -505,11 +504,11 @@ fn resolve_binary_operator_for_model(
|
|||||||
fn get_model_object_type_representation<'s>(
|
fn get_model_object_type_representation<'s>(
|
||||||
object_types: &'s HashMap<
|
object_types: &'s HashMap<
|
||||||
Qualified<CustomTypeName>,
|
Qualified<CustomTypeName>,
|
||||||
type_permissions::ObjectTypeWithPermissions,
|
relationships::ObjectTypeWithRelationships,
|
||||||
>,
|
>,
|
||||||
data_type: &Qualified<CustomTypeName>,
|
data_type: &Qualified<CustomTypeName>,
|
||||||
model_name: &Qualified<ModelName>,
|
model_name: &Qualified<ModelName>,
|
||||||
) -> Result<&'s type_permissions::ObjectTypeWithPermissions, crate::metadata::resolved::error::Error>
|
) -> Result<&'s relationships::ObjectTypeWithRelationships, crate::metadata::resolved::error::Error>
|
||||||
{
|
{
|
||||||
match object_types.get(data_type) {
|
match object_types.get(data_type) {
|
||||||
Some(object_type_representation) => Ok(object_type_representation),
|
Some(object_type_representation) => Ok(object_type_representation),
|
||||||
|
@ -228,7 +228,6 @@ pub fn resolve_object_type(
|
|||||||
|
|
||||||
Ok(ObjectTypeRepresentation {
|
Ok(ObjectTypeRepresentation {
|
||||||
fields: resolved_fields,
|
fields: resolved_fields,
|
||||||
relationships: IndexMap::new(),
|
|
||||||
global_id_fields: resolved_global_id_fields,
|
global_id_fields: resolved_global_id_fields,
|
||||||
graphql_output_type_name: graphql_type_name,
|
graphql_output_type_name: graphql_type_name,
|
||||||
graphql_input_type_name,
|
graphql_input_type_name,
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use crate::metadata::resolved::error::Error;
|
use crate::metadata::resolved::error::Error;
|
||||||
use crate::metadata::resolved::relationship::Relationship;
|
|
||||||
use crate::metadata::resolved::subgraph::QualifiedTypeReference;
|
use crate::metadata::resolved::subgraph::QualifiedTypeReference;
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
use open_dds::types::{CustomTypeName, Deprecated, FieldName};
|
use open_dds::types::{CustomTypeName, Deprecated, FieldName};
|
||||||
@ -92,7 +90,6 @@ pub struct DataConnectorTypeMappingsOutput {
|
|||||||
#[display(fmt = "Display")]
|
#[display(fmt = "Display")]
|
||||||
pub struct ObjectTypeRepresentation {
|
pub struct ObjectTypeRepresentation {
|
||||||
pub fields: IndexMap<FieldName, FieldDefinition>,
|
pub fields: IndexMap<FieldName, FieldDefinition>,
|
||||||
pub relationships: IndexMap<ast::Name, Relationship>,
|
|
||||||
pub global_id_fields: Vec<FieldName>,
|
pub global_id_fields: Vec<FieldName>,
|
||||||
pub apollo_federation_config: Option<ResolvedObjectApolloFederationConfig>,
|
pub apollo_federation_config: Option<ResolvedObjectApolloFederationConfig>,
|
||||||
pub graphql_output_type_name: Option<ast::TypeName>,
|
pub graphql_output_type_name: Option<ast::TypeName>,
|
||||||
|
@ -7,6 +7,7 @@ pub mod data_connector_type_mappings;
|
|||||||
pub mod data_connectors;
|
pub mod data_connectors;
|
||||||
pub mod graphql_config;
|
pub mod graphql_config;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
|
pub mod relationships;
|
||||||
pub mod roles;
|
pub mod roles;
|
||||||
pub mod scalar_types;
|
pub mod scalar_types;
|
||||||
pub mod type_permissions;
|
pub mod type_permissions;
|
||||||
@ -97,11 +98,19 @@ pub fn resolve(metadata: open_dds::Metadata) -> Result<Metadata, Error> {
|
|||||||
&apollo_federation_entity_enabled_types,
|
&apollo_federation_entity_enabled_types,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let object_types_with_relationships = relationships::resolve(
|
||||||
|
&metadata_accessor,
|
||||||
|
&data_connectors,
|
||||||
|
&object_types_with_permissions,
|
||||||
|
&models,
|
||||||
|
&commands,
|
||||||
|
)?;
|
||||||
|
|
||||||
resolve_metadata(
|
resolve_metadata(
|
||||||
&metadata_accessor,
|
&metadata_accessor,
|
||||||
&graphql_config,
|
&graphql_config,
|
||||||
&data_connector_type_mappings,
|
&data_connector_type_mappings,
|
||||||
object_types_with_permissions,
|
object_types_with_relationships,
|
||||||
&scalar_types,
|
&scalar_types,
|
||||||
&boolean_expression_types,
|
&boolean_expression_types,
|
||||||
&data_connectors,
|
&data_connectors,
|
||||||
|
@ -1,89 +1,100 @@
|
|||||||
use super::error::{Error, RelationshipError};
|
mod types;
|
||||||
use super::stages::{
|
pub use types::{
|
||||||
commands, data_connector_scalar_types, data_connectors, models, type_permissions,
|
ObjectTypeWithRelationships, Relationship, RelationshipCapabilities,
|
||||||
|
RelationshipCommandMapping, RelationshipExecutionCategory, RelationshipModelMapping,
|
||||||
|
RelationshipTarget, RelationshipTargetName,
|
||||||
};
|
};
|
||||||
use super::subgraph::Qualified;
|
|
||||||
use super::subgraph::QualifiedTypeReference;
|
|
||||||
use super::types::mk_name;
|
|
||||||
use super::types::NdcColumnForComparison;
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
use lang_graphql::ast::common as ast;
|
|
||||||
use open_dds::arguments::ArgumentName;
|
|
||||||
use open_dds::commands::CommandName;
|
|
||||||
|
|
||||||
use open_dds::models::ModelName;
|
use std::collections::HashMap;
|
||||||
use open_dds::relationships::{
|
|
||||||
self, FieldAccess, RelationshipName, RelationshipType, RelationshipV1,
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
|
use open_dds::{commands::CommandName, models::ModelName, types::CustomTypeName};
|
||||||
|
|
||||||
|
use crate::metadata::resolved::error::{Error, RelationshipError};
|
||||||
|
use crate::metadata::resolved::subgraph::Qualified;
|
||||||
|
|
||||||
|
use crate::metadata::resolved::stages::{
|
||||||
|
commands, data_connector_scalar_types, data_connector_type_mappings, data_connectors, models,
|
||||||
|
type_permissions,
|
||||||
};
|
};
|
||||||
use open_dds::types::CustomTypeName;
|
use crate::metadata::resolved::types::mk_name;
|
||||||
use open_dds::types::Deprecated;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use open_dds::relationships::{self, FieldAccess, RelationshipName, RelationshipV1};
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
/// resolve relationships
|
||||||
pub enum RelationshipTarget {
|
/// returns updated `types` value
|
||||||
Model {
|
pub fn resolve(
|
||||||
// TODO(Abhinav): Refactor resolved types to contain denormalized data (eg: actual resolved model)
|
metadata_accessor: &open_dds::accessor::MetadataAccessor,
|
||||||
model_name: Qualified<ModelName>,
|
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
||||||
relationship_type: RelationshipType,
|
object_types_with_permissions: &HashMap<
|
||||||
target_typename: Qualified<CustomTypeName>,
|
Qualified<CustomTypeName>,
|
||||||
mappings: Vec<RelationshipModelMapping>,
|
type_permissions::ObjectTypeWithPermissions,
|
||||||
|
>,
|
||||||
|
models: &IndexMap<Qualified<ModelName>, models::Model>,
|
||||||
|
commands: &IndexMap<Qualified<CommandName>, commands::Command>,
|
||||||
|
) -> Result<HashMap<Qualified<CustomTypeName>, ObjectTypeWithRelationships>, Error> {
|
||||||
|
let mut object_types_with_relationships = HashMap::new();
|
||||||
|
for (
|
||||||
|
object_type_name,
|
||||||
|
type_permissions::ObjectTypeWithPermissions {
|
||||||
|
type_output_permissions,
|
||||||
|
type_input_permissions,
|
||||||
|
object_type,
|
||||||
},
|
},
|
||||||
Command {
|
) in object_types_with_permissions
|
||||||
command_name: Qualified<CommandName>,
|
{
|
||||||
target_type: QualifiedTypeReference,
|
object_types_with_relationships.insert(
|
||||||
mappings: Vec<RelationshipCommandMapping>,
|
object_type_name.clone(),
|
||||||
|
ObjectTypeWithRelationships {
|
||||||
|
object_type: object_type.clone(),
|
||||||
|
type_output_permissions: type_output_permissions.clone(),
|
||||||
|
type_input_permissions: type_input_permissions.clone(),
|
||||||
|
relationships: IndexMap::new(),
|
||||||
},
|
},
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
for open_dds::accessor::QualifiedObject {
|
||||||
|
subgraph,
|
||||||
|
object: relationship,
|
||||||
|
} in &metadata_accessor.relationships
|
||||||
|
{
|
||||||
|
let qualified_relationship_source_type_name =
|
||||||
|
Qualified::new(subgraph.to_string(), relationship.source.to_owned());
|
||||||
|
let object_representation = object_types_with_relationships
|
||||||
|
.get_mut(&qualified_relationship_source_type_name)
|
||||||
|
.ok_or_else(|| Error::RelationshipDefinedOnUnknownType {
|
||||||
|
relationship_name: relationship.name.clone(),
|
||||||
|
type_name: qualified_relationship_source_type_name.clone(),
|
||||||
|
})?;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
let resolved_relationship = resolve_relationship(
|
||||||
pub enum RelationshipTargetName {
|
relationship,
|
||||||
Model(Qualified<ModelName>),
|
subgraph,
|
||||||
Command(Qualified<CommandName>),
|
models,
|
||||||
}
|
commands,
|
||||||
|
data_connectors,
|
||||||
|
&object_representation.object_type,
|
||||||
|
)?;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
if object_representation
|
||||||
pub struct RelationshipModelMapping {
|
.relationships
|
||||||
pub source_field: FieldAccess,
|
.insert(
|
||||||
pub target_field: FieldAccess,
|
resolved_relationship.field_name.clone(),
|
||||||
// Optional because we allow building schema without specifying a data source
|
resolved_relationship,
|
||||||
pub target_ndc_column: Option<NdcColumnForComparison>,
|
)
|
||||||
}
|
.is_some()
|
||||||
|
{
|
||||||
|
return Err(Error::DuplicateRelationshipInSourceType {
|
||||||
|
type_name: qualified_relationship_source_type_name,
|
||||||
|
relationship_name: relationship.name.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
Ok(object_types_with_relationships)
|
||||||
pub struct RelationshipCommandMapping {
|
|
||||||
pub source_field: FieldAccess,
|
|
||||||
pub argument_name: ArgumentName,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct Relationship {
|
|
||||||
pub name: RelationshipName,
|
|
||||||
// `ast::Name` representation of `RelationshipName`. This is used to avoid
|
|
||||||
// the recurring conversion between `RelationshipName` to `ast::Name` during
|
|
||||||
// relationship IR generation
|
|
||||||
pub field_name: ast::Name,
|
|
||||||
pub source: Qualified<CustomTypeName>,
|
|
||||||
pub target: RelationshipTarget,
|
|
||||||
pub target_capabilities: Option<RelationshipCapabilities>,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub deprecated: Option<Deprecated>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct RelationshipCapabilities {
|
|
||||||
// TODO: We don't handle relationships without foreach.
|
|
||||||
// Change this to a bool, when we support that
|
|
||||||
pub foreach: (),
|
|
||||||
pub relationships: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum RelationshipExecutionCategory {
|
|
||||||
// Push down relationship definition to the data connector
|
|
||||||
Local,
|
|
||||||
// Use foreach in the data connector to fetch related rows for multiple objects in a single request
|
|
||||||
RemoteForEach,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::match_single_binding)]
|
#[allow(clippy::match_single_binding)]
|
||||||
@ -110,7 +121,7 @@ pub fn relationship_execution_category(
|
|||||||
fn resolve_relationship_source_mapping<'a>(
|
fn resolve_relationship_source_mapping<'a>(
|
||||||
relationship_name: &'a RelationshipName,
|
relationship_name: &'a RelationshipName,
|
||||||
source_type_name: &'a Qualified<CustomTypeName>,
|
source_type_name: &'a Qualified<CustomTypeName>,
|
||||||
source_type: &type_permissions::ObjectTypeWithPermissions,
|
source_type: &data_connector_type_mappings::ObjectTypeRepresentation,
|
||||||
relationship_mapping: &'a open_dds::relationships::RelationshipMapping,
|
relationship_mapping: &'a open_dds::relationships::RelationshipMapping,
|
||||||
) -> Result<&'a FieldAccess, Error> {
|
) -> Result<&'a FieldAccess, Error> {
|
||||||
match &relationship_mapping.source {
|
match &relationship_mapping.source {
|
||||||
@ -125,11 +136,7 @@ fn resolve_relationship_source_mapping<'a>(
|
|||||||
relationship_name: relationship_name.clone(),
|
relationship_name: relationship_name.clone(),
|
||||||
}),
|
}),
|
||||||
[field_access] => {
|
[field_access] => {
|
||||||
if !source_type
|
if !source_type.fields.contains_key(&field_access.field_name) {
|
||||||
.object_type
|
|
||||||
.fields
|
|
||||||
.contains_key(&field_access.field_name)
|
|
||||||
{
|
|
||||||
return Err(Error::RelationshipError {
|
return Err(Error::RelationshipError {
|
||||||
relationship_error:
|
relationship_error:
|
||||||
RelationshipError::UnknownSourceFieldInRelationshipMapping {
|
RelationshipError::UnknownSourceFieldInRelationshipMapping {
|
||||||
@ -151,7 +158,7 @@ fn resolve_relationship_source_mapping<'a>(
|
|||||||
fn resolve_relationship_mappings_model(
|
fn resolve_relationship_mappings_model(
|
||||||
relationship: &RelationshipV1,
|
relationship: &RelationshipV1,
|
||||||
source_type_name: &Qualified<CustomTypeName>,
|
source_type_name: &Qualified<CustomTypeName>,
|
||||||
source_type: &type_permissions::ObjectTypeWithPermissions,
|
source_type: &data_connector_type_mappings::ObjectTypeRepresentation,
|
||||||
target_model: &models::Model,
|
target_model: &models::Model,
|
||||||
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
||||||
) -> Result<Vec<RelationshipModelMapping>, Error> {
|
) -> Result<Vec<RelationshipModelMapping>, Error> {
|
||||||
@ -255,7 +262,7 @@ fn resolve_relationship_mappings_model(
|
|||||||
fn resolve_relationship_mappings_command(
|
fn resolve_relationship_mappings_command(
|
||||||
relationship: &RelationshipV1,
|
relationship: &RelationshipV1,
|
||||||
source_type_name: &Qualified<CustomTypeName>,
|
source_type_name: &Qualified<CustomTypeName>,
|
||||||
source_type: &type_permissions::ObjectTypeWithPermissions,
|
source_type: &data_connector_type_mappings::ObjectTypeRepresentation,
|
||||||
target_command: &commands::Command,
|
target_command: &commands::Command,
|
||||||
) -> Result<Vec<RelationshipCommandMapping>, Error> {
|
) -> Result<Vec<RelationshipCommandMapping>, Error> {
|
||||||
let mut resolved_relationship_mappings = Vec::new();
|
let mut resolved_relationship_mappings = Vec::new();
|
||||||
@ -382,7 +389,7 @@ pub fn resolve_relationship(
|
|||||||
models: &IndexMap<Qualified<ModelName>, models::Model>,
|
models: &IndexMap<Qualified<ModelName>, models::Model>,
|
||||||
commands: &IndexMap<Qualified<CommandName>, commands::Command>,
|
commands: &IndexMap<Qualified<CommandName>, commands::Command>,
|
||||||
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
|
||||||
source_type: &type_permissions::ObjectTypeWithPermissions,
|
source_type: &data_connector_type_mappings::ObjectTypeRepresentation,
|
||||||
) -> Result<Relationship, Error> {
|
) -> Result<Relationship, Error> {
|
||||||
let source_type_name = Qualified::new(subgraph.to_string(), relationship.source.clone());
|
let source_type_name = Qualified::new(subgraph.to_string(), relationship.source.clone());
|
||||||
let (relationship_target, source_data_connector, target_name) = match &relationship.target {
|
let (relationship_target, source_data_connector, target_name) = match &relationship.target {
|
@ -0,0 +1,93 @@
|
|||||||
|
use crate::metadata::resolved::stages::{data_connector_type_mappings, type_permissions};
|
||||||
|
use crate::metadata::resolved::subgraph::{Qualified, QualifiedTypeReference};
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use open_dds::permissions::Role;
|
||||||
|
use open_dds::{commands::CommandName, models::ModelName, types::CustomTypeName};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::metadata::resolved::types::NdcColumnForComparison;
|
||||||
|
use lang_graphql::ast::common as ast;
|
||||||
|
use open_dds::arguments::ArgumentName;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use open_dds::relationships::{FieldAccess, RelationshipName, RelationshipType};
|
||||||
|
use open_dds::types::Deprecated;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, derive_more::Display)]
|
||||||
|
#[display(fmt = "Display")]
|
||||||
|
pub struct ObjectTypeWithRelationships {
|
||||||
|
pub object_type: data_connector_type_mappings::ObjectTypeRepresentation,
|
||||||
|
/// permissions on this type, when it is used in an output context (e.g. as
|
||||||
|
/// a return type of Model or Command)
|
||||||
|
pub type_output_permissions: HashMap<Role, open_dds::permissions::TypeOutputPermission>,
|
||||||
|
/// permissions on this type, when it is used in an input context (e.g. in
|
||||||
|
/// an argument type of Model or Command)
|
||||||
|
pub type_input_permissions: HashMap<Role, type_permissions::TypeInputPermission>,
|
||||||
|
/// any relationships defined on this object
|
||||||
|
pub relationships: IndexMap<ast::Name, Relationship>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum RelationshipTarget {
|
||||||
|
Model {
|
||||||
|
// TODO(Abhinav): Refactor resolved types to contain denormalized data (eg: actual resolved model)
|
||||||
|
model_name: Qualified<ModelName>,
|
||||||
|
relationship_type: RelationshipType,
|
||||||
|
target_typename: Qualified<CustomTypeName>,
|
||||||
|
mappings: Vec<RelationshipModelMapping>,
|
||||||
|
},
|
||||||
|
Command {
|
||||||
|
command_name: Qualified<CommandName>,
|
||||||
|
target_type: QualifiedTypeReference,
|
||||||
|
mappings: Vec<RelationshipCommandMapping>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum RelationshipTargetName {
|
||||||
|
Model(Qualified<ModelName>),
|
||||||
|
Command(Qualified<CommandName>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct RelationshipModelMapping {
|
||||||
|
pub source_field: FieldAccess,
|
||||||
|
pub target_field: FieldAccess,
|
||||||
|
// Optional because we allow building schema without specifying a data source
|
||||||
|
pub target_ndc_column: Option<NdcColumnForComparison>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct RelationshipCommandMapping {
|
||||||
|
pub source_field: FieldAccess,
|
||||||
|
pub argument_name: ArgumentName,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Relationship {
|
||||||
|
pub name: RelationshipName,
|
||||||
|
// `ast::Name` representation of `RelationshipName`. This is used to avoid
|
||||||
|
// the recurring conversion between `RelationshipName` to `ast::Name` during
|
||||||
|
// relationship IR generation
|
||||||
|
pub field_name: ast::Name,
|
||||||
|
pub source: Qualified<CustomTypeName>,
|
||||||
|
pub target: RelationshipTarget,
|
||||||
|
pub target_capabilities: Option<RelationshipCapabilities>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub deprecated: Option<Deprecated>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct RelationshipCapabilities {
|
||||||
|
// TODO: We don't handle relationships without foreach.
|
||||||
|
// Change this to a bool, when we support that
|
||||||
|
pub foreach: (),
|
||||||
|
pub relationships: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum RelationshipExecutionCategory {
|
||||||
|
// Push down relationship definition to the data connector
|
||||||
|
Local,
|
||||||
|
// Use foreach in the data connector to fetch related rows for multiple objects in a single request
|
||||||
|
RemoteForEach,
|
||||||
|
}
|
@ -7,11 +7,11 @@ use open_dds::{commands::CommandName, models::ModelName, types::CustomTypeName};
|
|||||||
|
|
||||||
use crate::metadata::resolved::subgraph::Qualified;
|
use crate::metadata::resolved::subgraph::Qualified;
|
||||||
|
|
||||||
use crate::metadata::resolved::stages::{commands, models, type_permissions};
|
use crate::metadata::resolved::stages::{commands, models, relationships};
|
||||||
|
|
||||||
/// Gather all roles from various permission objects.
|
/// Gather all roles from various permission objects.
|
||||||
pub fn resolve(
|
pub fn resolve(
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
|
||||||
models: &IndexMap<Qualified<ModelName>, models::Model>,
|
models: &IndexMap<Qualified<ModelName>, models::Model>,
|
||||||
commands: &IndexMap<Qualified<CommandName>, commands::Command>,
|
commands: &IndexMap<Qualified<CommandName>, commands::Command>,
|
||||||
) -> Vec<Role> {
|
) -> Vec<Role> {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use super::stages::{
|
use super::stages::{
|
||||||
boolean_expressions, data_connector_type_mappings, scalar_types, type_permissions,
|
boolean_expressions, data_connector_type_mappings, relationships, scalar_types,
|
||||||
|
type_permissions,
|
||||||
};
|
};
|
||||||
use crate::metadata::resolved::error::{BooleanExpressionError, Error};
|
use crate::metadata::resolved::error::{BooleanExpressionError, Error};
|
||||||
|
|
||||||
@ -53,26 +54,23 @@ pub fn resolve_field(
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// we do not want to store our types like this, but occasionally it is useful
|
/// we do not want to store our types like this, but occasionally it is useful
|
||||||
/// for pattern matching
|
/// for pattern matching
|
||||||
pub enum TypeRepresentation<'a> {
|
pub enum TypeRepresentation<'a, ObjectType> {
|
||||||
Scalar(&'a scalar_types::ScalarTypeRepresentation),
|
Scalar(&'a scalar_types::ScalarTypeRepresentation),
|
||||||
Object(&'a type_permissions::ObjectTypeWithPermissions),
|
Object(&'a ObjectType),
|
||||||
BooleanExpression(&'a boolean_expressions::ObjectBooleanExpressionType),
|
BooleanExpression(&'a boolean_expressions::ObjectBooleanExpressionType),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// validate whether a given CustomTypeName exists within `object_types`, `scalar_types` or
|
/// validate whether a given CustomTypeName exists within `object_types`, `scalar_types` or
|
||||||
/// `boolean_expression_types`
|
/// `boolean_expression_types`
|
||||||
pub fn get_type_representation<'a>(
|
pub fn get_type_representation<'a, ObjectType>(
|
||||||
custom_type_name: &Qualified<CustomTypeName>,
|
custom_type_name: &Qualified<CustomTypeName>,
|
||||||
object_types: &'a HashMap<
|
object_types: &'a HashMap<Qualified<CustomTypeName>, ObjectType>,
|
||||||
Qualified<CustomTypeName>,
|
|
||||||
type_permissions::ObjectTypeWithPermissions,
|
|
||||||
>,
|
|
||||||
scalar_types: &'a HashMap<Qualified<CustomTypeName>, scalar_types::ScalarTypeRepresentation>,
|
scalar_types: &'a HashMap<Qualified<CustomTypeName>, scalar_types::ScalarTypeRepresentation>,
|
||||||
boolean_expression_types: &'a HashMap<
|
boolean_expression_types: &'a HashMap<
|
||||||
Qualified<CustomTypeName>,
|
Qualified<CustomTypeName>,
|
||||||
boolean_expressions::ObjectBooleanExpressionType,
|
boolean_expressions::ObjectBooleanExpressionType,
|
||||||
>,
|
>,
|
||||||
) -> Result<TypeRepresentation<'a>, Error> {
|
) -> Result<TypeRepresentation<'a, ObjectType>, Error> {
|
||||||
match object_types.get(custom_type_name) {
|
match object_types.get(custom_type_name) {
|
||||||
Some(object_type_representation) => {
|
Some(object_type_representation) => {
|
||||||
Ok(TypeRepresentation::Object(object_type_representation))
|
Ok(TypeRepresentation::Object(object_type_representation))
|
||||||
@ -97,9 +95,9 @@ pub(crate) fn get_object_type_for_boolean_expression<'a>(
|
|||||||
boolean_expression_type: &boolean_expressions::ObjectBooleanExpressionType,
|
boolean_expression_type: &boolean_expressions::ObjectBooleanExpressionType,
|
||||||
object_types: &'a HashMap<
|
object_types: &'a HashMap<
|
||||||
Qualified<CustomTypeName>,
|
Qualified<CustomTypeName>,
|
||||||
type_permissions::ObjectTypeWithPermissions,
|
relationships::ObjectTypeWithRelationships,
|
||||||
>,
|
>,
|
||||||
) -> Result<&'a type_permissions::ObjectTypeWithPermissions, Error> {
|
) -> Result<&'a relationships::ObjectTypeWithRelationships, Error> {
|
||||||
object_types
|
object_types
|
||||||
.get(&boolean_expression_type.object_type)
|
.get(&boolean_expression_type.object_type)
|
||||||
.ok_or(Error::from(
|
.ok_or(Error::from(
|
||||||
@ -112,9 +110,9 @@ pub(crate) fn get_object_type_for_boolean_expression<'a>(
|
|||||||
// Get the underlying object type by resolving Custom ObjectType, Array and
|
// Get the underlying object type by resolving Custom ObjectType, Array and
|
||||||
// Nullable container types
|
// Nullable container types
|
||||||
// check that `custom_type_name` exists in `object_types`
|
// check that `custom_type_name` exists in `object_types`
|
||||||
pub fn object_type_exists(
|
pub fn object_type_exists<ObjectType>(
|
||||||
custom_type_name: &Qualified<CustomTypeName>,
|
custom_type_name: &Qualified<CustomTypeName>,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, ObjectType>,
|
||||||
) -> Result<Qualified<CustomTypeName>, Error> {
|
) -> Result<Qualified<CustomTypeName>, Error> {
|
||||||
object_types
|
object_types
|
||||||
.get(custom_type_name)
|
.get(custom_type_name)
|
||||||
|
@ -8,9 +8,6 @@ use super::types::output_type::get_object_type_representation;
|
|||||||
use super::types::output_type::relationship::{FilterRelationshipAnnotation, ModelTargetSource};
|
use super::types::output_type::relationship::{FilterRelationshipAnnotation, ModelTargetSource};
|
||||||
use super::types::{BooleanExpressionAnnotation, InputAnnotation, TypeId};
|
use super::types::{BooleanExpressionAnnotation, InputAnnotation, TypeId};
|
||||||
use crate::metadata::resolved;
|
use crate::metadata::resolved;
|
||||||
use crate::metadata::resolved::relationship::{
|
|
||||||
relationship_execution_category, RelationshipExecutionCategory, RelationshipTarget,
|
|
||||||
};
|
|
||||||
use crate::metadata::resolved::subgraph::Qualified;
|
use crate::metadata::resolved::subgraph::Qualified;
|
||||||
use crate::metadata::resolved::types::mk_name;
|
use crate::metadata::resolved::types::mk_name;
|
||||||
|
|
||||||
@ -143,9 +140,8 @@ pub fn build_boolean_expression_input_schema(
|
|||||||
|
|
||||||
// relationship fields
|
// relationship fields
|
||||||
// TODO(naveen): Add support for command relationships
|
// TODO(naveen): Add support for command relationships
|
||||||
for (rel_name, relationship) in object_type_representation.object_type.relationships.iter()
|
for (rel_name, relationship) in object_type_representation.relationships.iter() {
|
||||||
{
|
if let resolved::RelationshipTarget::Model {
|
||||||
if let RelationshipTarget::Model {
|
|
||||||
model_name,
|
model_name,
|
||||||
relationship_type,
|
relationship_type,
|
||||||
target_typename,
|
target_typename,
|
||||||
@ -168,11 +164,13 @@ pub fn build_boolean_expression_input_schema(
|
|||||||
ModelTargetSource::from_model_source(target_source, relationship)?;
|
ModelTargetSource::from_model_source(target_source, relationship)?;
|
||||||
|
|
||||||
// filter expression with relationships is currently only supported for local relationships
|
// filter expression with relationships is currently only supported for local relationships
|
||||||
if let RelationshipExecutionCategory::Local = relationship_execution_category(
|
if let resolved::RelationshipExecutionCategory::Local =
|
||||||
|
resolved::relationship_execution_category(
|
||||||
&boolean_expression_type.data_connector_link,
|
&boolean_expression_type.data_connector_link,
|
||||||
&target_source.data_connector,
|
&target_source.data_connector,
|
||||||
&target_model_source.capabilities,
|
&target_model_source.capabilities,
|
||||||
) {
|
)
|
||||||
|
{
|
||||||
if target_source.data_connector.name
|
if target_source.data_connector.name
|
||||||
== boolean_expression_type.data_connector_name
|
== boolean_expression_type.data_connector_name
|
||||||
{
|
{
|
||||||
|
@ -8,9 +8,6 @@ use std::collections::{BTreeMap, HashMap};
|
|||||||
use super::types::output_type::relationship::{ModelTargetSource, OrderByRelationshipAnnotation};
|
use super::types::output_type::relationship::{ModelTargetSource, OrderByRelationshipAnnotation};
|
||||||
use super::types::{output_type::get_object_type_representation, Annotation, TypeId};
|
use super::types::{output_type::get_object_type_representation, Annotation, TypeId};
|
||||||
use crate::metadata::resolved;
|
use crate::metadata::resolved;
|
||||||
use crate::metadata::resolved::relationship::{
|
|
||||||
relationship_execution_category, RelationshipExecutionCategory, RelationshipTarget,
|
|
||||||
};
|
|
||||||
use crate::metadata::resolved::subgraph::Qualified;
|
use crate::metadata::resolved::subgraph::Qualified;
|
||||||
use crate::metadata::resolved::types::mk_name;
|
use crate::metadata::resolved::types::mk_name;
|
||||||
use crate::schema::permissions;
|
use crate::schema::permissions;
|
||||||
@ -159,9 +156,8 @@ pub fn build_model_order_by_input_schema(
|
|||||||
|
|
||||||
// relationship fields
|
// relationship fields
|
||||||
// TODO(naveen): Add support for command relationships.
|
// TODO(naveen): Add support for command relationships.
|
||||||
for (rel_name, relationship) in object_type_representation.object_type.relationships.iter()
|
for (rel_name, relationship) in object_type_representation.relationships.iter() {
|
||||||
{
|
if let resolved::RelationshipTarget::Model {
|
||||||
if let RelationshipTarget::Model {
|
|
||||||
model_name,
|
model_name,
|
||||||
relationship_type,
|
relationship_type,
|
||||||
target_typename,
|
target_typename,
|
||||||
@ -185,11 +181,13 @@ pub fn build_model_order_by_input_schema(
|
|||||||
let target_model_source =
|
let target_model_source =
|
||||||
ModelTargetSource::from_model_source(target_source, relationship)?;
|
ModelTargetSource::from_model_source(target_source, relationship)?;
|
||||||
// order_by expression with relationships is currently only supported for local relationships
|
// order_by expression with relationships is currently only supported for local relationships
|
||||||
if let RelationshipExecutionCategory::Local = relationship_execution_category(
|
if let resolved::RelationshipExecutionCategory::Local =
|
||||||
|
resolved::relationship_execution_category(
|
||||||
&model_source.data_connector,
|
&model_source.data_connector,
|
||||||
&target_source.data_connector,
|
&target_source.data_connector,
|
||||||
&target_model_source.capabilities,
|
&target_model_source.capabilities,
|
||||||
) {
|
)
|
||||||
|
{
|
||||||
// TODO(naveen): Support Array relationships in order_by when the support for aggregates is implemented
|
// TODO(naveen): Support Array relationships in order_by when the support for aggregates is implemented
|
||||||
if let RelationshipType::Object = relationship_type {
|
if let RelationshipType::Object = relationship_type {
|
||||||
// If the relationship target model does not have orderByExpressionType do not include
|
// If the relationship target model does not have orderByExpressionType do not include
|
||||||
|
@ -2,7 +2,9 @@ use open_dds::types::{CustomTypeName, FieldName};
|
|||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
use crate::metadata::resolved::permission::ValueExpression;
|
use crate::metadata::resolved::permission::ValueExpression;
|
||||||
use crate::metadata::resolved::stages::{data_connector_type_mappings, models, type_permissions};
|
use crate::metadata::resolved::stages::{
|
||||||
|
data_connector_type_mappings, models, relationships, type_permissions,
|
||||||
|
};
|
||||||
use crate::metadata::resolved::subgraph::{Qualified, QualifiedTypeReference};
|
use crate::metadata::resolved::subgraph::{Qualified, QualifiedTypeReference};
|
||||||
use crate::metadata::resolved::types::{object_type_exists, unwrap_custom_type_name};
|
use crate::metadata::resolved::types::{object_type_exists, unwrap_custom_type_name};
|
||||||
use crate::metadata::resolved::{self};
|
use crate::metadata::resolved::{self};
|
||||||
@ -14,7 +16,7 @@ use super::types::ArgumentNameAndPath;
|
|||||||
/// Build namespace annotation for select permissions
|
/// Build namespace annotation for select permissions
|
||||||
pub(crate) fn get_select_permissions_namespace_annotations(
|
pub(crate) fn get_select_permissions_namespace_annotations(
|
||||||
model: &models::Model,
|
model: &models::Model,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, resolved::ObjectTypeWithRelationships>,
|
||||||
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, schema::Error> {
|
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, schema::Error> {
|
||||||
let mut permissions: HashMap<Role, Option<types::NamespaceAnnotation>> = model
|
let mut permissions: HashMap<Role, Option<types::NamespaceAnnotation>> = model
|
||||||
.select_permissions
|
.select_permissions
|
||||||
@ -97,9 +99,9 @@ pub(crate) fn get_select_permissions_namespace_annotations(
|
|||||||
/// as we need to check the permissions of the arguments used in the selection.
|
/// as we need to check the permissions of the arguments used in the selection.
|
||||||
pub(crate) fn get_select_one_namespace_annotations(
|
pub(crate) fn get_select_one_namespace_annotations(
|
||||||
model: &models::Model,
|
model: &models::Model,
|
||||||
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
|
object_type_representation: &resolved::ObjectTypeWithRelationships,
|
||||||
select_unique: &models::SelectUniqueGraphQlDefinition,
|
select_unique: &models::SelectUniqueGraphQlDefinition,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, resolved::ObjectTypeWithRelationships>,
|
||||||
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, schema::Error> {
|
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, schema::Error> {
|
||||||
let select_permissions = get_select_permissions_namespace_annotations(model, object_types)?;
|
let select_permissions = get_select_permissions_namespace_annotations(model, object_types)?;
|
||||||
|
|
||||||
@ -120,10 +122,10 @@ pub(crate) fn get_select_one_namespace_annotations(
|
|||||||
/// in the relationship mappings.
|
/// in the relationship mappings.
|
||||||
pub(crate) fn get_model_relationship_namespace_annotations(
|
pub(crate) fn get_model_relationship_namespace_annotations(
|
||||||
target_model: &models::Model,
|
target_model: &models::Model,
|
||||||
source_object_type_representation: &type_permissions::ObjectTypeWithPermissions,
|
source_object_type_representation: &resolved::ObjectTypeWithRelationships,
|
||||||
target_object_type_representation: &type_permissions::ObjectTypeWithPermissions,
|
target_object_type_representation: &resolved::ObjectTypeWithRelationships,
|
||||||
mappings: &[resolved::relationship::RelationshipModelMapping],
|
mappings: &[resolved::RelationshipModelMapping],
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
|
||||||
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, schema::Error> {
|
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, schema::Error> {
|
||||||
let select_permissions =
|
let select_permissions =
|
||||||
get_select_permissions_namespace_annotations(target_model, object_types)?;
|
get_select_permissions_namespace_annotations(target_model, object_types)?;
|
||||||
@ -147,7 +149,7 @@ pub(crate) fn get_model_relationship_namespace_annotations(
|
|||||||
/// Build namespace annotation for commands
|
/// Build namespace annotation for commands
|
||||||
pub(crate) fn get_command_namespace_annotations(
|
pub(crate) fn get_command_namespace_annotations(
|
||||||
command: &resolved::Command,
|
command: &resolved::Command,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, resolved::ObjectTypeWithRelationships>,
|
||||||
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, crate::schema::Error> {
|
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, crate::schema::Error> {
|
||||||
let mut permissions = HashMap::new();
|
let mut permissions = HashMap::new();
|
||||||
|
|
||||||
@ -224,16 +226,16 @@ fn build_annotations_from_input_object_type_permissions(
|
|||||||
field_path: &mut [String],
|
field_path: &mut [String],
|
||||||
type_reference: &QualifiedTypeReference,
|
type_reference: &QualifiedTypeReference,
|
||||||
ndc_argument_name: &Option<String>,
|
ndc_argument_name: &Option<String>,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, resolved::ObjectTypeWithRelationships>,
|
||||||
type_mappings: &BTreeMap<Qualified<CustomTypeName>, data_connector_type_mappings::TypeMapping>,
|
type_mappings: &BTreeMap<Qualified<CustomTypeName>, data_connector_type_mappings::TypeMapping>,
|
||||||
role_presets_map: &mut HashMap<Role, types::ArgumentPresets>,
|
role_presets_map: &mut HashMap<Role, types::ArgumentPresets>,
|
||||||
) -> Result<(), schema::Error> {
|
) -> Result<(), schema::Error> {
|
||||||
if let Some(custom_typename) = unwrap_custom_type_name(type_reference) {
|
if let Some(object_type) = unwrap_custom_type_name(type_reference) {
|
||||||
if let Ok(object_type) = object_type_exists(custom_typename, object_types) {
|
if object_type_exists(object_type, object_types).is_ok() {
|
||||||
if let Some(object_type_repr) = object_types.get(&object_type) {
|
if let Some(object_type_repr) = object_types.get(object_type) {
|
||||||
let field_mappings =
|
let field_mappings =
|
||||||
type_mappings
|
type_mappings
|
||||||
.get(&object_type)
|
.get(object_type)
|
||||||
.map(|type_mapping| match type_mapping {
|
.map(|type_mapping| match type_mapping {
|
||||||
data_connector_type_mappings::TypeMapping::Object {
|
data_connector_type_mappings::TypeMapping::Object {
|
||||||
ndc_object_type_name: _,
|
ndc_object_type_name: _,
|
||||||
@ -248,7 +250,7 @@ fn build_annotations_from_input_object_type_permissions(
|
|||||||
type_reference,
|
type_reference,
|
||||||
field_path,
|
field_path,
|
||||||
ndc_argument_name,
|
ndc_argument_name,
|
||||||
&object_type,
|
object_type,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
role_presets_map.insert(
|
role_presets_map.insert(
|
||||||
@ -359,9 +361,9 @@ fn build_preset_map_from_input_object_type_permission(
|
|||||||
/// in the relationship mappings.
|
/// in the relationship mappings.
|
||||||
pub(crate) fn get_command_relationship_namespace_annotations(
|
pub(crate) fn get_command_relationship_namespace_annotations(
|
||||||
command: &resolved::Command,
|
command: &resolved::Command,
|
||||||
source_object_type_representation: &type_permissions::ObjectTypeWithPermissions,
|
source_object_type_representation: &resolved::ObjectTypeWithRelationships,
|
||||||
mappings: &[resolved::relationship::RelationshipCommandMapping],
|
mappings: &[resolved::RelationshipCommandMapping],
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, resolved::ObjectTypeWithRelationships>,
|
||||||
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, crate::schema::Error> {
|
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, crate::schema::Error> {
|
||||||
let select_permissions = get_command_namespace_annotations(command, object_types)?;
|
let select_permissions = get_command_namespace_annotations(command, object_types)?;
|
||||||
|
|
||||||
@ -384,7 +386,7 @@ pub(crate) fn get_command_relationship_namespace_annotations(
|
|||||||
/// for a role if the role has access (select permissions)
|
/// for a role if the role has access (select permissions)
|
||||||
/// to all the Global ID fields.
|
/// to all the Global ID fields.
|
||||||
pub(crate) fn get_node_interface_annotations(
|
pub(crate) fn get_node_interface_annotations(
|
||||||
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
|
object_type_representation: &resolved::ObjectTypeWithRelationships,
|
||||||
) -> HashMap<Role, Option<types::NamespaceAnnotation>> {
|
) -> HashMap<Role, Option<types::NamespaceAnnotation>> {
|
||||||
let mut permissions = HashMap::new();
|
let mut permissions = HashMap::new();
|
||||||
for (role, type_output_permission) in &object_type_representation.type_output_permissions {
|
for (role, type_output_permission) in &object_type_representation.type_output_permissions {
|
||||||
@ -405,7 +407,7 @@ pub(crate) fn get_node_interface_annotations(
|
|||||||
/// for a role if the role has access (select permissions)
|
/// for a role if the role has access (select permissions)
|
||||||
/// to all the key fields.
|
/// to all the key fields.
|
||||||
pub(crate) fn get_entity_union_permissions(
|
pub(crate) fn get_entity_union_permissions(
|
||||||
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
|
object_type_representation: &resolved::ObjectTypeWithRelationships,
|
||||||
) -> HashMap<Role, Option<types::NamespaceAnnotation>> {
|
) -> HashMap<Role, Option<types::NamespaceAnnotation>> {
|
||||||
let mut permissions = HashMap::new();
|
let mut permissions = HashMap::new();
|
||||||
for (role, type_output_permission) in &object_type_representation.type_output_permissions {
|
for (role, type_output_permission) in &object_type_representation.type_output_permissions {
|
||||||
@ -423,7 +425,7 @@ pub(crate) fn get_entity_union_permissions(
|
|||||||
|
|
||||||
/// Build namespace annotations for each field based on the type permissions
|
/// Build namespace annotations for each field based on the type permissions
|
||||||
pub(crate) fn get_allowed_roles_for_field<'a>(
|
pub(crate) fn get_allowed_roles_for_field<'a>(
|
||||||
object_type_representation: &'a type_permissions::ObjectTypeWithPermissions,
|
object_type_representation: &'a resolved::ObjectTypeWithRelationships,
|
||||||
field_name: &'a FieldName,
|
field_name: &'a FieldName,
|
||||||
) -> impl Iterator<Item = &'a Role> {
|
) -> impl Iterator<Item = &'a Role> {
|
||||||
object_type_representation
|
object_type_representation
|
||||||
@ -440,7 +442,7 @@ pub(crate) fn get_allowed_roles_for_field<'a>(
|
|||||||
|
|
||||||
/// Builds namespace annotations for the `node` field.
|
/// Builds namespace annotations for the `node` field.
|
||||||
pub(crate) fn get_node_field_namespace_permissions(
|
pub(crate) fn get_node_field_namespace_permissions(
|
||||||
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
|
object_type_representation: &resolved::ObjectTypeWithRelationships,
|
||||||
model: &models::Model,
|
model: &models::Model,
|
||||||
) -> HashMap<Role, resolved::FilterPermission> {
|
) -> HashMap<Role, resolved::FilterPermission> {
|
||||||
let mut permissions = HashMap::new();
|
let mut permissions = HashMap::new();
|
||||||
@ -479,7 +481,7 @@ pub(crate) fn get_node_field_namespace_permissions(
|
|||||||
|
|
||||||
/// Builds namespace annotations for the `_entities` field.
|
/// Builds namespace annotations for the `_entities` field.
|
||||||
pub(crate) fn get_entities_field_namespace_permissions(
|
pub(crate) fn get_entities_field_namespace_permissions(
|
||||||
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
|
object_type_representation: &resolved::ObjectTypeWithRelationships,
|
||||||
model: &models::Model,
|
model: &models::Model,
|
||||||
) -> HashMap<Role, resolved::FilterPermission> {
|
) -> HashMap<Role, resolved::FilterPermission> {
|
||||||
let mut permissions = HashMap::new();
|
let mut permissions = HashMap::new();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
metadata::resolved::{
|
metadata::resolved::{
|
||||||
stages::{data_connector_type_mappings, type_permissions},
|
self,
|
||||||
|
stages::data_connector_type_mappings,
|
||||||
subgraph::{Qualified, QualifiedBaseType, QualifiedTypeName, QualifiedTypeReference},
|
subgraph::{Qualified, QualifiedBaseType, QualifiedTypeName, QualifiedTypeReference},
|
||||||
types::{get_type_representation, mk_name, TypeRepresentation},
|
types::{get_type_representation, mk_name, TypeRepresentation},
|
||||||
},
|
},
|
||||||
@ -80,7 +81,7 @@ fn get_custom_input_type(
|
|||||||
.map_err(|_| crate::schema::Error::InternalTypeNotFound {
|
.map_err(|_| crate::schema::Error::InternalTypeNotFound {
|
||||||
type_name: gds_type_name.clone(),
|
type_name: gds_type_name.clone(),
|
||||||
})? {
|
})? {
|
||||||
TypeRepresentation::Object(type_permissions::ObjectTypeWithPermissions {
|
TypeRepresentation::Object(resolved::ObjectTypeWithRelationships {
|
||||||
object_type:
|
object_type:
|
||||||
data_connector_type_mappings::ObjectTypeRepresentation {
|
data_connector_type_mappings::ObjectTypeRepresentation {
|
||||||
graphql_input_type_name,
|
graphql_input_type_name,
|
||||||
@ -125,7 +126,7 @@ fn get_custom_input_type(
|
|||||||
fn input_object_type_input_fields(
|
fn input_object_type_input_fields(
|
||||||
gds: &GDS,
|
gds: &GDS,
|
||||||
builder: &mut gql_schema::Builder<GDS>,
|
builder: &mut gql_schema::Builder<GDS>,
|
||||||
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
|
object_type_representation: &resolved::ObjectTypeWithRelationships,
|
||||||
) -> 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> {
|
||||||
object_type_representation
|
object_type_representation
|
||||||
.object_type
|
.object_type
|
||||||
|
@ -14,7 +14,7 @@ use self::relationship::{
|
|||||||
};
|
};
|
||||||
use super::inbuilt_type::base_type_container_for_inbuilt_type;
|
use super::inbuilt_type::base_type_container_for_inbuilt_type;
|
||||||
use super::{Annotation, PossibleApolloFederationTypes, TypeId};
|
use super::{Annotation, PossibleApolloFederationTypes, TypeId};
|
||||||
use crate::metadata::resolved::stages::{data_connector_type_mappings, type_permissions};
|
use crate::metadata::resolved::stages::data_connector_type_mappings;
|
||||||
use crate::metadata::resolved::subgraph::{
|
use crate::metadata::resolved::subgraph::{
|
||||||
Qualified, QualifiedBaseType, QualifiedTypeName, QualifiedTypeReference,
|
Qualified, QualifiedBaseType, QualifiedTypeName, QualifiedTypeReference,
|
||||||
};
|
};
|
||||||
@ -190,8 +190,8 @@ fn object_type_fields(
|
|||||||
gds: &GDS,
|
gds: &GDS,
|
||||||
builder: &mut gql_schema::Builder<GDS>,
|
builder: &mut gql_schema::Builder<GDS>,
|
||||||
type_name: &Qualified<CustomTypeName>,
|
type_name: &Qualified<CustomTypeName>,
|
||||||
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
|
object_type_representation: &resolved::ObjectTypeWithRelationships,
|
||||||
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
|
object_types: &HashMap<Qualified<CustomTypeName>, resolved::ObjectTypeWithRelationships>,
|
||||||
) -> Result<BTreeMap<ast::Name, gql_schema::Namespaced<GDS, gql_schema::Field<GDS>>>, Error> {
|
) -> Result<BTreeMap<ast::Name, gql_schema::Namespaced<GDS, gql_schema::Field<GDS>>>, Error> {
|
||||||
let mut graphql_fields = object_type_representation
|
let mut graphql_fields = object_type_representation
|
||||||
.object_type
|
.object_type
|
||||||
@ -225,13 +225,11 @@ fn object_type_fields(
|
|||||||
Ok((graphql_field_name, namespaced_field))
|
Ok((graphql_field_name, namespaced_field))
|
||||||
})
|
})
|
||||||
.collect::<Result<BTreeMap<_, _>, _>>()?;
|
.collect::<Result<BTreeMap<_, _>, _>>()?;
|
||||||
for (relationship_field_name, relationship) in
|
for (relationship_field_name, relationship) in &object_type_representation.relationships {
|
||||||
&object_type_representation.object_type.relationships
|
|
||||||
{
|
|
||||||
let deprecation_status = mk_deprecation_status(&relationship.deprecated);
|
let deprecation_status = mk_deprecation_status(&relationship.deprecated);
|
||||||
|
|
||||||
let relationship_field = match &relationship.target {
|
let relationship_field = match &relationship.target {
|
||||||
resolved::relationship::RelationshipTarget::Command {
|
resolved::RelationshipTarget::Command {
|
||||||
command_name,
|
command_name,
|
||||||
target_type,
|
target_type,
|
||||||
mappings,
|
mappings,
|
||||||
@ -292,7 +290,7 @@ fn object_type_fields(
|
|||||||
)?,
|
)?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
resolved::relationship::RelationshipTarget::Model {
|
resolved::RelationshipTarget::Model {
|
||||||
model_name,
|
model_name,
|
||||||
relationship_type,
|
relationship_type,
|
||||||
target_typename,
|
target_typename,
|
||||||
@ -516,7 +514,7 @@ pub fn output_type_schema(
|
|||||||
pub(crate) fn get_object_type_representation<'s>(
|
pub(crate) fn get_object_type_representation<'s>(
|
||||||
gds: &'s GDS,
|
gds: &'s GDS,
|
||||||
gds_type: &Qualified<CustomTypeName>,
|
gds_type: &Qualified<CustomTypeName>,
|
||||||
) -> Result<&'s type_permissions::ObjectTypeWithPermissions, crate::schema::Error> {
|
) -> Result<&'s resolved::ObjectTypeWithRelationships, crate::schema::Error> {
|
||||||
gds.metadata.object_types.get(gds_type).ok_or_else(|| {
|
gds.metadata.object_types.get(gds_type).ok_or_else(|| {
|
||||||
crate::schema::Error::InternalTypeNotFound {
|
crate::schema::Error::InternalTypeNotFound {
|
||||||
type_name: gds_type.clone(),
|
type_name: gds_type.clone(),
|
||||||
|
@ -32,7 +32,7 @@ pub struct ModelRelationshipAnnotation {
|
|||||||
pub target_source: Option<ModelTargetSource>,
|
pub target_source: Option<ModelTargetSource>,
|
||||||
pub target_type: Qualified<CustomTypeName>,
|
pub target_type: Qualified<CustomTypeName>,
|
||||||
pub relationship_type: RelationshipType,
|
pub relationship_type: RelationshipType,
|
||||||
pub mappings: Vec<resolved::relationship::RelationshipModelMapping>,
|
pub mappings: Vec<resolved::RelationshipModelMapping>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
@ -49,7 +49,7 @@ pub struct FilterRelationshipAnnotation {
|
|||||||
pub target_source: ModelTargetSource,
|
pub target_source: ModelTargetSource,
|
||||||
pub target_type: Qualified<CustomTypeName>,
|
pub target_type: Qualified<CustomTypeName>,
|
||||||
pub target_model_name: Qualified<ModelName>,
|
pub target_model_name: Qualified<ModelName>,
|
||||||
pub mappings: Vec<resolved::relationship::RelationshipModelMapping>,
|
pub mappings: Vec<resolved::RelationshipModelMapping>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
@ -66,7 +66,7 @@ pub struct OrderByRelationshipAnnotation {
|
|||||||
pub target_source: ModelTargetSource,
|
pub target_source: ModelTargetSource,
|
||||||
pub target_type: Qualified<CustomTypeName>,
|
pub target_type: Qualified<CustomTypeName>,
|
||||||
pub target_model_name: Qualified<ModelName>,
|
pub target_model_name: Qualified<ModelName>,
|
||||||
pub mappings: Vec<resolved::relationship::RelationshipModelMapping>,
|
pub mappings: Vec<resolved::RelationshipModelMapping>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
@ -83,19 +83,19 @@ pub struct PredicateRelationshipAnnotation {
|
|||||||
pub target_source: ModelTargetSource,
|
pub target_source: ModelTargetSource,
|
||||||
pub target_type: Qualified<CustomTypeName>,
|
pub target_type: Qualified<CustomTypeName>,
|
||||||
pub target_model_name: Qualified<ModelName>,
|
pub target_model_name: Qualified<ModelName>,
|
||||||
pub mappings: Vec<resolved::relationship::RelationshipModelMapping>,
|
pub mappings: Vec<resolved::RelationshipModelMapping>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct ModelTargetSource {
|
pub struct ModelTargetSource {
|
||||||
pub(crate) model: models::ModelSource,
|
pub(crate) model: models::ModelSource,
|
||||||
pub(crate) capabilities: resolved::relationship::RelationshipCapabilities,
|
pub(crate) capabilities: resolved::RelationshipCapabilities,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModelTargetSource {
|
impl ModelTargetSource {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
model: &models::Model,
|
model: &models::Model,
|
||||||
relationship: &resolved::relationship::Relationship,
|
relationship: &resolved::Relationship,
|
||||||
) -> Result<Option<Self>, schema::Error> {
|
) -> Result<Option<Self>, schema::Error> {
|
||||||
model
|
model
|
||||||
.source
|
.source
|
||||||
@ -106,7 +106,7 @@ impl ModelTargetSource {
|
|||||||
|
|
||||||
pub fn from_model_source(
|
pub fn from_model_source(
|
||||||
model_source: &models::ModelSource,
|
model_source: &models::ModelSource,
|
||||||
relationship: &resolved::relationship::Relationship,
|
relationship: &resolved::Relationship,
|
||||||
) -> Result<Self, schema::Error> {
|
) -> Result<Self, schema::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
model: model_source.clone(),
|
model: model_source.clone(),
|
||||||
@ -130,20 +130,20 @@ pub struct CommandRelationshipAnnotation {
|
|||||||
pub target_source: Option<CommandTargetSource>,
|
pub target_source: Option<CommandTargetSource>,
|
||||||
pub target_type: QualifiedTypeReference,
|
pub target_type: QualifiedTypeReference,
|
||||||
pub target_base_type_kind: TypeKind,
|
pub target_base_type_kind: TypeKind,
|
||||||
pub mappings: Vec<resolved::relationship::RelationshipCommandMapping>,
|
pub mappings: Vec<resolved::RelationshipCommandMapping>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct CommandTargetSource {
|
pub struct CommandTargetSource {
|
||||||
pub(crate) details: CommandSourceDetail,
|
pub(crate) details: CommandSourceDetail,
|
||||||
pub(crate) function_name: FunctionName,
|
pub(crate) function_name: FunctionName,
|
||||||
pub(crate) capabilities: resolved::relationship::RelationshipCapabilities,
|
pub(crate) capabilities: resolved::RelationshipCapabilities,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandTargetSource {
|
impl CommandTargetSource {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
command: &resolved::Command,
|
command: &resolved::Command,
|
||||||
relationship: &resolved::relationship::Relationship,
|
relationship: &resolved::Relationship,
|
||||||
) -> Result<Option<Self>, schema::Error> {
|
) -> Result<Option<Self>, schema::Error> {
|
||||||
command
|
command
|
||||||
.source
|
.source
|
||||||
|
Loading…
Reference in New Issue
Block a user