Relay Spec (#6089)

* Install graphql-relay

* Add relayNodeInterface to ParseGraphQLSchema

* Add support to global id

* Add support to global id in other operations

* Fix sort by glboal id

* Fix where by global id

* Introduce IdWhereInput

* Add Relay object identification tests

* Client mutation id on createFile mutation

* Client mutation id on callCloudCode mutation

* Client mutation id on signUp mutation

* Client mutation id on logIn mutation

* Client mutation id on logOut mutation

* Client mutation id on createClass mutation

* Client mutation id on updateClass mutation

* Client mutation id on deleteClass mutation

* Client mutation id on create object mutation

* Improve Viewer type

* Client mutation id on update object mutation

* Client mutation id on delete object mutation

* Introducing connections

* Fix tests

* Add pagination test

* Fix file location

* Fix postgres tests

* Add comments

* Tests to calculateSkipAndLimit
This commit is contained in:
Antonio Davi Macedo Coelho de Castro
2019-12-01 21:43:08 -08:00
committed by GitHub
parent 67e3c33ffe
commit a9066e20dc
22 changed files with 4685 additions and 2816 deletions

View File

@@ -1,4 +1,5 @@
import Parse from 'parse/node';
import { offsetToCursor, cursorToOffset } from 'graphql-relay';
import rest from '../../rest';
import { transformQueryInputToParse } from '../transformers/query';
@@ -51,8 +52,11 @@ const findObjects = async (
className,
where,
order,
skip,
limit,
skipInput,
first,
after,
last,
before,
keys,
include,
includeAll,
@@ -68,11 +72,52 @@ const findObjects = async (
if (!where) {
where = {};
}
transformQueryInputToParse(where, fields);
transformQueryInputToParse(where, fields, className);
const skipAndLimitCalculation = calculateSkipAndLimit(
skipInput,
first,
after,
last,
before,
config.maxLimit
);
let { skip } = skipAndLimitCalculation;
const { limit, needToPreCount } = skipAndLimitCalculation;
let preCount = undefined;
if (needToPreCount) {
const preCountOptions = {
limit: 0,
count: true,
};
if (readPreference) {
preCountOptions.readPreference = readPreference;
}
if (Object.keys(where).length > 0 && subqueryReadPreference) {
preCountOptions.subqueryReadPreference = subqueryReadPreference;
}
preCount = (
await rest.find(
config,
auth,
className,
where,
preCountOptions,
info.clientSDK
)
).count;
if ((skip || 0) + limit < preCount) {
skip = preCount - limit;
}
}
const options = {};
if (selectedFields.includes('results')) {
if (
selectedFields.find(
field => field.startsWith('edges.') || field.startsWith('pageInfo.')
)
) {
if (limit || limit === 0) {
options.limit = limit;
}
@@ -104,7 +149,12 @@ const findObjects = async (
options.limit = 0;
}
if (selectedFields.includes('count')) {
if (
(selectedFields.includes('count') ||
selectedFields.includes('pageInfo.hasPreviousPage') ||
selectedFields.includes('pageInfo.hasNextPage')) &&
!needToPreCount
) {
options.count = true;
}
@@ -115,7 +165,151 @@ const findObjects = async (
options.subqueryReadPreference = subqueryReadPreference;
}
return rest.find(config, auth, className, where, options, info.clientSDK);
let results, count;
if (options.count || !options.limit || (options.limit && options.limit > 0)) {
const findResult = await rest.find(
config,
auth,
className,
where,
options,
info.clientSDK
);
results = findResult.results;
count = findResult.count;
}
let edges = null;
let pageInfo = null;
if (results) {
edges = results.map((result, index) => ({
cursor: offsetToCursor((skip || 0) + index),
node: result,
}));
pageInfo = {
hasPreviousPage:
((preCount && preCount > 0) || (count && count > 0)) &&
skip !== undefined &&
skip > 0,
startCursor: offsetToCursor(skip || 0),
endCursor: offsetToCursor((skip || 0) + (results.length || 1) - 1),
hasNextPage: (preCount || count) > (skip || 0) + results.length,
};
}
return {
edges,
pageInfo,
count: preCount || count,
};
};
export { getObject, findObjects };
const calculateSkipAndLimit = (
skipInput,
first,
after,
last,
before,
maxLimit
) => {
let skip = undefined;
let limit = undefined;
let needToPreCount = false;
// Validates the skip input
if (skipInput || skipInput === 0) {
if (skipInput < 0) {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'Skip should be a positive number'
);
}
skip = skipInput;
}
// Validates the after param
if (after) {
after = cursorToOffset(after);
if ((!after && after !== 0) || after < 0) {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'After is not a valid cursor'
);
}
// If skip and after are passed, a new skip is calculated by adding them
skip = (skip || 0) + (after + 1);
}
// Validates the first param
if (first || first === 0) {
if (first < 0) {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'First should be a positive number'
);
}
// The first param is translated to the limit param of the Parse legacy API
limit = first;
}
// Validates the before param
if (before || before === 0) {
// This method converts the cursor to the index of the object
before = cursorToOffset(before);
if ((!before && before !== 0) || before < 0) {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'Before is not a valid cursor'
);
}
if ((skip || 0) >= before) {
// If the before index is less then the skip, no objects will be returned
limit = 0;
} else if ((!limit && limit !== 0) || (skip || 0) + limit > before) {
// If there is no limit set, the limit is calculated. Or, if the limit (plus skip) is bigger than the before index, the new limit is set.
limit = before - (skip || 0);
}
}
// Validates the last param
if (last || last === 0) {
if (last < 0) {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'Last should be a positive number'
);
}
if (last > maxLimit) {
// Last can't be bigger than Parse server maxLimit config.
last = maxLimit;
}
if (limit || limit === 0) {
// If there is a previous limit set, it may be adjusted
if (last < limit) {
// if last is less than the current limit
skip = (skip || 0) + (limit - last); // The skip is adjusted
limit = last; // the limit is adjusted
}
} else if (last === 0) {
// No objects will be returned
limit = 0;
} else {
// No previous limit set, the limit will be equal to last and pre count is needed.
limit = last;
needToPreCount = true;
}
}
return {
skip,
limit,
needToPreCount,
};
};
export { getObject, findObjects, calculateSkipAndLimit };