IR generation for remote type to command relationship (#266)

Co-authored-by: Anon Ray <rayanon004@gmail.com>
V3_GIT_ORIGIN_REV_ID: 6301ff61bc6fe59e5a735a52a501911d3e471cad
This commit is contained in:
Puru Gupta 2024-01-04 15:19:01 +05:30 committed by hasura-bot
parent 9485d781b8
commit 40fd49dad4
45 changed files with 6621 additions and 104 deletions

View File

@ -573,6 +573,51 @@ async fn get_schema() -> Json<models::SchemaResponse> {
};
// ANCHOR_END: schema_function_get_actors_by_name
// ANCHOR: schema_function_get_actors_by_movie_id
let get_actors_by_movie_id_function = models::FunctionInfo {
name: "get_actors_by_movie_id".into(),
description: Some("Get all actors from a movie by movie ID".into()),
arguments: BTreeMap::from_iter([(
"movie_id".into(),
models::ArgumentInfo {
description: Some("the id of the movie to fetch the actors from".into()),
argument_type: models::Type::Named { name: "Int".into() },
},
)]),
result_type: models::Type::Array {
element_type: Box::new(models::Type::Named {
name: "actor".into(),
}),
},
};
// ANCHOR_END: schema_function_get_actors_by_movie_id
// ANCHOR: schema_function_get_all_actors
let get_all_actors_function = models::FunctionInfo {
name: "get_all_actors".into(),
description: Some("Get all the actors".into()),
result_type: models::Type::Array {
element_type: Box::new(models::Type::Named {
name: "actor".into(),
}),
},
arguments: BTreeMap::new(),
};
// ANCHOR_END: schema_function_get_all_actors
// ANCHOR: schema_function_get_all_movies
let get_all_movies_function = models::FunctionInfo {
name: "get_all_movies".into(),
description: Some("Get all the movies".into()),
result_type: models::Type::Array {
element_type: Box::new(models::Type::Named {
name: "movie".into(),
}),
},
arguments: BTreeMap::new(),
};
// ANCHOR_END: schema_function_get_all_movies
// ANCHOR: schema_functions
let functions: Vec<models::FunctionInfo> = vec![
latest_actor_id_function,
@ -581,6 +626,9 @@ async fn get_schema() -> Json<models::SchemaResponse> {
get_actor_by_id_function,
get_movie_by_id_function,
get_actors_by_name_function,
get_actors_by_movie_id_function,
get_all_actors_function,
get_all_movies_function,
];
// ANCHOR_END: schema_functions
// ANCHOR: schema2
@ -698,6 +746,7 @@ fn get_collection_by_name(
}
"actor_names_by_movie" => actor_names_by_movie_rows(arguments, state),
"get_all_actors" => get_all_actors_rows(state, query, collection_relationships, variables),
"get_all_movies" => get_all_movies_rows(state, query, collection_relationships, variables),
"get_actors_by_movie_id_bounds" => get_actors_by_movie_id_bounds_rows(
arguments,
state,
@ -705,6 +754,9 @@ fn get_collection_by_name(
collection_relationships,
variables,
),
"get_actors_by_movie_id" => {
get_actors_by_movie_rows(arguments, state, query, collection_relationships, variables)
}
_ => Err((
StatusCode::BAD_REQUEST,
Json(models::ErrorResponse {
@ -931,7 +983,7 @@ fn get_movie_by_id_rows(
let id_value = arguments.get("movie_id").ok_or((
StatusCode::BAD_REQUEST,
Json(models::ErrorResponse {
message: "missing argument id".into(),
message: "missing argument movie_id".into(),
details: serde_json::Value::Null,
}),
))?;
@ -970,6 +1022,55 @@ fn get_movie_by_id_rows(
}
}
fn get_actors_by_movie_rows(
arguments: &BTreeMap<String, serde_json::Value>,
state: &AppState,
query: &models::Query,
collection_relationships: &BTreeMap<String, models::Relationship>,
variables: &BTreeMap<String, serde_json::Value>,
) -> Result<Vec<Row>> {
let movie_id = arguments.get("movie_id").ok_or((
StatusCode::BAD_REQUEST,
Json(models::ErrorResponse {
message: "missing argument movie_id".into(),
details: serde_json::Value::Null,
}),
))?;
let movie_id_int = movie_id.as_i64().ok_or((
StatusCode::BAD_REQUEST,
Json(models::ErrorResponse {
message: "movie_id must be a integer".into(),
details: serde_json::Value::Null,
}),
))?;
let mut actors_by_movie = vec![];
for (_id, actor) in state.actors.iter() {
let actor_movie_id_int = get_actor_movie_id(actor)?;
if actor_movie_id_int == movie_id_int {
let row = project_row(actor, state, query, collection_relationships, variables)?;
actors_by_movie.push(row)
}
}
let actors_by_movie_value = serde_json::to_value(actors_by_movie).map_err(|_| {
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(models::ErrorResponse {
message: " ".into(),
details: serde_json::Value::Null,
}),
)
})?;
Ok(vec![BTreeMap::from_iter([(
"__value".into(),
actors_by_movie_value,
)])])
}
fn project_row(
row: &Row,
state: &AppState,
@ -1264,6 +1365,41 @@ fn get_all_actors_rows(
)])])
}
fn get_all_movies_rows(
state: &AppState,
query: &models::Query,
collection_relationships: &BTreeMap<String, models::Relationship>,
variables: &BTreeMap<String, serde_json::Value>,
) -> Result<Vec<Row>> {
let mut movies = vec![];
for (_id, movie) in state.movies.iter() {
let rows = project_row(movie, state, query, collection_relationships, variables)?;
let movie_value = serde_json::to_value(rows).map_err(|_| {
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(models::ErrorResponse {
message: "unable to encode value".into(),
details: serde_json::Value::Null,
}),
)
})?;
movies.push(movie_value);
}
let movies_value = serde_json::to_value(movies).map_err(|_| {
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(models::ErrorResponse {
message: "unable to encode value".into(),
details: serde_json::Value::Null,
}),
)
})?;
Ok(vec![BTreeMap::from_iter([(
"__value".into(),
movies_value,
)])])
}
fn get_actors_by_movie_id_bounds_rows(
arguments: &BTreeMap<String, serde_json::Value>,
state: &AppState,

View File

@ -100,9 +100,6 @@ pub enum InternalEngineError {
#[error("remote relationships should have been handled separately")]
RemoteRelationshipsAreNotSupported,
#[error("remote relationships to command are not supported")]
RemoteCommandRelationshipsAreNotSupported,
#[error("expected filter predicate but filter predicate namespaced annotation not found")]
FilterPermissionAnnotationNotFound,

View File

@ -8,6 +8,7 @@ use nonempty::NonEmpty;
use crate::execute::{error, query_plan};
use super::remote_joins::types::RemoteJoinType;
use super::ExecuteOrExplainResponse;
pub mod types;
@ -151,15 +152,23 @@ fn get_join_steps(
for (alias, location) in join_locations.locations {
let mut sequence_steps = vec![];
if let Some((remote_join, _join_id)) = location.join_node {
// We only support remote joins to Model for now
// TODO (paritosh): Fix this when we support model to command relationships
let mut query_request = remote_join.target_ndc_ir;
query_request.variables = Some(vec![]);
sequence_steps.push(Box::new(types::Step::ForEach(
types::ForEachStep::ModelSelect(types::ModelSelectIR {
model_name: alias.clone(),
query_request,
}),
match remote_join.remote_join_type {
RemoteJoinType::ToModel => {
types::ForEachStep::ModelSelect(types::ModelSelectIR {
model_name: alias.clone(),
query_request,
})
}
RemoteJoinType::ToCommand => {
types::ForEachStep::CommandSelect(types::CommandSelectIR {
command_name: alias.clone(),
query_request,
})
}
},
)))
};
if let Some(rest_join_steps) = get_join_steps(alias, location.rest) {

View File

@ -59,6 +59,9 @@ pub struct FunctionBasedCommand<'s> {
/// Source function in the data connector for this model
pub function_name: &'s FunctionName,
/// Variable arguments to be used for remote joins
pub variable_arguments: BTreeMap<String, String>,
}
/// IR for the 'procedure based command' operations
@ -162,6 +165,7 @@ pub(crate) fn generate_function_based_command<'n, 's>(
Ok(FunctionBasedCommand {
command_info,
function_name,
variable_arguments: BTreeMap::new(),
})
}
@ -207,11 +211,10 @@ pub fn ir_to_ndc_query<'s>(
}
pub fn ir_to_ndc_query_ir<'s>(
function_name: &FunctionName,
ir: &FunctionBasedCommand<'s>,
join_id_counter: &mut MonotonicCounter,
) -> Result<(ndc::models::QueryRequest, JoinLocations<RemoteJoin<'s>>), error::Error> {
let arguments = ir
let mut arguments: BTreeMap<String, ndc_client::models::Argument> = ir
.command_info
.arguments
.iter()
@ -224,6 +227,17 @@ pub fn ir_to_ndc_query_ir<'s>(
)
})
.collect();
// Add the variable arguments which are used for remote joins
for (variable_name, variable_argument) in ir.variable_arguments.iter() {
arguments.insert(
variable_name.clone(),
ndc_client::models::Argument::Variable {
name: variable_argument.clone(),
},
);
}
let (query, jl) = ir_to_ndc_query(&ir.command_info, join_id_counter)?;
let mut collection_relationships = BTreeMap::new();
selection_set::collect_relationships(
@ -232,7 +246,7 @@ pub fn ir_to_ndc_query_ir<'s>(
)?;
let query_request = ndc::models::QueryRequest {
query,
collection: function_name.to_string(),
collection: ir.function_name.to_string(),
arguments,
collection_relationships,
variables: None,

View File

@ -3,6 +3,7 @@ use std::collections::BTreeMap;
use hasura_authn_core::SessionVariables;
use lang_graphql::normalized_ast::{self, Field};
use open_dds::{
arguments::ArgumentName,
relationships::{RelationshipName, RelationshipType},
types::{CustomTypeName, FieldName},
};
@ -65,6 +66,12 @@ pub struct RemoteModelRelationshipInfo<'s> {
pub join_mapping: Vec<(SourceField, TargetField)>,
}
#[derive(Debug, Serialize)]
pub(crate) struct RemoteCommandRelationshipInfo<'s> {
pub annotation: &'s CommandRelationshipAnnotation,
pub join_mapping: Vec<(SourceField, ArgumentName)>,
}
pub type SourceField = (FieldName, resolved::types::FieldMapping);
pub type TargetField = (FieldName, resolved::types::FieldMapping);
@ -359,9 +366,14 @@ pub(crate) fn generate_command_relationship_ir<'s>(
target_source,
session_variables,
),
RelationshipExecutionCategory::RemoteForEach => {
Err(error::InternalEngineError::RemoteCommandRelationshipsAreNotSupported)?
}
RelationshipExecutionCategory::RemoteForEach => build_remote_command_relationship(
field,
field_call,
annotation,
type_mappings,
target_source,
session_variables,
),
}
}
@ -535,6 +547,59 @@ pub(crate) fn build_remote_relationship<'n, 's>(
})
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn build_remote_command_relationship<'n, 's>(
field: &'n normalized_ast::Field<'s, GDS>,
field_call: &'n normalized_ast::FieldCall<'s, GDS>,
annotation: &'s CommandRelationshipAnnotation,
type_mappings: &'s BTreeMap<Qualified<CustomTypeName>, resolved::types::TypeMapping>,
target_source: &'s CommandTargetSource,
session_variables: &SessionVariables,
) -> Result<FieldSelection<'s>, error::Error> {
let mut join_mapping: Vec<(SourceField, ArgumentName)> = vec![];
for resolved::relationship::RelationshipCommandMapping {
source_field: source_field_path,
argument_name: target_argument_name,
} in annotation.mappings.iter()
{
let source_column = get_field_mapping_of_field_name(
type_mappings,
&annotation.source_type,
&annotation.relationship_name,
&source_field_path.field_name,
)?;
let source_field = (source_field_path.field_name.clone(), source_column);
join_mapping.push((source_field, target_argument_name.clone()));
}
let mut remote_relationships_ir = generate_function_based_command(
&annotation.command_name,
&target_source.function_name,
field,
field_call,
&annotation.underlying_object_typename,
&target_source.details,
session_variables,
)?;
// Add the arguments on which the join is done to the command arguments
let mut variable_arguments = BTreeMap::new();
for (_source, target_argument_name) in &join_mapping {
let target_value_variable = format!("${}", target_argument_name);
variable_arguments.insert(target_argument_name.to_string(), target_value_variable);
}
remote_relationships_ir.variable_arguments = variable_arguments;
let rel_info = RemoteCommandRelationshipInfo {
annotation,
join_mapping,
};
Ok(FieldSelection::CommandRelationshipRemote {
ir: remote_relationships_ir,
relationship_info: rel_info,
})
}
fn get_field_mapping_of_field_name(
type_mappings: &BTreeMap<Qualified<CustomTypeName>, resolved::types::TypeMapping>,
type_name: &Qualified<CustomTypeName>,

View File

@ -7,15 +7,18 @@ use open_dds::types::{CustomTypeName, FieldName};
use serde::Serialize;
use std::collections::{BTreeMap, HashMap};
use super::commands::{self, FunctionBasedCommand};
use super::commands::{self, ir_to_ndc_query_ir, FunctionBasedCommand};
use super::model_selection::{self, ModelSelection};
use super::relationship::{
self, LocalCommandRelationshipInfo, LocalModelRelationshipInfo, RemoteModelRelationshipInfo,
self, LocalCommandRelationshipInfo, LocalModelRelationshipInfo, RemoteCommandRelationshipInfo,
RemoteModelRelationshipInfo,
};
use crate::execute::error;
use crate::execute::global_id;
use crate::execute::model_tracking::UsagesCounts;
use crate::execute::remote_joins::types::{JoinLocations, MonotonicCounter};
use crate::execute::remote_joins::types::{
JoinLocations, MonotonicCounter, RemoteJoinType, ResponseType, TargetField,
};
use crate::execute::remote_joins::types::{Location, RemoteJoin};
use crate::metadata::resolved;
use crate::metadata::resolved::subgraph::Qualified;
@ -46,6 +49,10 @@ pub(crate) enum FieldSelection<'s> {
ir: ModelSelection<'s>,
relationship_info: RemoteModelRelationshipInfo<'s>,
},
CommandRelationshipRemote {
ir: FunctionBasedCommand<'s>,
relationship_info: RemoteCommandRelationshipInfo<'s>,
},
}
/// IR that represents the selected fields of an output type.
@ -283,19 +290,23 @@ pub(crate) fn process_selection_set_ir<'s>(
} => {
// For all the left join fields, create an alias and inject
// them into the NDC IR
let mut join_columns = HashMap::new();
let mut join_mapping = HashMap::new();
for ((src_field_alias, src_field), target_field) in &relationship_info.join_mapping
{
let lhs_alias = make_hasura_phantom_field(&src_field.column);
ndc_fields.insert(
lhs_alias.clone(),
ndc::models::Field::Column {
column: src_field.column.clone(),
},
);
join_columns.insert(
join_mapping.insert(
src_field_alias.clone(),
(lhs_alias.clone(), target_field.clone()),
(
lhs_alias.clone(),
TargetField::ModelField(target_field.clone()),
),
);
}
// Construct the `JoinLocations` tree
@ -304,7 +315,55 @@ pub(crate) fn process_selection_set_ir<'s>(
let rj_info = RemoteJoin {
target_ndc_ir: ndc_ir,
target_data_connector: ir.data_connector,
join_columns,
join_mapping,
process_response_as: ResponseType::Array { is_nullable: true },
remote_join_type: RemoteJoinType::ToModel,
};
join_locations.locations.insert(
alias.clone(),
Location {
join_node: Some(rj_info),
rest: sub_join_locations,
},
);
}
FieldSelection::CommandRelationshipRemote {
ir,
relationship_info,
} => {
// For all the left join fields, create an alias and inject
// them into the NDC IR
let mut join_mapping = HashMap::new();
for ((src_field_alias, src_field), target_field) in &relationship_info.join_mapping
{
let lhs_alias = make_hasura_phantom_field(&src_field.column);
ndc_fields.insert(
lhs_alias.clone(),
ndc::models::Field::Column {
column: src_field.column.clone(),
},
);
join_mapping.insert(
src_field_alias.clone(),
(
lhs_alias.clone(),
TargetField::CommandField(target_field.clone()),
),
);
}
// Construct the `JoinLocations` tree
let (ndc_ir, sub_join_locations) = ir_to_ndc_query_ir(ir, join_id_counter)?;
let rj_info = RemoteJoin {
target_ndc_ir: ndc_ir,
target_data_connector: ir.command_info.data_connector,
join_mapping,
process_response_as: ResponseType::Command {
// TODO: fix and remove clone()
command_name: ir.command_info.command_name.clone(),
type_container: ir.command_info.type_container.clone(),
},
remote_join_type: RemoteJoinType::ToCommand,
};
join_locations.locations.insert(
alias.clone(),
@ -357,6 +416,7 @@ pub(crate) fn collect_relationships(
// we ignore remote relationships as we are generating relationship
// definition for one data connector
FieldSelection::ModelRelationshipRemote { .. } => (),
FieldSelection::CommandRelationshipRemote { .. } => (),
};
}
Ok(())

View File

@ -81,7 +81,7 @@ pub struct ExecutionNode<'s> {
pub data_connector: &'s resolved::data_connector::DataConnector,
}
#[derive(Debug)]
#[derive(Clone, Debug, PartialEq)]
pub enum ProcessResponseAs<'ir> {
Object {
is_nullable: bool,
@ -215,9 +215,7 @@ fn plan_query<'n, 's, 'ir>(
None => NodeQueryPlan::RelayNodeSelect(None),
},
root_field::QueryRootField::FunctionBasedCommand { ir, selection_set } => {
let function_name = ir.function_name;
let (ndc_ir, join_locations) =
commands::ir_to_ndc_query_ir(function_name, ir, &mut counter)?;
let (ndc_ir, join_locations) = commands::ir_to_ndc_query_ir(ir, &mut counter)?;
let join_locations_ids = assign_with_join_ids(join_locations)?;
let execution_tree = ExecutionTree {
root_node: ExecutionNode {

View File

@ -8,6 +8,8 @@ use tracing_util::SpanVisibility;
use ndc_client as ndc;
use self::types::TargetField;
use super::error;
use super::ndc::execute_ndc_query;
use super::query_plan::ProcessResponseAs;
@ -124,6 +126,19 @@ pub async fn execute_join_locations(
)
.await?;
let process_response_as = match &join_node.process_response_as {
types::ResponseType::Array { is_nullable } => ProcessResponseAs::Array {
is_nullable: *is_nullable,
},
types::ResponseType::Command {
command_name,
type_container,
} => ProcessResponseAs::CommandResponse {
command_name,
type_container,
},
};
// if there is a `location.rest`, recursively process the tree; which
// will modify the `target_response` with all joins down the tree
if !location.rest.locations.is_empty() {
@ -133,10 +148,7 @@ pub async fn execute_join_locations(
// TODO: is this field span correct?
field_span_attribute.clone(),
&mut target_response,
// TODO: RHS CANNOT be command for now as we don't support
// it yet. Once we support it, we'll have to handle that
// case as well.
&ProcessResponseAs::Array { is_nullable: true },
&process_response_as,
sub_tree,
)
.await?;
@ -197,9 +209,6 @@ fn collect_arguments<'s>(
for row in rows.iter() {
let mut replacement_token = (0, 0);
// TODO: RHS CANNOT be command for now as we don't support it
// yet. Once we support it, we'll have to handle that case as
// well.
match lhs_response_type {
ProcessResponseAs::Array { .. } | ProcessResponseAs::Object { .. } => {
collect_argument_from_row(
@ -242,13 +251,14 @@ fn collect_arguments<'s>(
replacement_tokens.push(None);
}
}
match remote_join {
None => Err(error::Error::from(
match (remote_join, arguments.is_empty()) {
(None, true) => Ok(None),
(None, false) => Err(error::Error::from(
error::InternalEngineError::InternalGeneric {
description: "unexpected: remote join empty".to_string(),
},
)),
Some(remote_join) => Ok(Some(CollectArgumentResult {
(Some(remote_join), _) => Ok(Some(CollectArgumentResult {
join_node: remote_join,
sub_tree,
remote_alias,
@ -279,12 +289,23 @@ fn collect_argument_from_row<'s>(
if let Some((join_node, join_id)) = &location.join_node {
let mut argument = BTreeMap::new();
for (src_alias, target_field) in join_node.join_columns.values() {
let val = get_value(src_alias, row);
// use the target field name here to create the variable
// name to be used in RHS
let variable_name = format!("${}", &target_field.1.column);
argument.insert(variable_name, ValueExt::from(val.clone()));
for (src_alias, target_field) in join_node.join_mapping.values() {
match target_field {
TargetField::ModelField((_, field_mapping)) => {
let val = get_value(src_alias, row);
// use the target field name here to create the variable
// name to be used in RHS
let variable_name = format!("${}", &field_mapping.column);
argument.insert(variable_name, ValueExt::from(val.clone()));
}
TargetField::CommandField(argument_name) => {
let val = get_value(src_alias, row);
// use the target field name here to create the variable
// name to be used in RHS
let variable_name = format!("${}", &argument_name);
argument.insert(variable_name, ValueExt::from(val.clone()));
}
}
}
// de-duplicate arguments
let argument_id: i16;
@ -390,14 +411,23 @@ fn resolve_command_response_row(
}
}
json::Value::Array(values) => {
if type_container.is_list() {
let array_values: Vec<IndexMap<String, ndc::models::RowFieldValue>> =
// There can be cases when the command returns an array of objects,
// but the type container is not a list. This can happen when the
// command is used in a relationship whose RHS returns a list of objects
// which can have the same value for the field on which the relationship
// is defined.
// In case the container is not a list, we take the first object from
// the array and use that as the value for the relationship otherwise
// we return the array of objects.
let array_values: Vec<IndexMap<String, ndc::models::RowFieldValue>> =
json::from_value(json::Value::Array(values.to_vec()))?;
if type_container.is_list(){
Ok(array_values)
} else {
Err(error::InternalDeveloperError::BadGDCResponse {
summary: "Unable to parse response from NDC, array value expected".into(),
})?
Ok(vec![array_values.into_iter().next().ok_or(error::InternalDeveloperError::BadGDCResponse {
summary: "Unable to parse response from NDC, rowset is empty".into(),
})?])
}
}
}
@ -519,28 +549,3 @@ fn traverse_path_and_insert_value(
}
Ok(())
}
/*
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::remote_joins::types::{JoinLocations, Location, RemoteJoin};
use super::foo;
#[test]
fn test_collect_arguments() {
let location = Location {
join_node: Some(RemoteJoin {
})
};
let join_locations = JoinLocations {
locations: HashMap::from_iter([("weather", location)])
};
let lhs_response = todo!();
let x = foo(lhs_response, join_locations,);
}
}
*/

View File

@ -1,10 +1,14 @@
use indexmap::IndexMap;
use lang_graphql::ast::common::{TypeContainer, TypeName};
use ndc_client as ndc;
use open_dds;
use open_dds::arguments::ArgumentName;
use open_dds::commands::CommandName;
use open_dds::types::FieldName;
use std::collections::{BTreeMap, HashMap};
use crate::metadata::resolved;
use crate::metadata::resolved::subgraph::Qualified;
use crate::metadata::resolved::types::FieldMapping;
use crate::utils::json_ext::ValueExt;
@ -44,24 +48,17 @@ pub struct Location<T> {
pub rest: JoinLocations<T>,
}
/*
impl<T> JoinLocations<T> {
pub fn map<U, F>(self, mut f: F) -> JoinLocations<U>
where
F: FnMut(T) -> U,
{
let mut new_join_locations = JoinLocations::new();
for (key, location) in self.locations {
let new_node = location.join_node.map(&mut f);
let new_location = Location {
join_node: new_node,
rest: location.rest.map(&mut f),
};
new_join_locations.locations.insert(key, new_location);
}
new_join_locations
}
}*/
/// Represents how to process the remote join response.
#[derive(Debug, Clone, PartialEq)]
pub enum ResponseType {
Array {
is_nullable: bool,
},
Command {
command_name: Qualified<CommandName>,
type_container: TypeContainer<TypeName>,
},
}
/// Contains information to be captured for a join node
#[derive(Debug, Clone, PartialEq)]
@ -72,13 +69,29 @@ pub struct RemoteJoin<'s> {
pub target_ndc_ir: ndc::models::QueryRequest,
/// Mapping of the fields in source (LHS) to fields in target (RHS). TODO:
/// add more details about the type contents
pub join_columns: HashMap<SourceFieldName, (SourceFieldAlias, TargetField)>,
pub join_mapping: HashMap<SourceFieldName, (SourceFieldAlias, TargetField)>,
/// Represents how to process the join response.
pub process_response_as: ResponseType,
/// Represents the type of the remote join
pub remote_join_type: RemoteJoinType,
}
pub type SourceFieldName = FieldName;
pub type SourceFieldAlias = String;
pub type SourceField = (FieldName, FieldMapping);
pub type TargetField = (FieldName, FieldMapping);
#[derive(Debug, Clone, PartialEq)]
pub enum TargetField {
ModelField((FieldName, FieldMapping)),
CommandField(ArgumentName),
}
#[derive(Debug, Clone, PartialEq)]
pub enum RemoteJoinType {
ToModel,
ToCommand,
}
pub type JoinId = i16;
pub type Arguments = HashMap<Argument, ArgumentId>;

View File

@ -257,7 +257,7 @@
"name": "get_movie_by_id",
"description": "Get movie by ID",
"arguments": {
"id": {
"movie_id": {
"description": "the id of the movie to fetch",
"type": {
"type": "named",
@ -360,6 +360,29 @@
}
}
}
},
{
"name": "get_actors_by_movie_id",
"description": "Get all actors from a movie by movie ID",
"arguments": {
"movie_id": {
"description": "the id of the movie to fetch the actors from",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "actor"
}
}
}
}
],
"procedures": [
@ -479,4 +502,4 @@
]
}
]
}
}

View File

@ -271,12 +271,6 @@
"type": "named",
"name": "int8"
}
},
"_neq": {
"argument_type": {
"type": "named",
"name": "String"
}
}
},
"update_operators": {}

View File

@ -248,6 +248,56 @@
}
}
},
{
"name": "get_movie_by_id",
"description": "Get movie by ID",
"arguments": {
"movie_id": {
"description": "the id of the movie to fetch",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "movie"
}
}
},
{
"name": "get_all_actors",
"description": "Get list of all actors",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "actor"
}
}
}
},
{
"name": "get_all_movies",
"description": "Get list of all movies",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "movie"
}
}
}
},
{
"name": "get_actors_by_name",
"description": "Get actors by name",
@ -267,6 +317,29 @@
"name": "actor"
}
}
},
{
"name": "actor_names_by_movie",
"description": "Get actor names by movie ID",
"arguments": {
"movie_id": {
"description": "the id of the movie to fetch",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "String"
}
}
}
}
],
"procedures": [
@ -780,4 +853,4 @@
}
}
}
]
]

