Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

accept predicate arguments #92

Merged
merged 21 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
This changelog documents the changes between release versions.

## [Unreleased]
- Accept predicate arguments in native mutations and native queries ([#92](https://github.com/hasura/ndc-mongodb/pull/92))

## [1.0.0] - 2024-07-09

Expand Down
47 changes: 47 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 0 additions & 18 deletions crates/configuration/src/native_mutation.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::BTreeMap;

use itertools::Itertools as _;
use mongodb::{bson, options::SelectionCriteria};
use ndc_models as ndc;
use ndc_query_plan as plan;
Expand All @@ -17,7 +16,6 @@ use crate::{serialized, MongoScalarType};
#[derive(Clone, Debug)]
pub struct NativeMutation {
pub result_type: plan::Type<MongoScalarType>,
pub arguments: BTreeMap<ndc::ArgumentName, plan::Type<MongoScalarType>>,
pub command: bson::Document,
pub selection_criteria: Option<SelectionCriteria>,
pub description: Option<String>,
Expand All @@ -28,21 +26,6 @@ impl NativeMutation {
object_types: &BTreeMap<ndc::ObjectTypeName, ndc::ObjectType>,
input: serialized::NativeMutation,
) -> Result<NativeMutation, QueryPlanError> {
let arguments = input
.arguments
.into_iter()
.map(|(name, object_field)| {
Ok((
name,
inline_object_types(
object_types,
&object_field.r#type.into(),
MongoScalarType::lookup_scalar_type,
)?,
)) as Result<_, QueryPlanError>
})
.try_collect()?;

let result_type = inline_object_types(
object_types,
&input.result_type.into(),
Expand All @@ -51,7 +34,6 @@ impl NativeMutation {

Ok(NativeMutation {
result_type,
arguments,
command: input.command,
selection_criteria: input.selection_criteria,
description: input.description,
Expand Down
24 changes: 3 additions & 21 deletions crates/configuration/src/native_query.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use std::collections::BTreeMap;

use itertools::Itertools as _;
use mongodb::bson;
use ndc_models as ndc;
use ndc_query_plan as plan;
use plan::{inline_object_types, QueryPlanError};
use plan::QueryPlanError;
use schemars::JsonSchema;
use serde::Deserialize;

use crate::{serialized, MongoScalarType};
use crate::serialized;

/// Internal representation of Native Queries. For doc comments see
/// [crate::serialized::NativeQuery]
Expand All @@ -20,36 +19,19 @@ use crate::{serialized, MongoScalarType};
pub struct NativeQuery {
pub representation: NativeQueryRepresentation,
pub input_collection: Option<ndc::CollectionName>,
pub arguments: BTreeMap<ndc::ArgumentName, plan::Type<MongoScalarType>>,
pub result_document_type: ndc::ObjectTypeName,
pub pipeline: Vec<bson::Document>,
pub description: Option<String>,
}

impl NativeQuery {
pub fn from_serialized(
object_types: &BTreeMap<ndc::ObjectTypeName, ndc::ObjectType>,
_object_types: &BTreeMap<ndc::ObjectTypeName, ndc::ObjectType>,
input: serialized::NativeQuery,
) -> Result<NativeQuery, QueryPlanError> {
let arguments = input
.arguments
.into_iter()
.map(|(name, object_field)| {
Ok((
name,
inline_object_types(
object_types,
&object_field.r#type.into(),
MongoScalarType::lookup_scalar_type,
)?,
)) as Result<_, QueryPlanError>
})
.try_collect()?;

Ok(NativeQuery {
representation: input.representation,
input_collection: input.input_collection,
arguments,
result_document_type: input.result_document_type,
pipeline: input.pipeline,
description: input.description,
Expand Down
10 changes: 10 additions & 0 deletions crates/configuration/src/schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ pub enum Type {
ArrayOf(Box<Type>),
/// A nullable form of any of the other types
Nullable(Box<Type>),
/// A predicate type for a given object type
#[serde(rename_all = "camelCase")]
Predicate {
/// The object type name
object_type_name: ndc_models::ObjectTypeName,
},
}

impl Type {
Expand All @@ -42,6 +48,7 @@ impl Type {
Type::ExtendedJSON => Type::ExtendedJSON,
Type::Scalar(s) => Type::Scalar(s),
Type::Object(o) => Type::Object(o),
Type::Predicate { object_type_name } => Type::Predicate { object_type_name },
Type::ArrayOf(a) => Type::ArrayOf(Box::new((*a).normalize_type())),
Type::Nullable(n) => match *n {
Type::ExtendedJSON => Type::ExtendedJSON,
Expand Down Expand Up @@ -84,6 +91,9 @@ impl From<Type> for ndc_models::Type {
Type::Nullable(t) => ndc_models::Type::Nullable {
underlying_type: Box::new(map_normalized_type(*t)),
},
Type::Predicate { object_type_name } => {
ndc_models::Type::Predicate { object_type_name }
}
}
}
map_normalized_type(t.normalize_type())
Expand Down
1 change: 1 addition & 0 deletions crates/integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ndc-models = { workspace = true }
ndc-test-helpers = { path = "../ndc-test-helpers" }

anyhow = "1"
assert_json = "^0.1"
insta = { version = "^1.38", features = ["yaml"] }
reqwest = { version = "^0.12.4", features = ["json"] }
serde = { version = "1", features = ["derive"] }
Expand Down
2 changes: 2 additions & 0 deletions crates/integration-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod tests;

mod connector;
mod graphql;
mod validators;

use std::env;

Expand All @@ -16,6 +17,7 @@ use url::Url;

pub use self::connector::{run_connector_query, ConnectorQueryRequest};
pub use self::graphql::{graphql_query, GraphQLRequest, GraphQLResponse};
pub use self::validators::*;

const CONNECTOR_URL: &str = "CONNECTOR_URL";
const CONNECTOR_CHINOOK_URL: &str = "CONNECTOR_CHINOOK_URL";
Expand Down
56 changes: 55 additions & 1 deletion crates/integration-tests/src/tests/native_mutation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{graphql_query, GraphQLResponse};
use crate::{graphql_query, non_empty_array, GraphQLResponse};
use assert_json::{assert_json, validators};
use insta::assert_yaml_snapshot;
use serde_json::json;

Expand Down Expand Up @@ -57,3 +58,56 @@ async fn updates_with_native_mutation() -> anyhow::Result<()> {
);
Ok(())
}

#[tokio::test]
async fn accepts_predicate_argument() -> anyhow::Result<()> {
let album_id = 3;

let mutation_resp = graphql_query(
r#"
mutation($albumId: Int!) {
chinook_updateTrackPrices(newPrice: "11.99", where: {albumId: {_eq: $albumId}}) {
n
ok
}
}
"#,
)
.variables(json!({ "albumId": album_id }))
.run()
.await?;

assert_eq!(mutation_resp.errors, None);
assert_json!(mutation_resp.data, {
"chinook_updateTrackPrices": {
"ok": 1.0,
"n": validators::i64(|n| if n > &0 {
Ok(())
} else {
Err("expected number of updated documents to be non-zero".to_string())
})
}
});

let tracks_resp = graphql_query(
r#"
query($albumId: Int!) {
track(where: {albumId: {_eq: $albumId}}, order_by: {id: Asc}) {
name
unitPrice
}
}
"#,
)
.variables(json!({ "albumId": album_id }))
.run()
.await?;

assert_json!(tracks_resp.data, {
"track": non_empty_array().and(validators::array_for_each(validators::object([
("unitPrice".to_string(), Box::new(validators::eq("11.99")) as Box<dyn Validator>)
].into())))
});

Ok(())
}
22 changes: 22 additions & 0 deletions crates/integration-tests/src/validators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use assert_json::{Error, Validator};
use serde_json::Value;

pub fn non_empty_array() -> NonEmptyArrayValidator {
NonEmptyArrayValidator
}

pub struct NonEmptyArrayValidator;

impl Validator for NonEmptyArrayValidator {
fn validate<'a>(&self, value: &'a Value) -> Result<(), Error<'a>> {
if let Value::Array(xs) = value {
if xs.is_empty() {
Err(Error::InvalidValue(value, "non-empty array".to_string()))
} else {
Ok(())
}
} else {
Err(Error::InvalidType(value, "array".to_string()))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,13 @@ pub enum ErrorResponseType {
MutationPermissionCheckFailure,
}

impl ToString for ErrorResponseType {
fn to_string(&self) -> String {
impl std::fmt::Display for ErrorResponseType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UncaughtError => String::from("uncaught-error"),
Self::MutationConstraintViolation => String::from("mutation-constraint-violation"),
Self::UncaughtError => f.write_str("uncaught-error"),
Self::MutationConstraintViolation => f.write_str("mutation-constraint-violation"),
Self::MutationPermissionCheckFailure => {
String::from("mutation-permission-check-failure")
f.write_str("mutation-permission-check-failure")
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions crates/mongodb-agent-common/src/mongo_query_plan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,16 @@ fn scalar_type_name(t: &Type) -> Option<&'static str> {
}

pub type Aggregate = ndc_query_plan::Aggregate<MongoConfiguration>;
pub type Argument = ndc_query_plan::Argument<MongoConfiguration>;
pub type Arguments = ndc_query_plan::Arguments<MongoConfiguration>;
pub type ComparisonTarget = ndc_query_plan::ComparisonTarget<MongoConfiguration>;
pub type ComparisonValue = ndc_query_plan::ComparisonValue<MongoConfiguration>;
pub type ExistsInCollection = ndc_query_plan::ExistsInCollection;
pub type Expression = ndc_query_plan::Expression<MongoConfiguration>;
pub type Field = ndc_query_plan::Field<MongoConfiguration>;
pub type MutationOperation = ndc_query_plan::MutationOperation<MongoConfiguration>;
pub type MutationPlan = ndc_query_plan::MutationPlan<MongoConfiguration>;
pub type MutationProcedureArgument = ndc_query_plan::MutationProcedureArgument<MongoConfiguration>;
pub type NestedField = ndc_query_plan::NestedField<MongoConfiguration>;
pub type NestedArray = ndc_query_plan::NestedArray<MongoConfiguration>;
pub type NestedObject = ndc_query_plan::NestedObject<MongoConfiguration>;
Expand Down
Loading
Loading