* add context for following hooks. 1. beforeDelete 2. afterDelete 3. beforeFind 4. Cloud Function * revert un-necessary code change. * fix: failing test cases. * fix: failing test cases. * fix: failing test cases. * fix: failing test cases. * fix: failing test cases. * fix: failing test cases. * fix: failing test cases. * review changes * revert changes * revert changes * review changes * lint changes * review changes
368 lines
8.6 KiB
JavaScript
368 lines
8.6 KiB
JavaScript
import Parse from 'parse/node';
|
|
import { offsetToCursor, cursorToOffset } from 'graphql-relay';
|
|
import rest from '../../rest';
|
|
import { transformQueryInputToParse } from '../transformers/query';
|
|
|
|
// Eslint/Prettier conflict
|
|
/* eslint-disable*/
|
|
const needToGetAllKeys = (fields, keys, parseClasses) =>
|
|
keys
|
|
? keys.split(',').some((keyName) => {
|
|
const key = keyName.split('.');
|
|
if (fields[key[0]]) {
|
|
if (fields[key[0]].type === 'Pointer') {
|
|
const subClass = parseClasses.find(
|
|
({ className: parseClassName }) =>
|
|
fields[key[0]].targetClass === parseClassName
|
|
);
|
|
if (subClass && subClass.fields[key[1]]) {
|
|
// Current sub key is not custom
|
|
return false;
|
|
}
|
|
} else if (!key[1]) {
|
|
// current key is not custom
|
|
return false;
|
|
}
|
|
}
|
|
// Key not found into Parse Schema so it's custom
|
|
return true;
|
|
})
|
|
: true;
|
|
/* eslint-enable*/
|
|
|
|
const getObject = async (
|
|
className,
|
|
objectId,
|
|
keys,
|
|
include,
|
|
readPreference,
|
|
includeReadPreference,
|
|
config,
|
|
auth,
|
|
info,
|
|
parseClasses
|
|
) => {
|
|
const options = {};
|
|
try {
|
|
if (
|
|
!needToGetAllKeys(
|
|
parseClasses.find(
|
|
({ className: parseClassName }) => className === parseClassName
|
|
).fields,
|
|
keys,
|
|
parseClasses
|
|
)
|
|
) {
|
|
options.keys = keys;
|
|
}
|
|
} catch (e) {
|
|
console.log(e);
|
|
}
|
|
if (include) {
|
|
options.include = include;
|
|
if (includeReadPreference) {
|
|
options.includeReadPreference = includeReadPreference;
|
|
}
|
|
}
|
|
if (readPreference) {
|
|
options.readPreference = readPreference;
|
|
}
|
|
|
|
const response = await rest.get(
|
|
config,
|
|
auth,
|
|
className,
|
|
objectId,
|
|
options,
|
|
info.clientSDK,
|
|
info.context
|
|
);
|
|
|
|
if (!response.results || response.results.length == 0) {
|
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
|
|
}
|
|
|
|
const object = response.results[0];
|
|
if (className === '_User') {
|
|
delete object.sessionToken;
|
|
}
|
|
return object;
|
|
};
|
|
|
|
const findObjects = async (
|
|
className,
|
|
where,
|
|
order,
|
|
skipInput,
|
|
first,
|
|
after,
|
|
last,
|
|
before,
|
|
keys,
|
|
include,
|
|
includeAll,
|
|
readPreference,
|
|
includeReadPreference,
|
|
subqueryReadPreference,
|
|
config,
|
|
auth,
|
|
info,
|
|
selectedFields,
|
|
parseClasses
|
|
) => {
|
|
if (!where) {
|
|
where = {};
|
|
}
|
|
transformQueryInputToParse(where, className, parseClasses);
|
|
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,
|
|
info.context
|
|
)
|
|
).count;
|
|
if ((skip || 0) + limit < preCount) {
|
|
skip = preCount - limit;
|
|
}
|
|
}
|
|
|
|
const options = {};
|
|
|
|
if (
|
|
selectedFields.find(
|
|
(field) => field.startsWith('edges.') || field.startsWith('pageInfo.')
|
|
)
|
|
) {
|
|
if (limit || limit === 0) {
|
|
options.limit = limit;
|
|
} else {
|
|
options.limit = 100;
|
|
}
|
|
if (options.limit !== 0) {
|
|
if (order) {
|
|
options.order = order;
|
|
}
|
|
if (skip) {
|
|
options.skip = skip;
|
|
}
|
|
if (config.maxLimit && options.limit > config.maxLimit) {
|
|
// Silently replace the limit on the query with the max configured
|
|
options.limit = config.maxLimit;
|
|
}
|
|
if (
|
|
!needToGetAllKeys(
|
|
parseClasses.find(
|
|
({ className: parseClassName }) => className === parseClassName
|
|
).fields,
|
|
keys,
|
|
parseClasses
|
|
)
|
|
) {
|
|
options.keys = keys;
|
|
}
|
|
if (includeAll === true) {
|
|
options.includeAll = includeAll;
|
|
}
|
|
if (!options.includeAll && include) {
|
|
options.include = include;
|
|
}
|
|
if ((options.includeAll || options.include) && includeReadPreference) {
|
|
options.includeReadPreference = includeReadPreference;
|
|
}
|
|
}
|
|
} else {
|
|
options.limit = 0;
|
|
}
|
|
|
|
if (
|
|
(selectedFields.includes('count') ||
|
|
selectedFields.includes('pageInfo.hasPreviousPage') ||
|
|
selectedFields.includes('pageInfo.hasNextPage')) &&
|
|
!needToPreCount
|
|
) {
|
|
options.count = true;
|
|
}
|
|
|
|
if (readPreference) {
|
|
options.readPreference = readPreference;
|
|
}
|
|
if (Object.keys(where).length > 0 && subqueryReadPreference) {
|
|
options.subqueryReadPreference = subqueryReadPreference;
|
|
}
|
|
|
|
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,
|
|
info.context
|
|
);
|
|
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,
|
|
};
|
|
};
|
|
|
|
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, needToGetAllKeys };
|