View File

@ -0,0 +1,79 @@
[
{
"data": {
"getAllActors": [
{
"MovieFromActor": {
"movie_id": 1,
"rating": 4,
"title": "Titanic"
},
"actor_id": 1,
"name": "Leonardo DiCaprio"
},
{
"MovieFromActor": {
"movie_id": 1,
"rating": 4,
"title": "Titanic"
},
"actor_id": 2,
"name": "Kate Winslet"
},
{
"MovieFromActor": {
"movie_id": 2,
"rating": 5,
"title": "Slumdog Millionaire"
},
"actor_id": 3,
"name": "Irfan Khan"
},
{
"MovieFromActor": {
"movie_id": 3,
"rating": 4,
"title": "Godfather"
},
"actor_id": 4,
"name": "Al Pacino"
},
{
"MovieFromActor": {
"movie_id": 3,
"rating": 4,
"title": "Godfather"
},
"actor_id": 5,
"name": "Robert De Niro"
},
{
"MovieFromActor": {
"movie_id": 4,
"rating": 5,
"title": "Shawshank Redemption"
},
"actor_id": 6,
"name": "Morgan Freeman"
},
{
"MovieFromActor": {
"movie_id": 5,
"rating": 4,
"title": "Schindler's List"
},
"actor_id": 7,
"name": "Ben Kingsley"
}
]
}
},
{
"data": null,
"errors": [
{
"message": "validation failed: field rating on type CommandMovie is not allowed for user"
}
]
}
]

