Skip to content

Commit

Permalink
feat: enable field exclusion for the json protocol (prisma#4807)
Browse files Browse the repository at this point in the history
  • Loading branch information
Weakky authored Apr 12, 2024
1 parent 6d35870 commit b339b9d
Show file tree
Hide file tree
Showing 21 changed files with 468 additions and 270 deletions.
4 changes: 3 additions & 1 deletion psl/psl-core/src/common/preview_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ features!(
UncheckedScalarInputs,
Views,
RelationJoins,
PrismaSchemaFolder
PrismaSchemaFolder,
OmitApi,
);

/// Generator preview features (alphabetically sorted)
Expand All @@ -95,6 +96,7 @@ pub const ALL_PREVIEW_FEATURES: FeatureMap = FeatureMap {
| Tracing
| Views
| RelationJoins
| OmitApi
}),
deprecated: enumflags2::make_bitflags!(PreviewFeature::{
AtomicNumberOperations
Expand Down
2 changes: 1 addition & 1 deletion psl/psl/tests/config/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ fn nice_error_for_unknown_generator_preview_feature() {
.unwrap_err();

let expectation = expect![[r#"
[1;91merror[0m: [1mThe preview feature "foo" is not known. Expected one of: deno, driverAdapters, fullTextIndex, fullTextSearch, metrics, multiSchema, nativeDistinct, postgresqlExtensions, tracing, views, relationJoins[0m
[1;91merror[0m: [1mThe preview feature "foo" is not known. Expected one of: deno, driverAdapters, fullTextIndex, fullTextSearch, metrics, multiSchema, nativeDistinct, postgresqlExtensions, tracing, views, relationJoins, omitApi[0m
--> schema.prisma:3
 | 
 2 |  provider = "prisma-client-js"
Expand Down
2 changes: 1 addition & 1 deletion query-engine/core-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ edition = "2021"
[dev-dependencies]
dissimilar = "1.0.4"
user-facing-errors = { path = "../../libs/user-facing-errors" }
request-handlers = { path = "../request-handlers" }
request-handlers = { path = "../request-handlers", features=["native"] }
query-core = { path = "../core", features = ["metrics"] }
schema = { path = "../schema" }
psl.workspace = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@
"name": "dob",
"typeName": "DateTime",
"isRelation": false
},
{
"name": "posts",
"typeName": "Post[]",
"isRelation": true
},
{
"name": "_count",
"typeName": "UserCountOutputType",
"isRelation": false
}
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,14 @@ model User {
id Int @id
email String
dob DateTime?
posts Post[]
}

model Post {
id Int @id
name String
userId Int?
user User? @relation(fields: [userId], references: [id])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"action": "findMany",
"modelName": "User",
"query": {
"selection": {
"id": true,
"unknown_field": false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"action": "findMany",
"modelName": "User",
"query": {
"selection": {
"id": true,
"posts": {
"selection": {
"id": true,
"unknown_field": false
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@
"Null"
],
"required": false
},
{
"name": "posts",
"typeNames": [
"PostListRelationFilter"
],
"required": false
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@
"name": "dob",
"typeName": "DateTime",
"isRelation": false
},
{
"name": "posts",
"typeName": "Post[]",
"isRelation": true
},
{
"name": "_count",
"typeName": "UserCountOutputType",
"isRelation": false
}
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"is_panic": false,
"message": "Field 'unknown_field' not found in enclosing type 'User'",
"meta": {
"kind": "UnknownSelectionField",
"outputType": {
"name": "User",
"fields": [
{
"name": "id",
"typeName": "Int",
"isRelation": false
},
{
"name": "email",
"typeName": "String",
"isRelation": false
},
{
"name": "dob",
"typeName": "DateTime",
"isRelation": false
},
{
"name": "posts",
"typeName": "Post[]",
"isRelation": true
},
{
"name": "_count",
"typeName": "UserCountOutputType",
"isRelation": false
}
]
},
"selectionPath": [
"findManyUser",
"unknown_field"
]
},
"error_code": "P2009"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"is_panic": false,
"message": "Field 'unknown_field' not found in enclosing type 'Post'",
"meta": {
"kind": "UnknownSelectionField",
"outputType": {
"name": "Post",
"fields": [
{
"name": "id",
"typeName": "Int",
"isRelation": false
},
{
"name": "name",
"typeName": "String",
"isRelation": false
},
{
"name": "userId",
"typeName": "Int",
"isRelation": false
},
{
"name": "user",
"typeName": "User",
"isRelation": true
}
]
},
"selectionPath": [
"findManyUser",
"posts",
"unknown_field"
]
},
"error_code": "P2009"
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@
"Null"
],
"required": false
},
{
"name": "posts",
"typeNames": [
"PostListRelationFilter"
],
"required": false
}
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"is_panic": false,
"message": "Field 'notAField' not found in enclosing type 'User'",
"meta": {
"kind": "UnknownSelectionField",
"outputType": {
"name": "User",
"fields": [
{
"name": "id",
"typeName": "Int",
"isRelation": false
},
{
"name": "email",
"typeName": "String",
"isRelation": false
},
{
"name": "dob",
"typeName": "DateTime",
"isRelation": false
},
{
"name": "posts",
"typeName": "Post[]",
"isRelation": true
},
{
"name": "_count",
"typeName": "UserCountOutputType",
"isRelation": false
}
]
},
"selectionPath": [
"findManyUser",
"notAField"
]
},
"error_code": "P2009"
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@
"name": "dob",
"typeName": "DateTime",
"isRelation": false
},
{
"name": "posts",
"typeName": "Post[]",
"isRelation": true
},
{
"name": "_count",
"typeName": "UserCountOutputType",
"isRelation": false
}
]
},
Expand Down
2 changes: 1 addition & 1 deletion query-engine/core/src/query_document/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ mod transformers;

pub use argument_value::{ArgumentValue, ArgumentValueObject};
pub use operation::Operation;
pub use selection::{In, Selection, SelectionArgument, SelectionSet};
pub use selection::{Exclusion, In, Selection, SelectionArgument, SelectionSet};

pub(crate) use parse_ast::*;
pub(crate) use parser::*;
Expand Down
20 changes: 18 additions & 2 deletions query-engine/core/src/query_document/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ impl QueryDocumentParser {
pub fn parse<'a>(
&self,
selections: &[Selection],
exclusions: Option<&[Exclusion]>,
schema_object: &ObjectType<'a>,
fields: ResolveField<'a, '_>,
query_schema: &'a QuerySchema,
Expand All @@ -33,6 +34,7 @@ impl QueryDocumentParser {
Path::default(),
Path::default(),
selections,
exclusions,
schema_object,
Some(fields),
query_schema,
Expand All @@ -44,11 +46,13 @@ impl QueryDocumentParser {
/// In contrast, nullable and optional types on an input object are separate concepts.
/// The above is the reason we don't need to check nullability here, as it is done by the output
/// validation in the serialization step.
#[allow(clippy::too_many_arguments)]
fn parse_object<'a>(
&self,
selection_path: Path,
argument_path: Path,
selections: &[Selection],
exclusions: Option<&[Exclusion]>,
schema_object: &ObjectType<'a>,
resolve_field: Option<ResolveField<'a, '_>>,
query_schema: &'a QuerySchema,
Expand All @@ -63,6 +67,17 @@ impl QueryDocumentParser {
let resolve_adhoc = move |name: &str| schema_object.find_field(name).cloned();
let resolve_field = resolve_field.unwrap_or(&resolve_adhoc);

if let Some(exclusions) = exclusions {
for exclusion in exclusions {
if resolve_field(&exclusion.name).is_none() {
return Err(ValidationError::unknown_selection_field(
selection_path.add(exclusion.name.to_owned()).segments(),
conversions::schema_object_to_output_type_description(schema_object),
));
}
}
}

selections
.iter()
.map(|selection| {
Expand Down Expand Up @@ -106,17 +121,18 @@ impl QueryDocumentParser {
)
.and_then(move |arguments| {
if !selection.nested_selections().is_empty() && schema_field.field_type().is_scalar() {
Err(ValidationError::selection_set_on_scalar(
return Err(ValidationError::selection_set_on_scalar(
selection.name().to_string(),
selection_path.segments(),
))
));
} else {
// If the output type of the field is an object type of any form, validate the sub selection as well.
let nested_fields = schema_field.field_type().as_object_type().map(|obj| {
self.parse_object(
selection_path.clone(),
argument_path.clone(),
selection.nested_selections(),
selection.nested_exclusions(),
obj,
None,
query_schema,
Expand Down
Loading

0 comments on commit b339b9d

Please sign in to comment.