Change order_by to accept a list (#298)

V3_GIT_ORIGIN_REV_ID: 603f6ae4ab8c6505a5484d6b71042b0a7e5aaa8c
This commit is contained in:
Karthikeyan Chinnakonda 2024-01-31 15:20:31 +05:30 committed by hasura-bot
parent 486249902a
commit f65d67bd10
28 changed files with 392 additions and 65 deletions

View File

@ -1,7 +1,9 @@
use std::collections::BTreeMap;
use gdc::models::PathElement;
use lang_graphql::normalized_ast::{self as normalized_ast, InputField};
use ndc_client as gdc;
use serde::Serialize;
@ -29,19 +31,43 @@ pub(crate) fn build_ndc_order_by<'s>(
usage_counts: &mut UsagesCounts,
) -> Result<ResolvedOrderBy<'s>, error::Error> {
match &args_field.value {
normalized_ast::Value::Object(arguments) => {
normalized_ast::Value::List(arguments) => {
let mut ndc_order_elements = Vec::new();
let mut relationships = BTreeMap::new();
// TODO: use argument.values?
for argument in arguments.values() {
let relationship_paths = Vec::new();
let order_by_element = build_ndc_order_by_element(
argument,
relationship_paths,
&mut relationships,
usage_counts,
)?;
ndc_order_elements.extend(order_by_element);
for v in arguments.iter() {
match v {
normalized_ast::Value::Object(arguments) => {
// Check if the input object contains exactly one key-value pair.
// This is done because the users might provide multiple key-value pairs
// in a single input object and the server might interpret it arbitrarily
// since input objects values are unordered key-value pair lists.
if arguments.len() != 1 {
Err(error::Error::ValidationFailed(
lang_graphql::validation::Error::OrderByObjectShouldExactlyHaveOneKeyValuePair,
))?
} else {
let argument = arguments
.first()
.ok_or(error::InternalEngineError::InternalGeneric {
description: "unexpected: could not find the first key-value pair of arguments"
.into(),
})?
.1;
let relationship_paths = Vec::new();
let order_by_element = build_ndc_order_by_element(
argument,
relationship_paths,
&mut relationships,
usage_counts,
)?;
ndc_order_elements.extend(order_by_element);
}
}
_ => Err(error::InternalEngineError::InternalGeneric {
description: "Expected list of input objects value for order_by".into(),
})?,
}
}
Ok(ResolvedOrderBy {
order_by: gdc::models::OrderBy {
@ -51,7 +77,7 @@ pub(crate) fn build_ndc_order_by<'s>(
})
}
_ => Err(error::InternalEngineError::InternalGeneric {
description: "Expected object value for model arguments".into(),
description: "Expected list of input objects value for order_by".into(),
})?,
}
}

View File

@ -89,12 +89,12 @@ pub fn get_order_by_expression_input_field(
types::Annotation::Input(types::InputAnnotation::Model(
types::ModelInputAnnotation::ModelOrderByExpression,
)),
ast::TypeContainer::named_null(builder.register_type(
ast::TypeContainer::list_null(ast::TypeContainer::named_non_null(builder.register_type(
types::TypeId::ModelOrderByExpression {
model_name,
graphql_type_name: order_by_expression_info.order_by_type_name.clone(),
},
)),
))),
None,
gql_schema::DeprecationStatus::NotDeprecated,
)

View File

@ -1,9 +1,9 @@
query {
AuthorMany(order_by: {first_name: Asc}) {
AuthorMany(order_by: [{first_name: Asc}]) {
author_id
first_name
}
a1: AuthorMany(order_by: {first_name: Asc}, limit: 1, offset: 1) {
a1: AuthorMany(order_by: [{first_name: Asc}], limit: 1, offset: 1) {
author_id
first_name
}

View File

@ -1,6 +1,6 @@
query MyQuery {
Track(
order_by: {AlbumId: Asc, TrackId: Desc}
order_by: [{AlbumId: Asc}, {TrackId: Desc}]
where: {_or: [{AlbumId: {_eq: 1}}, {AlbumId: {_eq: 4}}]}
) {
Name

View File

@ -0,0 +1,26 @@
[
{
"data": null,
"errors": [
{
"message": "validation failed: order_by expects a list of input objects with exactly one key-value pair per input object. Please split the input object with multiple key-value pairs into a list of single key-value pair objects."
}
]
},
{
"data": null,
"errors": [
{
"message": "validation failed: order_by expects a list of input objects with exactly one key-value pair per input object. Please split the input object with multiple key-value pairs into a list of single key-value pair objects."
}
]
},
{
"data": null,
"errors": [
{
"message": "validation failed: order_by expects a list of input objects with exactly one key-value pair per input object. Please split the input object with multiple key-value pairs into a list of single key-value pair objects."
}
]
}
]

View File

@ -0,0 +1,235 @@
{
"version": "v2",
"subgraphs": [
{
"name": "default",
"objects": [
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "db",
"dataConnectorScalarType": "String",
"representation": "String",
"graphql": {
"comparisonExpressionTypeName": "String_Comparison_Exp"
}
}
},
{
"kind": "DataConnectorScalarRepresentation",
"version": "v1",
"definition": {
"dataConnectorName": "db",
"dataConnectorScalarType": "Int",
"representation": "Int",
"graphql": {
"comparisonExpressionTypeName": "Int_comparison"
}
}
},
{
"definition": {
"name": "Track",
"fields": [
{
"name": "TrackId",
"type": "Int"
},
{
"name": "Name",
"type": "String"
},
{
"name": "AlbumId",
"type": "Int"
}
],
"graphql": {
"typeName": "Track"
}
},
"version": "v1",
"kind": "ObjectType"
},
{
"definition": {
"name": "Tracks",
"objectType": "Track",
"source": {
"dataConnectorName": "db",
"collection": "Track",
"typeMapping": {
"Track": {
"fieldMapping": {
"TrackId": {
"column": "TrackId"
},
"Name": {
"column": "Name"
},
"AlbumId": {
"column": "AlbumId"
}
}
}
}
},
"graphql": {
"selectUniques": [
{
"queryRootField": "TrackByID",
"uniqueIdentifier": [
"TrackId"
]
}
],
"selectMany": {
"queryRootField": "Track"
},
"filterExpressionType": "Track_Where_Exp",
"orderByExpressionType": "Track_Order_By"
},
"filterableFields": [
{
"fieldName": "TrackId",
"operators": {
"enableAll": true
}
},
{
"fieldName": "Name",
"operators": {
"enableAll": true
}
},
{
"fieldName": "AlbumId",
"operators": {
"enableAll": true
}
}
],
"orderableFields": [
{
"fieldName": "TrackId",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "Name",
"orderByDirections": {
"enableAll": true
}
},
{
"fieldName": "AlbumId",
"orderByDirections": {
"enableAll": true
}
}
]
},
"version": "v1",
"kind": "Model"
},
{
"kind": "TypePermissions",
"version": "v1",
"definition": {
"typeName": "Track",
"permissions": [
{
"role": "admin",
"output": {
"allowedFields": [
"TrackId",
"Name",
"AlbumId"
]
}
},
{
"role": "user_1",
"output": {
"allowedFields": [
"TrackId",
"Name",
"AlbumId"
]
}
},
{
"role": "user_2",
"output": {
"allowedFields": [
"TrackId",
"Name",
"AlbumId"
]
}
}
]
}
},
{
"kind": "ModelPermissions",
"version": "v1",
"definition": {
"modelName": "Tracks",
"permissions": [
{
"role": "admin",
"select": {
"filter": null
}
},
{
"role": "user_1",
"select": {
"filter": {
"fieldComparison": {
"field": "AlbumId",
"operator": "_eq",
"value": {
"sessionVariable": "x-hasura-user-id"
}
}
}
}
},
{
"role": "user_2",
"select": {
"filter": {
"and": [
{
"fieldComparison": {
"field": "Name",
"operator": "_like",
"value": {
"literal": "%Overdose%"
}
}
},
{
"fieldComparison": {
"field": "AlbumId",
"operator": "_eq",
"value": {
"sessionVariable": "x-hasura-user-id"
}
}
}
]
}
}
}
]
}
}
]
}
]
}

View File

@ -0,0 +1,10 @@
query MyQuery {
Track(
order_by: [{AlbumId: Asc, TrackId: Desc}]
) {
Name
TrackId
AlbumId
}
}

View File

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

View File

@ -1,5 +1,5 @@
query MyQuery {
Track(order_by: {Album: {Artist: {ArtistId: Asc}, AlbumId: Asc}}, limit: 15) {
Track(order_by: [{Album: {Artist: {ArtistId: Asc}}}, {AlbumId: Asc}], limit: 15) {
Album {
AlbumId
Artist {
@ -9,7 +9,7 @@ query MyQuery {
}
}
TrackOrderByWithFilter: Track(
order_by: {Album: {Artist: {ArtistId: Desc}, AlbumId: Asc}}
order_by: [{Album: {Artist: {ArtistId: Desc}}}, {AlbumId: Asc}]
where: {AlbumId: {_eq: 2}}
limit: 15
) {

View File

@ -1,12 +1,12 @@
query MyQuery {
Track(order_by: {Album: {ArtistId: Desc}}, limit: 3) {
Track(order_by: [{Album: {ArtistId: Desc}}], limit: 3) {
Album {
ArtistId
Title
}
}
TrackOrderByWithFilter: Track(
order_by: {Album: {ArtistId: Asc}, TrackId: Desc}
order_by: [{Album: {ArtistId: Asc}}, {TrackId: Desc}]
where: {Album: {Artist: {ArtistId: {_eq: 2}}}}
) {
TrackId

View File

@ -1,12 +1,12 @@
query MyQuery {
AuthorMany(order_by: {first_name: Asc}) {
AuthorMany(order_by: [{first_name: Asc}]) {
author_id
first_name
}
ArticlesByAuthorMany(args: {author_id: 2}, order_by: {article_id: Desc}) {
ArticlesByAuthorMany(args: {author_id: 2}, order_by: [{article_id: Desc}]) {
article_id
}
a1: AuthorMany(order_by: {first_name: Desc}, offset: 1, limit: 1) {
a1: AuthorMany(order_by: [{first_name: Desc}], offset: 1, limit: 1) {
author_id
first_name
}

View File

@ -1,12 +1,12 @@
query MyQuery {
AuthorMany(order_by_custom: {first_name: Asc_custom}) {
AuthorMany(order_by_custom: [{first_name: Asc_custom}]) {
author_id
first_name
}
ArticlesByAuthorMany(args_custom: {author_id: 2}, order_by_custom: {article_id: Desc_custom}) {
ArticlesByAuthorMany(args_custom: {author_id: 2}, order_by_custom: [{article_id: Desc_custom}]) {
article_id
}
a1: AuthorMany(order_by_custom: {first_name: Desc_custom}, offset_custom: 1, limit_custom: 1) {
a1: AuthorMany(order_by_custom: [{first_name: Desc_custom}], offset_custom: 1, limit_custom: 1) {
author_id
first_name
}

View File

@ -9,7 +9,7 @@ query MyQuery {
TrackId
}
}
AlbumPredicateOrderBy: Album(limit: 2, order_by: {AlbumId: Desc}) {
AlbumPredicateOrderBy: Album(limit: 2, order_by: [{AlbumId: Desc}]) {
AlbumId
Title
ArtistId

View File

@ -282,9 +282,17 @@
"name": "order_by",
"description": null,
"type": {
"kind": "INPUT_OBJECT",
"name": "Authors_order_by",
"ofType": null
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "Authors_order_by",
"ofType": null
}
}
},
"defaultValue": null
},

View File

@ -306,9 +306,17 @@
"name": "order_by_custom",
"description": null,
"type": {
"kind": "INPUT_OBJECT",
"name": "Authors_order_by",
"ofType": null
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "Authors_order_by",
"ofType": null
}
}
},
"defaultValue": null
},

View File

@ -1,5 +1,5 @@
query MyQuery {
AuthorMany(order_by: {first_name: Asc}) {
AuthorMany(order_by: [{first_name: Asc}]) {
author_id
last_name
}

View File

@ -6,7 +6,7 @@ query {
}
}
AuthorMany(order_by: {author_id: Desc}){
AuthorMany(order_by: [{author_id: Desc}]){
author_id
first_name
}

View File

@ -7,7 +7,7 @@ query {
}
}
AuthorMany(order_by: {author_id: Desc}){
AuthorMany(order_by: [{author_id: Desc}]){
author_id
first_name
}

View File

@ -6,7 +6,7 @@ query {
}
}
AuthorMany(order_by: {author_id: Desc}){
AuthorMany(order_by: [{author_id: Desc}]){
author_id
first_name
}

View File

@ -6,7 +6,7 @@ query {
}
}
AuthorMany(order_by: {author_id: Desc}){
AuthorMany(order_by: [{author_id: Desc}]){
author_id
first_name
}

View File

@ -2,7 +2,7 @@ query MyQuery {
AuthorMany {
first_name
author_id
Articles(limit: 2, offset: 1, order_by: {title: Asc}) {
Articles(limit: 2, offset: 1, order_by: [{title: Asc}]) {
title
article_id
}

View File

@ -2,7 +2,7 @@ query MyQuery {
AuthorMany {
first_name
author_id
Articles(limit_custom: 2, offset_custom: 1, order_by_custom: {title: Asc_custom}, where_custom: {title: {_is_null_custom: false}}) {
Articles(limit_custom: 2, offset_custom: 1, order_by_custom: [{title: Asc_custom}], where_custom: {title: {_is_null_custom: false}}) {
title
article_id
}

View File

@ -2,7 +2,7 @@ query MyQuery {
AuthorMany {
first_name
author_id
Articles(limit: 2, offset: 1, order_by: {title: Asc}) {
Articles(limit: 2, offset: 1, order_by: [{title: Asc}]) {
title
article_id
}

View File

@ -3,7 +3,7 @@ query RemoteJoinsCandidateQuery2 {
id
title
rating
Analytics(limit: 5, order_by: {analytics_id: Asc}) {
Analytics(limit: 5, order_by: [{analytics_id: Asc}]) {
id
num_users_faved
num_views_day

View File

@ -117,6 +117,13 @@ fn test_model_select_many_order_by_multiple_columns() {
common::test_execution_expectation(test_path_string, &[common_metadata_path_string]);
}
#[test]
fn test_model_select_many_order_by_multiple_columns_validation_check() {
let test_path_string = "execute/models/select_many/order_by/order_by_validation_check";
let common_metadata_path_string = "execute/common_metadata/postgres_connector_schema.json";
common::test_execution_expectation(test_path_string, &[common_metadata_path_string]);
}
// Type Permissions
#[test]
fn test_model_select_many_type_permission_order_by() {

View File

@ -3,7 +3,7 @@
name
}
AuthorMany(order_by: {author_id: Desc}){
AuthorMany(order_by: [{author_id: Desc}]){
author_id
first_name
}

View File

@ -31,7 +31,7 @@ query GetUser($gameID: smallint!, $id: Int, $link: String, $steamID: Int) {
lastActivity: last_activity
createdAt: created_at
friendsCount: friends_count
friends(where: {state: {_eq: "ACCEPTED"}}, order_by: {date: desc}, limit: 5) {
friends(where: {state: {_eq: "ACCEPTED"}}, order_by: [{date: desc}], limit: 5) {
...Friend
__typename
}
@ -66,7 +66,7 @@ query GetUser($gameID: smallint!, $id: Int, $link: String, $steamID: Int) {
type
readiness_passed
game_status
teams(order_by: {id: asc}) {
teams(order_by: [{id: asc}]) {
size
score
__typename
@ -74,7 +74,7 @@ query GetUser($gameID: smallint!, $id: Int, $link: String, $steamID: Int) {
created_at
started_at
best_of
maps(order_by: {number: asc}) {
maps(order_by: [{number: asc}]) {
map {
id
name
@ -173,7 +173,7 @@ fragment UserStats on users {
fragment UserBan on users {
bans(
where: {active: {_eq: true}, game_id: {_eq: $gameID}}
order_by: {until: desc}
order_by: [{until: desc}]
) {
id
since
@ -224,7 +224,7 @@ fragment Friend on friends {
fragment UserLastActiveBan on users {
bans(
where: {active: {_eq: true}, game_id: {_eq: $gameID}}
order_by: {length_minutes: desc}
order_by: [{length_minutes: desc}]
limit: 1
) {
id

View File

@ -178,4 +178,6 @@ pub enum Error {
field_name: ast::Name,
argument_name: ast::Name,
},
#[error("order_by expects a list of input objects with exactly one key-value pair per input object. Please split the input object with multiple key-value pairs into a list of single key-value pair objects.")]
OrderByObjectShouldExactlyHaveOneKeyValuePair,
}