View File

@ -0,0 +1,302 @@
{
"version": "v2",
"subgraphs": [
{
"name": "movie_namespace",
"objects": [
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "String",
"representation": "String",
"graphql": {
"comparisonExpressionTypeName": "String_Comparison_Exp"
}
}
},
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "Int",
"representation": "Int"
}
},
{
"kind": "ObjectType",
"version": "v1",
"definition": {
"name": "commandMovie",
"fields": [
{
"name": "movie_id",
"type": "Int!"
},
{
"name": "title",
"type": "String!"
},
{
"name": "rating",
"type": "Int!"
}
],
"graphql": {
"typeName": "CommandMovie"
}
}
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "commandMovie",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"movie_id",
"title",
"rating"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"movie_id",
"title"
]
}
}
]
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_movie_by_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_movie_by_id",
"arguments": [
{
"name": "movie_id",
"type": "Int!"
}
],
"outputType": "commandMovie",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_movie_by_id"
},
"typeMapping": {
"commandMovie": {
"fieldMapping": {
"movie_id": {
"column": "id"
},
"title": {
"column": "title"
},
"rating": {
"column": "rating"
}
}
}
},
"argumentMapping": {
"movie_id": "id"
}
},
"graphql": {
"rootFieldName": "getMovieById",
"rootFieldKind": "Query"
}
}
}
]
},
{
"name": "actor_namespace",
"objects": [
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "String",
"representation": "String",
"graphql": {
"comparisonExpressionTypeName": "String_Comparison_Exp"
}
}
},
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "Int",
"representation": "Int"
}
},
{
"kind": "ObjectType",
"version": "v1",
"definition": {
"name": "commandActor",
"fields": [
{
"name": "actor_id",
"type": "Int!"
},
{
"name": "name",
"type": "String!"
},
{
"name": "movie_id",
"type": "Int!"
}
],
"graphql": {
"typeName": "CommandActor"
}
}
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "commandActor",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"actor_id",
"name",
"movie_id"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"actor_id",
"name",
"movie_id"
]
}
}
]
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_all_actors",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_all_actors",
"arguments": [],
"outputType": "[commandActor]",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_all_actors"
},
"typeMapping": {
"commandActor": {
"fieldMapping": {
"actor_id": {
"column": "id"
},
"name": {
"column": "name"
},
"movie_id": {
"column": "movie_id"
}
}
}
}
},
"graphql": {
"rootFieldName": "getAllActors",
"rootFieldKind": "Query"
}
}
},
{
"definition": {
"source": "commandActor",
"name": "MovieFromActor",
"target": {
"command": {
"name": "get_movie_by_id",
"subgraph": "movie_namespace"
}
},
"mapping": [
{
"source": {
"fieldPath": [
{
"fieldName": "movie_id"
}
]
},
"target": {
"argument": {
"argumentName": "movie_id"
}
}
}
]
},
"version": "v1",
"kind": "Relationship"
}
]
}
]
}

