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:
Daniel Harvey 2024-04-29 15:56:42 +01:00 committed by hasura-bot
parent b1149c26de
commit 847f81ad96
19 changed files with 326 additions and 287 deletions

View File

@ -23,10 +23,7 @@ use super::{
};
use crate::execute::model_tracking::{count_model, UsagesCounts};
use crate::metadata::resolved::{
relationship::{relationship_execution_category, RelationshipExecutionCategory},
subgraph::serialize_qualified_btreemap,
};
use crate::metadata::resolved::subgraph::serialize_qualified_btreemap;
use crate::schema::types::output_type::relationship::{
ModelRelationshipAnnotation, ModelTargetSource,
};
@ -54,7 +51,7 @@ pub(crate) struct LocalModelRelationshipInfo<'s> {
pub source_type_mappings: &'s BTreeMap<Qualified<CustomTypeName>, resolved::TypeMapping>,
pub target_source: &'s ModelTargetSource,
pub target_type: &'s Qualified<CustomTypeName>,
pub mappings: &'s Vec<resolved::relationship::RelationshipModelMapping>,
pub mappings: &'s Vec<resolved::RelationshipModelMapping>,
}
#[derive(Debug, Serialize)]
@ -100,19 +97,19 @@ pub(crate) fn process_model_relationship_definition(
} = relationship_info;
let mut column_mapping = BTreeMap::new();
for resolved::relationship::RelationshipModelMapping {
for resolved::RelationshipModelMapping {
source_field: source_field_path,
target_field: _,
target_ndc_column,
} in mappings.iter()
{
if !matches!(
relationship_execution_category(
resolved::relationship_execution_category(
source_data_connector,
&target_source.model.data_connector,
&target_source.capabilities
),
RelationshipExecutionCategory::Local
resolved::RelationshipExecutionCategory::Local
) {
Err(error::InternalEngineError::RemoteRelationshipsAreNotSupported)?
} else {
@ -165,18 +162,18 @@ pub(crate) fn process_command_relationship_definition(
} = relationship_info;
let mut arguments = BTreeMap::new();
for resolved::relationship::RelationshipCommandMapping {
for resolved::RelationshipCommandMapping {
source_field: source_field_path,
argument_name: target_argument,
} in annotation.mappings.iter()
{
if !matches!(
relationship_execution_category(
resolved::relationship_execution_category(
source_data_connector,
&target_source.details.data_connector,
&target_source.capabilities
),
RelationshipExecutionCategory::Local
resolved::RelationshipExecutionCategory::Local
) {
Err(error::InternalEngineError::RemoteRelationshipsAreNotSupported)?
} else {
@ -293,12 +290,12 @@ pub(crate) fn generate_model_relationship_ir<'s>(
}
None => error::Error::from(normalized_ast::Error::NoTypenameFound),
})?;
match relationship_execution_category(
match resolved::relationship_execution_category(
source_data_connector,
&target_source.model.data_connector,
&target_source.capabilities,
) {
RelationshipExecutionCategory::Local => build_local_model_relationship(
resolved::RelationshipExecutionCategory::Local => build_local_model_relationship(
field,
field_call,
annotation,
@ -312,7 +309,7 @@ pub(crate) fn generate_model_relationship_ir<'s>(
session_variables,
usage_counts,
),
RelationshipExecutionCategory::RemoteForEach => build_remote_relationship(
resolved::RelationshipExecutionCategory::RemoteForEach => build_remote_relationship(
field,
field_call,
annotation,
@ -353,12 +350,12 @@ pub(crate) fn generate_command_relationship_ir<'s>(
None => error::Error::from(normalized_ast::Error::NoTypenameFound),
})?;
match relationship_execution_category(
match resolved::relationship_execution_category(
source_data_connector,
&target_source.details.data_connector,
&target_source.capabilities,
) {
RelationshipExecutionCategory::Local => build_local_command_relationship(
resolved::RelationshipExecutionCategory::Local => build_local_command_relationship(
field,
field_call,
annotation,
@ -367,14 +364,16 @@ pub(crate) fn generate_command_relationship_ir<'s>(
target_source,
session_variables,
),
RelationshipExecutionCategory::RemoteForEach => build_remote_command_relationship(
field,
field_call,
annotation,
type_mappings,
target_source,
session_variables,
),
resolved::RelationshipExecutionCategory::RemoteForEach => {
build_remote_command_relationship(
field,
field_call,
annotation,
type_mappings,
target_source,
session_variables,
)
}
}
}
@ -481,7 +480,7 @@ pub(crate) fn build_remote_relationship<'n, 's>(
usage_counts: &mut UsagesCounts,
) -> Result<FieldSelection<'s>, error::Error> {
let mut join_mapping: Vec<(SourceField, TargetField)> = vec![];
for resolved::relationship::RelationshipModelMapping {
for resolved::RelationshipModelMapping {
source_field: source_field_path,
target_field: target_field_path,
target_ndc_column,
@ -561,7 +560,7 @@ pub(crate) fn build_remote_command_relationship<'n, 's>(
session_variables: &SessionVariables,
) -> Result<FieldSelection<'s>, error::Error> {
let mut join_mapping: Vec<(SourceField, ArgumentName)> = vec![];
for resolved::relationship::RelationshipCommandMapping {
for resolved::RelationshipCommandMapping {
source_field: source_field_path,
argument_name: target_argument_name,
} in annotation.mappings.iter()

View File

@ -5,7 +5,6 @@ pub mod metadata;
pub mod model;
pub mod ndc_validation;
pub mod permission;
pub mod relationship;
pub mod stages;
pub mod subgraph;
mod typecheck;
@ -23,4 +22,11 @@ pub use stages::models::{
FilterPermission, Model, ModelOrderByExpression, ModelPredicate, ModelSource,
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;

View File

@ -6,7 +6,7 @@ use crate::metadata::resolved::ndc_validation;
use crate::metadata::resolved::permission::ValueExpression;
use crate::metadata::resolved::stages::{
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::{QualifiedBaseType, QualifiedTypeReference};
@ -165,7 +165,7 @@ pub(crate) fn resolve_value_expression_for_argument(
value_expression: &open_dds::permissions::ValueExpression,
argument_type: &QualifiedTypeReference,
subgraph: &str,
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
boolean_expression_types: &HashMap<
Qualified<CustomTypeName>,
boolean_expressions::ObjectBooleanExpressionType,

View File

@ -1,6 +1,6 @@
use super::stages::{
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::error::Error;
@ -17,7 +17,7 @@ use super::typecheck;
pub fn resolve_command_permissions(
command: &commands::Command,
permissions: &CommandPermissionsV1,
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
boolean_expression_types: &HashMap<
Qualified<CustomTypeName>,
boolean_expressions::ObjectBooleanExpressionType,

View File

@ -9,19 +9,18 @@ use open_dds::{commands::CommandName, models::ModelName, types::CustomTypeName};
use crate::metadata::resolved::command;
use crate::metadata::resolved::error::Error;
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::stages::{
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.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Metadata {
pub object_types:
HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
pub scalar_types: HashMap<Qualified<CustomTypeName>, scalar_types::ScalarTypeRepresentation>,
pub models: IndexMap<Qualified<ModelName>, models::Model>,
pub commands: IndexMap<Qualified<CommandName>, commands::Command>,
@ -38,7 +37,7 @@ pub fn resolve_metadata(
metadata_accessor: &open_dds::accessor::MetadataAccessor,
graphql_config: &graphql_config::GraphqlConfig,
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>,
boolean_expression_types: &HashMap<
Qualified<CustomTypeName>,
@ -48,15 +47,6 @@ pub fn resolve_metadata(
mut models: IndexMap<Qualified<ModelName>, models::Model>,
mut commands: IndexMap<Qualified<CommandName>, commands::Command>,
) -> Result<Metadata, Error> {
// resolve relationships
let object_types = resolve_relationships(
metadata_accessor,
data_connectors,
object_types,
&models,
&commands,
)?;
// resolve command permissions
// TODO: make this return values rather than blindly mutating it's inputs
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
/// this currently works by mutating `commands`, let's change it to
/// return new values instead where possible
fn resolve_command_permissions(
metadata_accessor: &open_dds::accessor::MetadataAccessor,
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<
Qualified<CustomTypeName>,
boolean_expressions::ObjectBooleanExpressionType,
@ -201,7 +136,7 @@ fn resolve_command_permissions(
fn resolve_model_permissions(
metadata_accessor: &open_dds::accessor::MetadataAccessor,
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>,
boolean_expression_types: &HashMap<
Qualified<CustomTypeName>,

View File

@ -1,8 +1,7 @@
use super::permission::ValueExpression;
use super::relationship::RelationshipTarget;
use super::stages::{
boolean_expressions, data_connector_scalar_types, data_connector_type_mappings, models,
type_permissions,
relationships,
};
use super::typecheck;
@ -84,9 +83,8 @@ fn resolve_model_predicate(
subgraph: &str,
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
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>,
// type_representation: &TypeRepresentation,
) -> Result<models::ModelPredicate, Error> {
match model_predicate {
permissions::ModelPredicate::FieldComparison(permissions::FieldComparisonPredicate {
@ -213,7 +211,6 @@ fn resolve_model_predicate(
)?;
let relationship_field_name = mk_name(&name.0)?;
let relationship = &object_type_representation
.object_type
.relationships
.get(&relationship_field_name)
.ok_or_else(|| Error::UnknownRelationshipInSelectPermissionsPredicate {
@ -223,11 +220,13 @@ fn resolve_model_predicate(
})?;
match &relationship.target {
RelationshipTarget::Command { .. } => Err(Error::UnsupportedFeature {
message: "Predicate cannot be built using command relationships"
.to_string(),
}),
RelationshipTarget::Model {
relationships::RelationshipTarget::Command { .. } => {
Err(Error::UnsupportedFeature {
message: "Predicate cannot be built using command relationships"
.to_string(),
})
}
relationships::RelationshipTarget::Model {
model_name,
relationship_type,
target_typename,
@ -364,7 +363,7 @@ pub fn resolve_model_select_permissions(
subgraph: &str,
model_permissions: &ModelPermissionsV1,
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>,
boolean_expression_types: &HashMap<
Qualified<CustomTypeName>,
@ -505,11 +504,11 @@ fn resolve_binary_operator_for_model(
fn get_model_object_type_representation<'s>(
object_types: &'s HashMap<
Qualified<CustomTypeName>,
type_permissions::ObjectTypeWithPermissions,
relationships::ObjectTypeWithRelationships,
>,
data_type: &Qualified<CustomTypeName>,
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) {
Some(object_type_representation) => Ok(object_type_representation),

View File

@ -228,7 +228,6 @@ pub fn resolve_object_type(
Ok(ObjectTypeRepresentation {
fields: resolved_fields,
relationships: IndexMap::new(),
global_id_fields: resolved_global_id_fields,
graphql_output_type_name: graphql_type_name,
graphql_input_type_name,

View File

@ -1,7 +1,5 @@
use crate::metadata::resolved::error::Error;
use crate::metadata::resolved::relationship::Relationship;
use crate::metadata::resolved::subgraph::QualifiedTypeReference;
use indexmap::IndexMap;
use open_dds::types::{CustomTypeName, Deprecated, FieldName};
@ -92,7 +90,6 @@ pub struct DataConnectorTypeMappingsOutput {
#[display(fmt = "Display")]
pub struct ObjectTypeRepresentation {
pub fields: IndexMap<FieldName, FieldDefinition>,
pub relationships: IndexMap<ast::Name, Relationship>,
pub global_id_fields: Vec<FieldName>,
pub apollo_federation_config: Option<ResolvedObjectApolloFederationConfig>,
pub graphql_output_type_name: Option<ast::TypeName>,

View File

@ -7,6 +7,7 @@ pub mod data_connector_type_mappings;
pub mod data_connectors;
pub mod graphql_config;
pub mod models;
pub mod relationships;
pub mod roles;
pub mod scalar_types;
pub mod type_permissions;
@ -97,11 +98,19 @@ pub fn resolve(metadata: open_dds::Metadata) -> Result<Metadata, Error> {
&apollo_federation_entity_enabled_types,
)?;
let object_types_with_relationships = relationships::resolve(
&metadata_accessor,
&data_connectors,
&object_types_with_permissions,
&models,
&commands,
)?;
resolve_metadata(
&metadata_accessor,
&graphql_config,
&data_connector_type_mappings,
object_types_with_permissions,
object_types_with_relationships,
&scalar_types,
&boolean_expression_types,
&data_connectors,

View File

@ -1,89 +1,100 @@
use super::error::{Error, RelationshipError};
use super::stages::{
commands, data_connector_scalar_types, data_connectors, models, type_permissions,
mod types;
pub use types::{
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 open_dds::relationships::{
self, FieldAccess, RelationshipName, RelationshipType, RelationshipV1,
use std::collections::HashMap;
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 open_dds::types::Deprecated;
use serde::{Deserialize, Serialize};
use crate::metadata::resolved::types::mk_name;
use open_dds::relationships::{self, FieldAccess, RelationshipName, RelationshipV1};
use std::collections::HashSet;
#[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>,
},
}
/// resolve relationships
/// returns updated `types` value
pub fn resolve(
metadata_accessor: &open_dds::accessor::MetadataAccessor,
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
object_types_with_permissions: &HashMap<
Qualified<CustomTypeName>,
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,
},
) in object_types_with_permissions
{
object_types_with_relationships.insert(
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)]
pub enum RelationshipTargetName {
Model(Qualified<ModelName>),
Command(Qualified<CommandName>),
}
let resolved_relationship = resolve_relationship(
relationship,
subgraph,
models,
commands,
data_connectors,
&object_representation.object_type,
)?;
#[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>,
}
if object_representation
.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(),
});
}
}
#[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,
Ok(object_types_with_relationships)
}
#[allow(clippy::match_single_binding)]
@ -110,7 +121,7 @@ pub fn relationship_execution_category(
fn resolve_relationship_source_mapping<'a>(
relationship_name: &'a RelationshipName,
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,
) -> Result<&'a FieldAccess, Error> {
match &relationship_mapping.source {
@ -125,11 +136,7 @@ fn resolve_relationship_source_mapping<'a>(
relationship_name: relationship_name.clone(),
}),
[field_access] => {
if !source_type
.object_type
.fields
.contains_key(&field_access.field_name)
{
if !source_type.fields.contains_key(&field_access.field_name) {
return Err(Error::RelationshipError {
relationship_error:
RelationshipError::UnknownSourceFieldInRelationshipMapping {
@ -151,7 +158,7 @@ fn resolve_relationship_source_mapping<'a>(
fn resolve_relationship_mappings_model(
relationship: &RelationshipV1,
source_type_name: &Qualified<CustomTypeName>,
source_type: &type_permissions::ObjectTypeWithPermissions,
source_type: &data_connector_type_mappings::ObjectTypeRepresentation,
target_model: &models::Model,
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
) -> Result<Vec<RelationshipModelMapping>, Error> {
@ -255,7 +262,7 @@ fn resolve_relationship_mappings_model(
fn resolve_relationship_mappings_command(
relationship: &RelationshipV1,
source_type_name: &Qualified<CustomTypeName>,
source_type: &type_permissions::ObjectTypeWithPermissions,
source_type: &data_connector_type_mappings::ObjectTypeRepresentation,
target_command: &commands::Command,
) -> Result<Vec<RelationshipCommandMapping>, Error> {
let mut resolved_relationship_mappings = Vec::new();
@ -382,7 +389,7 @@ pub fn resolve_relationship(
models: &IndexMap<Qualified<ModelName>, models::Model>,
commands: &IndexMap<Qualified<CommandName>, commands::Command>,
data_connectors: &data_connector_scalar_types::DataConnectorsWithScalars,
source_type: &type_permissions::ObjectTypeWithPermissions,
source_type: &data_connector_type_mappings::ObjectTypeRepresentation,
) -> Result<Relationship, Error> {
let source_type_name = Qualified::new(subgraph.to_string(), relationship.source.clone());
let (relationship_target, source_data_connector, target_name) = match &relationship.target {

View File

@ -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,
}

View File

@ -7,11 +7,11 @@ use open_dds::{commands::CommandName, models::ModelName, types::CustomTypeName};
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.
pub fn resolve(
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
models: &IndexMap<Qualified<ModelName>, models::Model>,
commands: &IndexMap<Qualified<CommandName>, commands::Command>,
) -> Vec<Role> {

View File

@ -1,5 +1,6 @@
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};
@ -53,26 +54,23 @@ pub fn resolve_field(
#[derive(Debug)]
/// we do not want to store our types like this, but occasionally it is useful
/// for pattern matching
pub enum TypeRepresentation<'a> {
pub enum TypeRepresentation<'a, ObjectType> {
Scalar(&'a scalar_types::ScalarTypeRepresentation),
Object(&'a type_permissions::ObjectTypeWithPermissions),
Object(&'a ObjectType),
BooleanExpression(&'a boolean_expressions::ObjectBooleanExpressionType),
}
/// validate whether a given CustomTypeName exists within `object_types`, `scalar_types` or
/// `boolean_expression_types`
pub fn get_type_representation<'a>(
pub fn get_type_representation<'a, ObjectType>(
custom_type_name: &Qualified<CustomTypeName>,
object_types: &'a HashMap<
Qualified<CustomTypeName>,
type_permissions::ObjectTypeWithPermissions,
>,
object_types: &'a HashMap<Qualified<CustomTypeName>, ObjectType>,
scalar_types: &'a HashMap<Qualified<CustomTypeName>, scalar_types::ScalarTypeRepresentation>,
boolean_expression_types: &'a HashMap<
Qualified<CustomTypeName>,
boolean_expressions::ObjectBooleanExpressionType,
>,
) -> Result<TypeRepresentation<'a>, Error> {
) -> Result<TypeRepresentation<'a, ObjectType>, Error> {
match object_types.get(custom_type_name) {
Some(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,
object_types: &'a HashMap<
Qualified<CustomTypeName>,
type_permissions::ObjectTypeWithPermissions,
relationships::ObjectTypeWithRelationships,
>,
) -> Result<&'a type_permissions::ObjectTypeWithPermissions, Error> {
) -> Result<&'a relationships::ObjectTypeWithRelationships, Error> {
object_types
.get(&boolean_expression_type.object_type)
.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
// Nullable container 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>,
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
object_types: &HashMap<Qualified<CustomTypeName>, ObjectType>,
) -> Result<Qualified<CustomTypeName>, Error> {
object_types
.get(custom_type_name)

View File

@ -8,9 +8,6 @@ use super::types::output_type::get_object_type_representation;
use super::types::output_type::relationship::{FilterRelationshipAnnotation, ModelTargetSource};
use super::types::{BooleanExpressionAnnotation, InputAnnotation, TypeId};
use crate::metadata::resolved;
use crate::metadata::resolved::relationship::{
relationship_execution_category, RelationshipExecutionCategory, RelationshipTarget,
};
use crate::metadata::resolved::subgraph::Qualified;
use crate::metadata::resolved::types::mk_name;
@ -143,9 +140,8 @@ pub fn build_boolean_expression_input_schema(
// relationship fields
// TODO(naveen): Add support for command relationships
for (rel_name, relationship) in object_type_representation.object_type.relationships.iter()
{
if let RelationshipTarget::Model {
for (rel_name, relationship) in object_type_representation.relationships.iter() {
if let resolved::RelationshipTarget::Model {
model_name,
relationship_type,
target_typename,
@ -168,11 +164,13 @@ pub fn build_boolean_expression_input_schema(
ModelTargetSource::from_model_source(target_source, relationship)?;
// filter expression with relationships is currently only supported for local relationships
if let RelationshipExecutionCategory::Local = relationship_execution_category(
&boolean_expression_type.data_connector_link,
&target_source.data_connector,
&target_model_source.capabilities,
) {
if let resolved::RelationshipExecutionCategory::Local =
resolved::relationship_execution_category(
&boolean_expression_type.data_connector_link,
&target_source.data_connector,
&target_model_source.capabilities,
)
{
if target_source.data_connector.name
== boolean_expression_type.data_connector_name
{

View File

@ -8,9 +8,6 @@ use std::collections::{BTreeMap, HashMap};
use super::types::output_type::relationship::{ModelTargetSource, OrderByRelationshipAnnotation};
use super::types::{output_type::get_object_type_representation, Annotation, TypeId};
use crate::metadata::resolved;
use crate::metadata::resolved::relationship::{
relationship_execution_category, RelationshipExecutionCategory, RelationshipTarget,
};
use crate::metadata::resolved::subgraph::Qualified;
use crate::metadata::resolved::types::mk_name;
use crate::schema::permissions;
@ -159,9 +156,8 @@ pub fn build_model_order_by_input_schema(
// relationship fields
// TODO(naveen): Add support for command relationships.
for (rel_name, relationship) in object_type_representation.object_type.relationships.iter()
{
if let RelationshipTarget::Model {
for (rel_name, relationship) in object_type_representation.relationships.iter() {
if let resolved::RelationshipTarget::Model {
model_name,
relationship_type,
target_typename,
@ -185,11 +181,13 @@ pub fn build_model_order_by_input_schema(
let target_model_source =
ModelTargetSource::from_model_source(target_source, relationship)?;
// order_by expression with relationships is currently only supported for local relationships
if let RelationshipExecutionCategory::Local = relationship_execution_category(
&model_source.data_connector,
&target_source.data_connector,
&target_model_source.capabilities,
) {
if let resolved::RelationshipExecutionCategory::Local =
resolved::relationship_execution_category(
&model_source.data_connector,
&target_source.data_connector,
&target_model_source.capabilities,
)
{
// TODO(naveen): Support Array relationships in order_by when the support for aggregates is implemented
if let RelationshipType::Object = relationship_type {
// If the relationship target model does not have orderByExpressionType do not include

View File

@ -2,7 +2,9 @@ use open_dds::types::{CustomTypeName, FieldName};
use std::collections::{BTreeMap, HashMap};
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::types::{object_type_exists, unwrap_custom_type_name};
use crate::metadata::resolved::{self};
@ -14,7 +16,7 @@ use super::types::ArgumentNameAndPath;
/// Build namespace annotation for select permissions
pub(crate) fn get_select_permissions_namespace_annotations(
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> {
let mut permissions: HashMap<Role, Option<types::NamespaceAnnotation>> = model
.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.
pub(crate) fn get_select_one_namespace_annotations(
model: &models::Model,
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
object_type_representation: &resolved::ObjectTypeWithRelationships,
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> {
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.
pub(crate) fn get_model_relationship_namespace_annotations(
target_model: &models::Model,
source_object_type_representation: &type_permissions::ObjectTypeWithPermissions,
target_object_type_representation: &type_permissions::ObjectTypeWithPermissions,
mappings: &[resolved::relationship::RelationshipModelMapping],
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
source_object_type_representation: &resolved::ObjectTypeWithRelationships,
target_object_type_representation: &resolved::ObjectTypeWithRelationships,
mappings: &[resolved::RelationshipModelMapping],
object_types: &HashMap<Qualified<CustomTypeName>, relationships::ObjectTypeWithRelationships>,
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, schema::Error> {
let select_permissions =
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
pub(crate) fn get_command_namespace_annotations(
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> {
let mut permissions = HashMap::new();
@ -224,16 +226,16 @@ fn build_annotations_from_input_object_type_permissions(
field_path: &mut [String],
type_reference: &QualifiedTypeReference,
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>,
role_presets_map: &mut HashMap<Role, types::ArgumentPresets>,
) -> Result<(), schema::Error> {
if let Some(custom_typename) = unwrap_custom_type_name(type_reference) {
if let Ok(object_type) = object_type_exists(custom_typename, object_types) {
if let Some(object_type_repr) = object_types.get(&object_type) {
if let Some(object_type) = unwrap_custom_type_name(type_reference) {
if object_type_exists(object_type, object_types).is_ok() {
if let Some(object_type_repr) = object_types.get(object_type) {
let field_mappings =
type_mappings
.get(&object_type)
.get(object_type)
.map(|type_mapping| match type_mapping {
data_connector_type_mappings::TypeMapping::Object {
ndc_object_type_name: _,
@ -248,7 +250,7 @@ fn build_annotations_from_input_object_type_permissions(
type_reference,
field_path,
ndc_argument_name,
&object_type,
object_type,
)?;
role_presets_map.insert(
@ -359,9 +361,9 @@ fn build_preset_map_from_input_object_type_permission(
/// in the relationship mappings.
pub(crate) fn get_command_relationship_namespace_annotations(
command: &resolved::Command,
source_object_type_representation: &type_permissions::ObjectTypeWithPermissions,
mappings: &[resolved::relationship::RelationshipCommandMapping],
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
source_object_type_representation: &resolved::ObjectTypeWithRelationships,
mappings: &[resolved::RelationshipCommandMapping],
object_types: &HashMap<Qualified<CustomTypeName>, resolved::ObjectTypeWithRelationships>,
) -> Result<HashMap<Role, Option<types::NamespaceAnnotation>>, crate::schema::Error> {
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)
/// to all the Global ID fields.
pub(crate) fn get_node_interface_annotations(
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
object_type_representation: &resolved::ObjectTypeWithRelationships,
) -> HashMap<Role, Option<types::NamespaceAnnotation>> {
let mut permissions = HashMap::new();
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)
/// to all the key fields.
pub(crate) fn get_entity_union_permissions(
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
object_type_representation: &resolved::ObjectTypeWithRelationships,
) -> HashMap<Role, Option<types::NamespaceAnnotation>> {
let mut permissions = HashMap::new();
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
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,
) -> impl Iterator<Item = &'a Role> {
object_type_representation
@ -440,7 +442,7 @@ pub(crate) fn get_allowed_roles_for_field<'a>(
/// Builds namespace annotations for the `node` field.
pub(crate) fn get_node_field_namespace_permissions(
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
object_type_representation: &resolved::ObjectTypeWithRelationships,
model: &models::Model,
) -> HashMap<Role, resolved::FilterPermission> {
let mut permissions = HashMap::new();
@ -479,7 +481,7 @@ pub(crate) fn get_node_field_namespace_permissions(
/// Builds namespace annotations for the `_entities` field.
pub(crate) fn get_entities_field_namespace_permissions(
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
object_type_representation: &resolved::ObjectTypeWithRelationships,
model: &models::Model,
) -> HashMap<Role, resolved::FilterPermission> {
let mut permissions = HashMap::new();

View File

@ -1,6 +1,7 @@
use crate::{
metadata::resolved::{
stages::{data_connector_type_mappings, type_permissions},
self,
stages::data_connector_type_mappings,
subgraph::{Qualified, QualifiedBaseType, QualifiedTypeName, QualifiedTypeReference},
types::{get_type_representation, mk_name, TypeRepresentation},
},
@ -80,7 +81,7 @@ fn get_custom_input_type(
.map_err(|_| crate::schema::Error::InternalTypeNotFound {
type_name: gds_type_name.clone(),
})? {
TypeRepresentation::Object(type_permissions::ObjectTypeWithPermissions {
TypeRepresentation::Object(resolved::ObjectTypeWithRelationships {
object_type:
data_connector_type_mappings::ObjectTypeRepresentation {
graphql_input_type_name,
@ -125,7 +126,7 @@ fn get_custom_input_type(
fn input_object_type_input_fields(
gds: &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> {
object_type_representation
.object_type

View File

@ -14,7 +14,7 @@ use self::relationship::{
};
use super::inbuilt_type::base_type_container_for_inbuilt_type;
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::{
Qualified, QualifiedBaseType, QualifiedTypeName, QualifiedTypeReference,
};
@ -190,8 +190,8 @@ fn object_type_fields(
gds: &GDS,
builder: &mut gql_schema::Builder<GDS>,
type_name: &Qualified<CustomTypeName>,
object_type_representation: &type_permissions::ObjectTypeWithPermissions,
object_types: &HashMap<Qualified<CustomTypeName>, type_permissions::ObjectTypeWithPermissions>,
object_type_representation: &resolved::ObjectTypeWithRelationships,
object_types: &HashMap<Qualified<CustomTypeName>, resolved::ObjectTypeWithRelationships>,
) -> Result<BTreeMap<ast::Name, gql_schema::Namespaced<GDS, gql_schema::Field<GDS>>>, Error> {
let mut graphql_fields = object_type_representation
.object_type
@ -225,13 +225,11 @@ fn object_type_fields(
Ok((graphql_field_name, namespaced_field))
})
.collect::<Result<BTreeMap<_, _>, _>>()?;
for (relationship_field_name, relationship) in
&object_type_representation.object_type.relationships
{
for (relationship_field_name, relationship) in &object_type_representation.relationships {
let deprecation_status = mk_deprecation_status(&relationship.deprecated);
let relationship_field = match &relationship.target {
resolved::relationship::RelationshipTarget::Command {
resolved::RelationshipTarget::Command {
command_name,
target_type,
mappings,
@ -292,7 +290,7 @@ fn object_type_fields(
)?,
)
}
resolved::relationship::RelationshipTarget::Model {
resolved::RelationshipTarget::Model {
model_name,
relationship_type,
target_typename,
@ -516,7 +514,7 @@ pub fn output_type_schema(
pub(crate) fn get_object_type_representation<'s>(
gds: &'s GDS,
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(|| {
crate::schema::Error::InternalTypeNotFound {
type_name: gds_type.clone(),

View File

@ -32,7 +32,7 @@ pub struct ModelRelationshipAnnotation {
pub target_source: Option<ModelTargetSource>,
pub target_type: Qualified<CustomTypeName>,
pub relationship_type: RelationshipType,
pub mappings: Vec<resolved::relationship::RelationshipModelMapping>,
pub mappings: Vec<resolved::RelationshipModelMapping>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
@ -49,7 +49,7 @@ pub struct FilterRelationshipAnnotation {
pub target_source: ModelTargetSource,
pub target_type: Qualified<CustomTypeName>,
pub target_model_name: Qualified<ModelName>,
pub mappings: Vec<resolved::relationship::RelationshipModelMapping>,
pub mappings: Vec<resolved::RelationshipModelMapping>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
@ -66,7 +66,7 @@ pub struct OrderByRelationshipAnnotation {
pub target_source: ModelTargetSource,
pub target_type: Qualified<CustomTypeName>,
pub target_model_name: Qualified<ModelName>,
pub mappings: Vec<resolved::relationship::RelationshipModelMapping>,
pub mappings: Vec<resolved::RelationshipModelMapping>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
@ -83,19 +83,19 @@ pub struct PredicateRelationshipAnnotation {
pub target_source: ModelTargetSource,
pub target_type: Qualified<CustomTypeName>,
pub target_model_name: Qualified<ModelName>,
pub mappings: Vec<resolved::relationship::RelationshipModelMapping>,
pub mappings: Vec<resolved::RelationshipModelMapping>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct ModelTargetSource {
pub(crate) model: models::ModelSource,
pub(crate) capabilities: resolved::relationship::RelationshipCapabilities,
pub(crate) capabilities: resolved::RelationshipCapabilities,
}
impl ModelTargetSource {
pub fn new(
model: &models::Model,
relationship: &resolved::relationship::Relationship,
relationship: &resolved::Relationship,
) -> Result<Option<Self>, schema::Error> {
model
.source
@ -106,7 +106,7 @@ impl ModelTargetSource {
pub fn from_model_source(
model_source: &models::ModelSource,
relationship: &resolved::relationship::Relationship,
relationship: &resolved::Relationship,
) -> Result<Self, schema::Error> {
Ok(Self {
model: model_source.clone(),
@ -130,20 +130,20 @@ pub struct CommandRelationshipAnnotation {
pub target_source: Option<CommandTargetSource>,
pub target_type: QualifiedTypeReference,
pub target_base_type_kind: TypeKind,
pub mappings: Vec<resolved::relationship::RelationshipCommandMapping>,
pub mappings: Vec<resolved::RelationshipCommandMapping>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct CommandTargetSource {
pub(crate) details: CommandSourceDetail,
pub(crate) function_name: FunctionName,
pub(crate) capabilities: resolved::relationship::RelationshipCapabilities,
pub(crate) capabilities: resolved::RelationshipCapabilities,
}
impl CommandTargetSource {
pub fn new(
command: &resolved::Command,
relationship: &resolved::relationship::Relationship,
relationship: &resolved::Relationship,
) -> Result<Option<Self>, schema::Error> {
command
.source