View File

@ -0,0 +1,959 @@
{
"version": "v2",
"subgraphs": [
{
"name": "movie_namespace",
"objects": [
{
"kind": "DataConnector",
"version": "v2",
"definition": {
"name": "custom",
"url": {
"singleUrl": {
"value": "http://custom_connector:8101"
}
},
"headers": {},
"schema": {
"scalar_types": {
"Actor_Name": {
"aggregate_functions": {},
"comparison_operators": {}
},
"Int": {
"aggregate_functions": {
"max": {
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "Int"
}
}
},
"min": {
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "Int"
}
}
}
},
"comparison_operators": {}
},
"String": {
"aggregate_functions": {},
"comparison_operators": {
"like": {
"argument_type": {
"type": "named",
"name": "String"
}
}
}
}
},
"object_types": {
"actor": {
"description": "An actor",
"fields": {
"id": {
"description": "The actor's primary key",
"type": {
"type": "named",
"name": "Int"
}
},
"movie_id": {
"description": "The actor's movie ID",
"type": {
"type": "named",
"name": "Int"
}
},
"name": {
"description": "The actor's name",
"type": {
"type": "named",
"name": "String"
}
}
}
},
"movie": {
"description": "A movie",
"fields": {
"id": {
"description": "The movie's primary key",
"type": {
"type": "named",
"name": "Int"
}
},
"rating": {
"description": "The movie's rating",
"type": {
"type": "named",
"name": "Int"
}
},
"title": {
"description": "The movie's title",
"type": {
"type": "named",
"name": "String"
}
}
}
},
"name_query": {
"description": "parameters for querying by name",
"fields": {
"first_name": {
"description": "The actor's first name or null to match any first name",
"type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "String"
}
}
},
"last_name": {
"description": "The actor's last name or null to match any last",
"type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "String"
}
}
}
}
}
},
"collections": [
{
"name": "actors",
"description": "A collection of actors",
"arguments": {},
"type": "actor",
"uniqueness_constraints": {
"ActorByID": {
"unique_columns": [
"id"
]
}
},
"foreign_keys": {}
},
{
"name": "movies",
"description": "A collection of movies",
"arguments": {},
"type": "movie",
"uniqueness_constraints": {
"MovieByID": {
"unique_columns": [
"id"
]
}
},
"foreign_keys": {}
},
{
"name": "actors_by_movie",
"description": "Actors parameterized by movie",
"arguments": {
"movie_id": {
"type": {
"type": "named",
"name": "Int"
}
}
},
"type": "actor",
"uniqueness_constraints": {},
"foreign_keys": {}
},
{
"name": "movies_by_actor_name",
"description": "Movies filtered by actor name search parameters",
"arguments": {
"actor_name": {
"description": "the actor name components to search by",
"type": {
"type": "named",
"name": "name_query"
}
}
},
"type": "movie",
"uniqueness_constraints": {},
"foreign_keys": {}
}
],
"functions": [
{
"name": "latest_actor_id",
"description": "Get the ID of the most recent actor",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "Int"
}
}
},
{
"name": "latest_actor_name",
"description": "Get the name of the most recent actor",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "Actor_Name"
}
}
},
{
"name": "latest_actor",
"description": "Get the most recent actor",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "get_actor_by_id",
"description": "Get actor by ID",
"arguments": {
"id": {
"description": "the id of the actor to fetch",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "get_actors_by_name",
"description": "Get actors by name",
"arguments": {
"name": {
"description": "the name components to search by",
"type": {
"type": "named",
"name": "name_query"
}
}
},
"result_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "actor_names_by_movie",
"description": "Get actor names by movie ID",
"arguments": {
"movie_id": {
"description": "the id of the movie to fetch",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "String"
}
}
}
},
{
"name": "get_all_actors",
"description": "Get list of all actors",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "actor"
}
}
}
},
{
"name": "get_actors_by_movie_id_bounds",
"description": "Get all actors within a given lower and upper movie id bound",
"arguments": {
"lower_bound": {
"description": "the lower bound for movie id",
"type": {
"type": "named",
"name": "Int"
}
},
"upper_bound": {
"description": "the upper bound for movie id",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "actor"
}
}
}
},
{
"name": "get_movie_by_id",
"description": "Get movie by ID",
"arguments": {
"id": {
"description": "the id of the movie to fetch",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "movie"
}
}
}
],
"procedures": [
{
"name": "upsert_actor",
"description": "Insert or update an actor",
"arguments": {
"actor": {
"description": "The actor to insert or update",
"type": {
"type": "named",
"name": "actor"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "update_actor_name_by_id",
"description": "Update an actor name given the ID and new name",
"arguments": {
"id": {
"description": "the id of the actor to update",
"type": {
"type": "named",
"name": "Int"
}
},
"name": {
"description": "the new name of the actor",
"type": {
"type": "named",
"name": "String"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "noop_procedure",
"description": "Procedure which does not perform any actual mutuations on the data",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "String"
}
}
},
{
"name": "uppercase_actor_name_by_id",
"description": "Uppercase an actor name given the ID",
"arguments": {
"id": {
"description": "the id of the actor to update",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "uppercase_all_actor_names",
"description": "Uppercase all actor names",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "actor"
}
}
}
},
{
"name": "uppercase_all_actor_names_return_names_list",
"description": "Uppercase all actor names and return a list of the updated names",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "String"
}
}
}
}
]
}
}
}
]
},
{
"name": "actor_namespace",
"objects": [
{
"kind": "DataConnector",
"version": "v2",
"definition": {
"name": "custom",
"url": {
"singleUrl": {
"value": "http://custom_connector:8101"
}
},
"headers": {},
"schema": {
"scalar_types": {
"Actor_Name": {
"aggregate_functions": {},
"comparison_operators": {}
},
"Int": {
"aggregate_functions": {
"max": {
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "Int"
}
}
},
"min": {
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "Int"
}
}
}
},
"comparison_operators": {}
},
"String": {
"aggregate_functions": {},
"comparison_operators": {
"like": {
"argument_type": {
"type": "named",
"name": "String"
}
}
}
}
},
"object_types": {
"actor": {
"description": "An actor",
"fields": {
"id": {
"description": "The actor's primary key",
"type": {
"type": "named",
"name": "Int"
}
},
"movie_id": {
"description": "The actor's movie ID",
"type": {
"type": "named",
"name": "Int"
}
},
"name": {
"description": "The actor's name",
"type": {
"type": "named",
"name": "String"
}
}
}
},
"movie": {
"description": "A movie",
"fields": {
"id": {
"description": "The movie's primary key",
"type": {
"type": "named",
"name": "Int"
}
},
"rating": {
"description": "The movie's rating",
"type": {
"type": "named",
"name": "Int"
}
},
"title": {
"description": "The movie's title",
"type": {
"type": "named",
"name": "String"
}
}
}
},
"name_query": {
"description": "parameters for querying by name",
"fields": {
"first_name": {
"description": "The actor's first name or null to match any first name",
"type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "String"
}
}
},
"last_name": {
"description": "The actor's last name or null to match any last",
"type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "String"
}
}
}
}
}
},
"collections": [
{
"name": "actors",
"description": "A collection of actors",
"arguments": {},
"type": "actor",
"uniqueness_constraints": {
"ActorByID": {
"unique_columns": [
"id"
]
}
},
"foreign_keys": {}
},
{
"name": "movies",
"description": "A collection of movies",
"arguments": {},
"type": "movie",
"uniqueness_constraints": {
"MovieByID": {
"unique_columns": [
"id"
]
}
},
"foreign_keys": {}
},
{
"name": "actors_by_movie",
"description": "Actors parameterized by movie",
"arguments": {
"movie_id": {
"type": {
"type": "named",
"name": "Int"
}
}
},
"type": "actor",
"uniqueness_constraints": {},
"foreign_keys": {}
},
{
"name": "movies_by_actor_name",
"description": "Movies filtered by actor name search parameters",
"arguments": {
"actor_name": {
"description": "the actor name components to search by",
"type": {
"type": "named",
"name": "name_query"
}
}
},
"type": "movie",
"uniqueness_constraints": {},
"foreign_keys": {}
}
],
"functions": [
{
"name": "latest_actor_id",
"description": "Get the ID of the most recent actor",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "Int"
}
}
},
{
"name": "latest_actor_name",
"description": "Get the name of the most recent actor",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "Actor_Name"
}
}
},
{
"name": "latest_actor",
"description": "Get the most recent actor",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "get_actor_by_id",
"description": "Get actor by ID",
"arguments": {
"id": {
"description": "the id of the actor to fetch",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "get_actors_by_name",
"description": "Get actors by name",
"arguments": {
"name": {
"description": "the name components to search by",
"type": {
"type": "named",
"name": "name_query"
}
}
},
"result_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "actor_names_by_movie",
"description": "Get actor names by movie ID",
"arguments": {
"movie_id": {
"description": "the id of the movie to fetch",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "String"
}
}
}
},
{
"name": "get_all_actors",
"description": "Get list of all actors",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "actor"
}
}
}
},
{
"name": "get_actors_by_movie_id_bounds",
"description": "Get all actors within a given lower and upper movie id bound",
"arguments": {
"lower_bound": {
"description": "the lower bound for movie id",
"type": {
"type": "named",
"name": "Int"
}
},
"upper_bound": {
"description": "the upper bound for movie id",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "actor"
}
}
}
},
{
"name": "get_movie_by_id",
"description": "Get movie by ID",
"arguments": {
"id": {
"description": "the id of the movie to fetch",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "movie"
}
}
}
],
"procedures": [
{
"name": "upsert_actor",
"description": "Insert or update an actor",
"arguments": {
"actor": {
"description": "The actor to insert or update",
"type": {
"type": "named",
"name": "actor"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "update_actor_name_by_id",
"description": "Update an actor name given the ID and new name",
"arguments": {
"id": {
"description": "the id of the actor to update",
"type": {
"type": "named",
"name": "Int"
}
},
"name": {
"description": "the new name of the actor",
"type": {
"type": "named",
"name": "String"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "noop_procedure",
"description": "Procedure which does not perform any actual mutuations on the data",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "String"
}
}
},
{
"name": "uppercase_actor_name_by_id",
"description": "Uppercase an actor name given the ID",
"arguments": {
"id": {
"description": "the id of the actor to update",
"type": {
"type": "named",
"name": "Int"
}
}
},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "named",
"name": "actor"
}
}
},
{
"name": "uppercase_all_actor_names",
"description": "Uppercase all actor names",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "actor"
}
}
}
},
{
"name": "uppercase_all_actor_names_return_names_list",
"description": "Uppercase all actor names and return a list of the updated names",
"arguments": {},
"result_type": {
"type": "nullable",
"underlying_type": {
"type": "array",
"element_type": {
"type": "named",
"name": "String"
}
}
}
}
]
}
}
}
]
}
]
}

View File

@ -0,0 +1,11 @@
query MyQuery {
getAllActors {
MovieFromActor {
movie_id
rating
title
}
actor_id
name
}
}

View File

@ -0,0 +1,4 @@
[
{ "x-hasura-role": "admin" },
{ "x-hasura-role": "user" }
]

View File

@ -0,0 +1,21 @@
[
{
"data": {
"getActorById": {
"getMovieFromActor": {
"rating": 4,
"title": "Titanic"
},
"name": "Leonardo DiCaprio"
}
}
},
{
"data": null,
"errors": [
{
"message": "validation failed: field rating on type CommandMovie is not allowed for user"
}
]
}
]

View File

@ -0,0 +1,214 @@
{
"version": "v2",
"subgraphs": [
{
"name": "default",
"objects": [
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "commandActor",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"actor_id",
"name",
"movie_id"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"actor_id",
"name",
"movie_id"
]
}
}
]
}
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "commandMovie",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"movie_id",
"title",
"rating"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"movie_id",
"title"
]
}
}
]
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_actor_by_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_actor_by_id",
"arguments": [
{
"name": "actor_id",
"type": "Int!"
}
],
"outputType": "commandActor",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_actor_by_id"
},
"typeMapping": {
"commandActor": {
"fieldMapping": {
"actor_id": {
"column": "id"
},
"name": {
"column": "name"
},
"movie_id": {
"column": "movie_id"
}
}
}
},
"argumentMapping": {
"actor_id": "id"
}
},
"graphql": {
"rootFieldName": "getActorById",
"rootFieldKind": "Query"
}
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_movie_by_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_movie_by_id",
"arguments": [
{
"name": "movie_id",
"type": "Int!"
}
],
"outputType": "commandMovie",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_movie_by_id"
},
"typeMapping": {
"commandMovie": {
"fieldMapping": {
"movie_id": {
"column": "id"
},
"title": {
"column": "title"
},
"rating": {
"column": "rating"
}
}
}
},
"argumentMapping": {
"movie_id": "movie_id"
}
},
"graphql": {
"rootFieldName": "getMovieById",
"rootFieldKind": "Query"
}
}
},
{
"definition": {
"source": "commandActor",
"name": "getMovieFromActor",
"target": {
"command": {
"name": "get_movie_by_id"
}
},
"mapping": [
{
"source": {
"fieldPath": [
{
"fieldName": "movie_id"
}
]
},
"target": {
"argument": {
"argumentName": "movie_id"
}
}
}
]
},
"version": "v1",
"kind": "Relationship"
}
]
}
]
}

View File

@ -0,0 +1,54 @@
[
{
"data": {
"getMovieById": {
"getActorsFromMovie": [
{
"getMovieFromActor": {
"getActorsFromMovie": [
{
"getMovieFromActor": {
"rating": 4,
"title": "Titanic"
}
},
{
"getMovieFromActor": {
"rating": 4,
"title": "Titanic"
}
}
]
}
},
{
"getMovieFromActor": {
"getActorsFromMovie": [
{
"getMovieFromActor": {
"rating": 4,
"title": "Titanic"
}
},
{
"getMovieFromActor": {
"rating": 4,
"title": "Titanic"
}
}
]
}
}
]
}
}
},
{
"data": null,
"errors": [
{
"message": "validation failed: field rating on type CommandMovie is not allowed for user"
}
]
}
]

View File

@ -0,0 +1,302 @@
{
"version": "v2",
"subgraphs": [
{
"name": "default",
"objects": [
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "commandActor",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"actor_id",
"name",
"movie_id"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"actor_id",
"name",
"movie_id"
]
}
}
]
}
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "commandMovie",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"movie_id",
"title",
"rating"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"movie_id",
"title"
]
}
}
]
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_actor_by_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_actor_by_id",
"arguments": [
{
"name": "actor_id",
"type": "Int!"
}
],
"outputType": "commandActor",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_actor_by_id"
},
"typeMapping": {
"commandActor": {
"fieldMapping": {
"actor_id": {
"column": "id"
},
"name": {
"column": "name"
},
"movie_id": {
"column": "movie_id"
}
}
}
},
"argumentMapping": {
"actor_id": "id"
}
},
"graphql": {
"rootFieldName": "getActorById",
"rootFieldKind": "Query"
}
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_movie_by_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_movie_by_id",
"arguments": [
{
"name": "movie_id",
"type": "Int!"
}
],
"outputType": "commandMovie",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_movie_by_id"
},
"typeMapping": {
"commandMovie": {
"fieldMapping": {
"movie_id": {
"column": "id"
},
"title": {
"column": "title"
},
"rating": {
"column": "rating"
}
}
}
},
"argumentMapping": {
"movie_id": "movie_id"
}
},
"graphql": {
"rootFieldName": "getMovieById",
"rootFieldKind": "Query"
}
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_actors_by_movie_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_actors_by_movie_id",
"arguments": [
{
"name": "movie_id",
"type": "Int!"
}
],
"outputType": "[commandActor]",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_actors_by_movie_id"
},
"typeMapping": {
"commandActor": {
"fieldMapping": {
"actor_id": {
"column": "id"
},
"name": {
"column": "name"
},
"movie_id": {
"column": "movie_id"
}
}
}
},
"argumentMapping": {
"movie_id": "movie_id"
}
},
"graphql": {
"rootFieldName": "getAllActorsOfMovie",
"rootFieldKind": "Query"
}
}
},
{
"definition": {
"source": "commandActor",
"name": "getMovieFromActor",
"target": {
"command": {
"name": "get_movie_by_id"
}
},
"mapping": [
{
"source": {
"fieldPath": [
{
"fieldName": "movie_id"
}
]
},
"target": {
"argument": {
"argumentName": "movie_id"
}
}
}
]
},
"version": "v1",
"kind": "Relationship"
},
{
"definition": {
"source": "commandMovie",
"name": "getActorsFromMovie",
"target": {
"command": {
"name": "get_actors_by_movie_id"
}
},
"mapping": [
{
"source": {
"fieldPath": [
{
"fieldName": "movie_id"
}
]
},
"target": {
"argument": {
"argumentName": "movie_id"
}
}
}
]
},
"version": "v1",
"kind": "Relationship"
}
]
}
]
}

View File

@ -0,0 +1,14 @@
query MyQuery {
getMovieById(movie_id: 1) {
getActorsFromMovie {
getMovieFromActor {
getActorsFromMovie {
getMovieFromActor {
rating
title
}
}
}
}
}
}

View File

@ -0,0 +1,8 @@
[
{
"x-hasura-role": "admin"
},
{
"x-hasura-role": "user"
}
]

View File

@ -0,0 +1,54 @@
[
{
"data": {
"getMovieById": {
"getActorsFromMovie": [
{
"MovieFromActor": {
"getActorsFromMovie": [
{
"MovieFromActor": {
"movie_id": 1,
"rating": 4
}
},
{
"MovieFromActor": {
"movie_id": 1,
"rating": 4
}
}
]
}
},
{
"MovieFromActor": {
"getActorsFromMovie": [
{
"MovieFromActor": {
"movie_id": 1,
"rating": 4
}
},
{
"MovieFromActor": {
"movie_id": 1,
"rating": 4
}
}
]
}
}
]
}
}
},
{
"data": null,
"errors": [
{
"message": "validation failed: field rating on type CommandMovie is not allowed for user"
}
]
}
]

View File

@ -0,0 +1,391 @@
{
"version": "v2",
"subgraphs": [
{
"name": "movie_namespace",
"objects": [
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "String",
"representation": "String",
"graphql": {
"comparisonExpressionTypeName": "String_Comparison_Exp"
}
}
},
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "Int",
"representation": "Int"
}
},
{
"kind": "ObjectType",
"version": "v1",
"definition": {
"name": "commandMovie",
"fields": [
{
"name": "movie_id",
"type": "Int!"
},
{
"name": "title",
"type": "String!"
},
{
"name": "rating",
"type": "Int!"
}
],
"graphql": {
"typeName": "CommandMovie"
}
}
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "commandMovie",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"movie_id",
"title",
"rating"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"movie_id",
"title"
]
}
}
]
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_movie_by_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_movie_by_id",
"arguments": [
{
"name": "movie_id",
"type": "Int!"
}
],
"outputType": "commandMovie",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_movie_by_id"
},
"typeMapping": {
"commandMovie": {
"fieldMapping": {
"movie_id": {
"column": "id"
},
"title": {
"column": "title"
},
"rating": {
"column": "rating"
}
}
}
},
"argumentMapping": {
"movie_id": "movie_id"
}
},
"graphql": {
"rootFieldName": "getMovieById",
"rootFieldKind": "Query"
}
}
},
{
"definition": {
"source": "commandMovie",
"name": "getActorsFromMovie",
"target": {
"command": {
"name": "get_actors_by_movie_id",
"subgraph": "actor_namespace"
}
},
"mapping": [
{
"source": {
"fieldPath": [
{
"fieldName": "movie_id"
}
]
},
"target": {
"argument": {
"argumentName": "movie_id"
}
}
}
]
},
"version": "v1",
"kind": "Relationship"
}
]
},
{
"name": "actor_namespace",
"objects": [
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "String",
"representation": "String",
"graphql": {
"comparisonExpressionTypeName": "String_Comparison_Exp"
}
}
},
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "Int",
"representation": "Int"
}
},
{
"kind": "ObjectType",
"version": "v1",
"definition": {
"name": "commandActor",
"fields": [
{
"name": "actor_id",
"type": "Int!"
},
{
"name": "name",
"type": "String!"
},
{
"name": "movie_id",
"type": "Int!"
}
],
"graphql": {
"typeName": "CommandActor"
}
}
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "commandActor",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"actor_id",
"name",
"movie_id"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"actor_id",
"name",
"movie_id"
]
}
}
]
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_all_actors",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_all_actors",
"arguments": [],
"outputType": "[commandActor]",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_all_actors"
},
"typeMapping": {
"commandActor": {
"fieldMapping": {
"actor_id": {
"column": "id"
},
"name": {
"column": "name"
},
"movie_id": {
"column": "movie_id"
}
}
}
}
},
"graphql": {
"rootFieldName": "getAllActors",
"rootFieldKind": "Query"
}
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_actors_by_movie_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_actors_by_movie_id",
"arguments": [
{
"name": "movie_id",
"type": "Int!"
}
],
"outputType": "[commandActor]",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_actors_by_movie_id"
},
"typeMapping": {
"commandActor": {
"fieldMapping": {
"actor_id": {
"column": "id"
},
"name": {
"column": "name"
},
"movie_id": {
"column": "movie_id"
}
}
}
},
"argumentMapping": {
"movie_id": "movie_id"
}
},
"graphql": {
"rootFieldName": "getAllActorsOfMovie",
"rootFieldKind": "Query"
}
}
},
{
"definition": {
"source": "commandActor",
"name": "MovieFromActor",
"target": {
"command": {
"name": "get_movie_by_id",
"subgraph": "movie_namespace"
}
},
"mapping": [
{
"source": {
"fieldPath": [
{
"fieldName": "movie_id"
}
]
},
"target": {
"argument": {
"argumentName": "movie_id"
}
}
}
]
},
"version": "v1",
"kind": "Relationship"
}
]
}
]
}

View File

@ -0,0 +1,14 @@
query MyQuery {
getMovieById(movie_id: 1) {
getActorsFromMovie {
MovieFromActor {
getActorsFromMovie {
MovieFromActor {
movie_id
rating
}
}
}
}
}
}

View File

@ -0,0 +1,4 @@
[
{ "x-hasura-role": "admin" },
{ "x-hasura-role": "user" }
]

View File

@ -0,0 +1,9 @@
query MyQuery {
getActorById(actor_id: 1) {
getMovieFromActor {
rating
title
}
name
}
}

View File

@ -0,0 +1,8 @@
[
{
"x-hasura-role": "admin"
},
{
"x-hasura-role": "user"
}
]

View File

@ -288,7 +288,7 @@
}
},
"argumentMapping": {
"movie_id": "id"
"movie_id": "movie_id"
}
},
"graphql": {

View File

@ -0,0 +1,54 @@
[
{
"data": {
"Analytics": [
{
"MovieFromCommand": {
"movie_id": 1,
"title": "Titanic",
"rating": 4
},
"analytics_id": 1,
"movie_id": 1,
"total_votes": 578
},
{
"MovieFromCommand": {
"movie_id": 2,
"title": "Slumdog Millionaire",
"rating": 5
},
"analytics_id": 2,
"movie_id": 2,
"total_votes": 432
},
{
"MovieFromCommand": {
"movie_id": 3,
"title": "Godfather",
"rating": 4
},
"analytics_id": 3,
"movie_id": 3,
"total_votes": 849
}
]
}
},
{
"data": {
"Analytics": [
{
"MovieFromCommand": {
"movie_id": 1,
"title": "Titanic",
"rating": 4
},
"analytics_id": 1,
"movie_id": 1,
"total_votes": 578
}
]
}
}
]

View File

@ -0,0 +1,628 @@
[
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "Actor_Name",
"representation": "Actor_Name"
}
},
{
"kind": "ScalarType",
"version": "v1",
"definition": {
"name": "Actor_Name",
"graphql": {
"typeName": "Actor_Name"
}
}
},
{
"kind": "ObjectType",
"version": "v1",
"definition": {
"name": "movie_analytics",
"fields": [
{
"name": "analytics_id",
"type": "Int!"
},
{
"name": "movie_id",
"type": "Int!"
},
{
"name": "num_users_faved",
"type": "Int"
},
{
"name": "num_users_watchlisted",
"type": "Int"
},
{
"name": "num_views_day",
"type": "Int"
},
{
"name": "num_votes_day",
"type": "Int"
},
{
"name": "prev_day_scores",
"type": "Int"
},
{
"name": "total_votes",
"type": "Int"
}
],
"globalIdFields": [
"analytics_id"
],
"graphql": {
"typeName": "MovieAnalytics"
}
}
},
{
"kind": "Model",
"version": "v1",
"definition": {
"name": "MovieAnalytics",
"objectType": "movie_analytics",
"globalIdSource": true,
"source": {
"dataConnectorName": "db",
"collection": "movie_analytics",
"typeMapping": {
"movie_analytics": {
"fieldMapping": {
"analytics_id": {
"column": "id"
},
"movie_id": {
"column": "movie_id"
},
"num_users_faved": {
"column": "num_users_faved"
},
"num_users_watchlisted": {
"column": "num_users_watchlisted"
},
"num_views_day": {
"column": "num_views_day"
},
"num_votes_day": {
"column": "num_votes_day"
},
"prev_day_scores": {
"column": "prev_day_scores"
},
"total_votes": {
"column": "total_votes"
}
}
}
}
},
"filterableFields": [
{
"fieldName": "analytics_id",
"operators": {
"enableAll": true
}
},
{
"fieldName": "movie_id",
"operators": {
"enableAll": true
}
},
{
"fieldName": "num_users_faved",
"operators": {
"enableAll": true
}
},
{
"fieldName": "num_users_watchlisted",
"operators": {
"enableAll": true
}
},
{
"fieldName": "num_views_day",
"operators": {
"enableAll": true
}
},
{
"fieldName": "num_votes_day",
"operators": {
"enableAll": true
}
},
{
"fieldName": "prev_day_scores",
"operators": {
"enableAll": true
}
},
{
"fieldName": "total_votes",
"operators": {
"enableAll": true
}
}
],
"orderableFields": [
{
"fieldName": "analytics_id",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "movie_id",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "num_users_faved",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "num_users_watchlisted",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "num_views_day",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "num_votes_day",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "prev_day_scores",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "total_votes",
"orderByDirections": {
"enableAll": true
}
}
],
"graphql": {
"selectUniques": [
{
"queryRootField": "AnalyticsById",
"uniqueIdentifier": [
"analytics_id"
]
}
],
"selectMany": {
"queryRootField": "Analytics"
},
"filterExpressionType": "AnalyticsFilterExp",
"orderByExpressionType": "AnalyticsOrderBy"
}
}
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "movie_analytics",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"analytics_id",
"movie_id",
"num_users_faved",
"num_users_watchlisted",
"num_views_day",
"num_votes_day",
"total_votes"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"analytics_id",
"movie_id",
"num_users_faved",
"num_views_day",
"total_votes"
]
}
}
]
}
},
{
"kind": "ModelPermissions",
"version": "v1",
"definition": {
"modelName": "MovieAnalytics",
"permissions": [
{
"role": "admin",
"select": {
"filter": null
}
},
{
"role": "user",
"select": {
"filter": {
"fieldComparison": {
"field": "movie_id",
"operator": "_eq",
"value": {
"sessionVariable": "x-hasura-user-id"
}
}
}
}
}
]
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_latest_actor_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_latest_actor_id",
"arguments": [],
"outputType": "Int",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "latest_actor_id"
}
},
"graphql": {
"rootFieldName": "getLatestActorId",
"rootFieldKind": "Query"
}
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_latest_actor_name",
"permissions": [
{
"role": "admin",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_latest_actor_name",
"arguments": [],
"outputType": "Actor_Name",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "latest_actor_name"
}
},
"graphql": {
"rootFieldName": "getLatestActorName",
"rootFieldKind": "Query"
}
}
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "commandActor",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"actor_id",
"name",
"movie_id"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"actor_id",
"name",
"movie_id"
]
}
}
]
}
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "commandMovie",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"movie_id",
"title",
"rating"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"movie_id",
"title",
"rating"
]
}
}
]
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_actor_by_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_actor_by_id",
"arguments": [
{
"name": "actor_id",
"type": "Int!"
}
],
"outputType": "commandActor",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_actor_by_id"
},
"typeMapping": {
"commandActor": {
"fieldMapping": {
"actor_id": {
"column": "id"
},
"name": {
"column": "name"
},
"movie_id": {
"column": "movie_id"
}
}
}
},
"argumentMapping": {
"actor_id": "id"
}
},
"graphql": {
"rootFieldName": "getActorById",
"rootFieldKind": "Query"
}
}
},
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "String",
"representation": "String",
"graphql": {
"comparisonExpressionTypeName": "String_Comparison_Exp"
}
}
},
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "Int",
"representation": "Int"
}
},
{
"kind": "ObjectType",
"version": "v1",
"definition": {
"name": "commandMovie",
"fields": [
{
"name": "movie_id",
"type": "Int!"
},
{
"name": "title",
"type": "String!"
},
{
"name": "rating",
"type": "Int!"
}
],
"graphql": {
"typeName": "CommandMovie"
}
}
},
{
"kind": "ObjectType",
"version": "v1",
"definition": {
"name": "commandActor",
"fields": [
{
"name": "actor_id",
"type": "Int!"
},
{
"name": "name",
"type": "String!"
},
{
"name": "movie_id",
"type": "Int!"
}
],
"graphql": {
"typeName": "CommandActor"
}
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_movie_by_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_movie_by_id",
"arguments": [
{
"name": "movie_id",
"type": "Int!"
}
],
"outputType": "commandMovie",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_movie_by_id"
},
"typeMapping": {
"commandMovie": {
"fieldMapping": {
"movie_id": {
"column": "id"
},
"title": {
"column": "title"
},
"rating": {
"column": "rating"
}
}
}
},
"argumentMapping": {
"movie_id": "movie_id"
}
},
"graphql": {
"rootFieldName": "getMovieById",
"rootFieldKind": "Query"
}
}
},
{
"kind": "Relationship",
"definition": {
"source": "movie_analytics",
"name": "MovieFromCommand",
"target": {
"command": {
"name": "get_movie_by_id"
}
},
"mapping": [
{
"source": {
"fieldPath": [
{
"fieldName": "movie_id"
}
]
},
"target": {
"argument": {
"argumentName": "movie_id"
}
}
}
]
},
"version": "v1"
}
]

View File

@ -0,0 +1,136 @@
[
{
"data": {
"happy": {
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"rating": 4,
"title": "Titanic"
}
}
]
}
}
]
},
"unhappy_1": {
"AnalyticsFromMovie": []
},
"unhappy_2": null,
"array": [
{
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"rating": 4,
"title": "Titanic"
}
}
]
}
}
]
},
{
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"rating": 5,
"title": "Slumdog Millionaire"
}
}
]
}
}
]
},
{
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"rating": 4,
"title": "Godfather"
}
}
]
}
}
]
},
{
"AnalyticsFromMovie": []
},
{
"AnalyticsFromMovie": []
}
]
}
},
{
"data": {
"happy": {
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"rating": 4,
"title": "Titanic"
}
}
]
}
}
]
},
"unhappy_1": {
"AnalyticsFromMovie": []
},
"unhappy_2": null,
"array": [
{
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"AnalyticsFromMovie": [
{
"MovieFromCommand": {
"rating": 4,
"title": "Titanic"
}
}
]
}
}
]
},
{
"AnalyticsFromMovie": []
},
{
"AnalyticsFromMovie": []
},
{
"AnalyticsFromMovie": []
},
{
"AnalyticsFromMovie": []
}
]
}
}
]

View File

@ -0,0 +1,517 @@
[
{
"kind": "ObjectType",
"version": "v1",
"definition": {
"name": "movie_analytics",
"fields": [
{
"name": "analytics_id",
"type": "Int!"
},
{
"name": "movie_id",
"type": "Int!"
},
{
"name": "num_users_faved",
"type": "Int"
},
{
"name": "num_users_watchlisted",
"type": "Int"
},
{
"name": "num_views_day",
"type": "Int"
},
{
"name": "num_votes_day",
"type": "Int"
},
{
"name": "prev_day_scores",
"type": "Int"
},
{
"name": "total_votes",
"type": "Int"
}
],
"globalIdFields": [
"analytics_id"
],
"graphql": {
"typeName": "MovieAnalytics"
}
}
},
{
"kind": "Model",
"version": "v1",
"definition": {
"name": "MovieAnalytics",
"objectType": "movie_analytics",
"globalIdSource": true,
"source": {
"dataConnectorName": "db",
"collection": "movie_analytics",
"typeMapping": {
"movie_analytics": {
"fieldMapping": {
"analytics_id": {
"column": "id"
},
"movie_id": {
"column": "movie_id"
},
"num_users_faved": {
"column": "num_users_faved"
},
"num_users_watchlisted": {
"column": "num_users_watchlisted"
},
"num_views_day": {
"column": "num_views_day"
},
"num_votes_day": {
"column": "num_votes_day"
},
"prev_day_scores": {
"column": "prev_day_scores"
},
"total_votes": {
"column": "total_votes"
}
}
}
}
},
"filterableFields": [
{
"fieldName": "analytics_id",
"operators": {
"enableAll": true
}
},
{
"fieldName": "movie_id",
"operators": {
"enableAll": true
}
},
{
"fieldName": "num_users_faved",
"operators": {
"enableAll": true
}
},
{
"fieldName": "num_users_watchlisted",
"operators": {
"enableAll": true
}
},
{
"fieldName": "num_views_day",
"operators": {
"enableAll": true
}
},
{
"fieldName": "num_votes_day",
"operators": {
"enableAll": true
}
},
{
"fieldName": "prev_day_scores",
"operators": {
"enableAll": true
}
},
{
"fieldName": "total_votes",
"operators": {
"enableAll": true
}
}
],
"orderableFields": [
{
"fieldName": "analytics_id",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "movie_id",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "num_users_faved",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "num_users_watchlisted",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "num_views_day",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "num_votes_day",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "prev_day_scores",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "total_votes",
"orderByDirections": {
"enableAll": true
}
}
],
"graphql": {
"selectUniques": [
{
"queryRootField": "AnalyticsById",
"uniqueIdentifier": [
"analytics_id"
]
}
],
"selectMany": {
"queryRootField": "Analytics"
},
"filterExpressionType": "AnalyticsFilterExp",
"orderByExpressionType": "AnalyticsOrderBy"
}
}
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "movie_analytics",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"analytics_id",
"movie_id",
"num_users_faved",
"num_users_watchlisted",
"num_views_day",
"num_votes_day",
"total_votes"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"analytics_id",
"movie_id",
"num_users_faved",
"num_views_day",
"total_votes"
]
}
}
]
}
},
{
"kind": "ModelPermissions",
"version": "v1",
"definition": {
"modelName": "MovieAnalytics",
"permissions": [
{
"role": "admin",
"select": {
"filter": null
}
},
{
"role": "user",
"select": {
"filter": {
"fieldComparison": {
"field": "movie_id",
"operator": "_eq",
"value": {
"sessionVariable": "x-hasura-user-id"
}
}
}
}
}
]
}
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "commandMovie",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"movie_id",
"title",
"rating"
]
}
},
{
"role": "user",
"output": {
"allowedFields": [
"movie_id",
"title",
"rating"
]
}
}
]
}
},
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "String",
"representation": "String",
"graphql": {
"comparisonExpressionTypeName": "String_Comparison_Exp"
}
}
},
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "custom",
"dataConnectorScalarType": "Int",
"representation": "Int"
}
},
{
"kind": "ObjectType",
"version": "v1",
"definition": {
"name": "commandMovie",
"fields": [
{
"name": "movie_id",
"type": "Int!"
},
{
"name": "title",
"type": "String!"
},
{
"name": "rating",
"type": "Int!"
}
],
"graphql": {
"typeName": "CommandMovie"
}
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_movie_by_id",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_movie_by_id",
"arguments": [
{
"name": "movie_id",
"type": "Int!"
}
],
"outputType": "commandMovie",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_movie_by_id"
},
"typeMapping": {
"commandMovie": {
"fieldMapping": {
"movie_id": {
"column": "id"
},
"title": {
"column": "title"
},
"rating": {
"column": "rating"
}
}
}
},
"argumentMapping": {
"movie_id": "movie_id"
}
},
"graphql": {
"rootFieldName": "getMovieById",
"rootFieldKind": "Query"
}
}
},
{
"kind": "CommandPermissions",
"version": "v1",
"definition": {
"commandName": "get_all_movies",
"permissions": [
{
"role": "admin",
"allowExecution": true
},
{
"role": "user",
"allowExecution": true
}
]
}
},
{
"kind": "Command",
"version": "v1",
"definition": {
"name": "get_all_movies",
"arguments": [],
"outputType": "[commandMovie]",
"source": {
"dataConnectorName": "custom",
"dataConnectorCommand": {
"function": "get_all_movies"
},
"typeMapping": {
"commandMovie": {
"fieldMapping": {
"movie_id": {
"column": "id"
},
"title": {
"column": "title"
},
"rating": {
"column": "rating"
}
}
}
},
"argumentMapping": {}
},
"graphql": {
"rootFieldName": "getAllMovies",
"rootFieldKind": "Query"
}
}
},
{
"kind": "Relationship",
"version": "v1",
"definition": {
"source": "commandMovie",
"name": "AnalyticsFromMovie",
"target": {
"model": {
"name": "MovieAnalytics",
"relationshipType": "Array"
}
},
"mapping": [
{
"source": {
"fieldPath": [
{
"fieldName": "movie_id"
}
]
},
"target": {
"modelField": [
{
"fieldName": "movie_id"
}
]
}
}
]
}
},
{
"kind": "Relationship",
"definition": {
"source": "movie_analytics",
"name": "MovieFromCommand",
"target": {
"command": {
"name": "get_movie_by_id"
}
},
"mapping": [
{
"source": {
"fieldPath": [
{
"fieldName": "movie_id"
}
]
},
"target": {
"argument": {
"argumentName": "movie_id"
}
}
}
]
},
"version": "v1"
}
]

View File

@ -0,0 +1,50 @@
query MyQuery {
happy: getMovieById(movie_id: 1) {
AnalyticsFromMovie {
MovieFromCommand {
AnalyticsFromMovie {
MovieFromCommand {
rating
title
}
}
}
}
}
unhappy_1: getMovieById(movie_id: 4) {
AnalyticsFromMovie {
MovieFromCommand {
AnalyticsFromMovie {
MovieFromCommand {
rating
title
}
}
}
}
}
unhappy_2: getMovieById(movie_id: 10) {
AnalyticsFromMovie {
MovieFromCommand {
AnalyticsFromMovie {
MovieFromCommand {
rating
title
}
}
}
}
}
array: getAllMovies {
AnalyticsFromMovie {
MovieFromCommand {
AnalyticsFromMovie {
MovieFromCommand {
rating
title
}
}
}
}
}
}

View File

@ -0,0 +1,4 @@
[
{"x-hasura-role": "admin"},
{"x-hasura-role": "user", "x-hasura-user-id": "1"}
]

View File

@ -0,0 +1,12 @@
query MyQuery {
Analytics {
MovieFromCommand {
movie_id
title
rating
}
analytics_id
movie_id
total_votes
}
}

View File

@ -0,0 +1,4 @@
[
{"x-hasura-role": "admin"},
{"x-hasura-role": "user", "x-hasura-user-id": "1"}
]

View File

@ -0,0 +1,107 @@
[
{
"data": {
"MovieMany": [
{
"title": "Titanic",
"Actors": [
{
"name": "Leonardo DiCaprio",
"MovieFromCommand": {
"title": "Titanic",
"rating": 4
}
},
{
"name": "Kate Winslet",
"MovieFromCommand": {
"title": "Titanic",
"rating": 4
}
}
]
},
{
"title": "Slumdog Millionaire",
"Actors": [
{
"name": "Irfan Khan",
"MovieFromCommand": {
"title": "Slumdog Millionaire",
"rating": 5
}
}
]
},
{
"title": "Godfather",
"Actors": [
{
"name": "Al Pacino",
"MovieFromCommand": {
"title": "Godfather",
"rating": 4
}
},
{
"name": "Robert De Niro",
"MovieFromCommand": {
"title": "Godfather",
"rating": 4
}
}
]
},
{
"title": "Shawshank Redemption",
"Actors": [
{
"name": "Morgan Freeman",
"MovieFromCommand": {
"title": "Shawshank Redemption",
"rating": 5
}
}
]
},
{
"title": "Schindler's List",
"Actors": [
{
"name": "Ben Kingsley",
"MovieFromCommand": {
"title": "Schindler's List",
"rating": 4
}
}
]
}
]
}
},
{
"data": {
"MovieMany": [
{
"title": "Titanic",
"Actors": [
{
"name": "Leonardo DiCaprio",
"MovieFromCommand": {
"title": "Titanic",
"rating": 4
}
},
{
"name": "Kate Winslet",
"MovieFromCommand": {
"title": "Titanic",
"rating": 4
}
}
]
}
]
}
}
]

View File

@ -0,0 +1,12 @@
query MyQuery {
MovieMany {
title
Actors {
name
MovieFromCommand {
title
rating
}
}
}
}

View File

@ -0,0 +1,4 @@
[
{"x-hasura-role": "admin"},
{"x-hasura-role": "user", "x-hasura-user-id": "1"}
]

View File

@ -1078,7 +1078,8 @@
]
}
},
"function_name": "latest_article"
"function_name": "latest_article",
"variable_arguments": {}
}
}
}

View File

@ -67,6 +67,34 @@ fn test_local_relationships_model_to_command() {
);
}
#[test]
fn test_local_relationships_command_to_command() {
let test_path_string = "execute/relationships/command_to_command";
let common_metadata_path_string = "execute/common_metadata/custom_connector_schema.json";
let common_command_metadata_path_string = "execute/common_metadata/command_metadata.json";
common::test_execution_expectation(
test_path_string,
&[
common_metadata_path_string,
common_command_metadata_path_string,
],
);
}
#[test]
fn test_local_mutually_recursive_relationships_to_command() {
let test_path_string = "execute/relationships/command_to_command/mutually_recursive";
let common_metadata_path_string = "execute/common_metadata/custom_connector_schema.json";
let common_command_metadata_path_string = "execute/common_metadata/command_metadata.json";
common::test_execution_expectation(
test_path_string,
&[
common_metadata_path_string,
common_command_metadata_path_string,
],
);
}
#[test]
fn test_local_relationships_permissions_target_model_filter_predicate() {
let test_path_string = "execute/relationships/permissions/target_model_filter_predicate";
@ -92,6 +120,23 @@ fn test_relationships_command_to_model_across_namespace() {
common::test_execution_expectation(test_path_string, &[common_metadata_path_string]);
}
#[test]
fn test_relationships_command_to_command_across_namespace() {
let test_path_string = "execute/relationships/command_to_command/across_namespace";
let common_metadata_path_string =
"execute/relationships/command_to_command/across_namespace/namespaced_connectors.json";
common::test_execution_expectation(test_path_string, &[common_metadata_path_string]);
}
#[test]
fn test_remote_mutually_recursive_relationships_to_command_across_namespace() {
let test_path_string =
"execute/relationships/command_to_command/mutually_recursive_across_namespace";
let common_metadata_path_string =
"execute/relationships/command_to_command/mutually_recursive_across_namespace/namespaced_connectors.json";
common::test_execution_expectation(test_path_string, &[common_metadata_path_string]);
}
#[test]
fn test_remote_relationships_model_to_model_array() {
let test_path_string = "execute/remote_relationships/array";
@ -99,6 +144,21 @@ fn test_remote_relationships_model_to_model_array() {
common::test_execution_expectation(test_path_string, &[common_metadata_path_string]);
}
#[test]
fn test_remote_relationships_model_to_command_array() {
let test_path_string = "execute/remote_relationships/command/model_to_command";
let common_metadata_path_string = "execute/common_metadata/two_connectors_schema.json";
common::test_execution_expectation(test_path_string, &[common_metadata_path_string]);
}
#[test]
fn test_remote_mutually_recursive_relationships_model_to_command() {
let test_path_string =
"execute/remote_relationships/command/model_to_command/mutually_recursive";
let common_metadata_path_string = "execute/common_metadata/two_connectors_schema.json";
common::test_execution_expectation(test_path_string, &[common_metadata_path_string]);
}
#[test]
fn test_remote_relationships_model_to_model_array_with_arguments() {
let test_path_string = "execute/remote_relationships/array/arguments";
@ -113,6 +173,13 @@ fn test_remote_relationships_remote_in_local() {
common::test_execution_expectation(test_path_string, &[common_metadata_path_string]);
}
#[test]
fn test_remote_relationships_model_to_command_remote_in_local() {
let test_path_string = "execute/remote_relationships/command/remote_in_local";
let common_metadata_path_string = "execute/common_metadata/two_connectors_schema.json";
common::test_execution_expectation(test_path_string, &[common_metadata_path_string]);
}
#[test]
fn test_remote_relationships_mutually_recursive() {
let test_path_string = "execute/remote_relationships/mutually_recursive";