Fix Prettier (#7066)
This commit is contained in:
@@ -2,12 +2,9 @@ var https = require('https'),
|
||||
crypto = require('crypto');
|
||||
var Parse = require('parse/node').Parse;
|
||||
|
||||
var OAuth = function(options) {
|
||||
var OAuth = function (options) {
|
||||
if (!options) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
||||
'No options passed to OAuth'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'No options passed to OAuth');
|
||||
}
|
||||
this.consumer_key = options.consumer_key;
|
||||
this.consumer_secret = options.consumer_secret;
|
||||
@@ -17,22 +14,22 @@ var OAuth = function(options) {
|
||||
this.oauth_params = options.oauth_params || {};
|
||||
};
|
||||
|
||||
OAuth.prototype.send = function(method, path, params, body) {
|
||||
OAuth.prototype.send = function (method, path, params, body) {
|
||||
var request = this.buildRequest(method, path, params, body);
|
||||
// Encode the body properly, the current Parse Implementation don't do it properly
|
||||
return new Promise(function(resolve, reject) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var httpRequest = https
|
||||
.request(request, function(res) {
|
||||
.request(request, function (res) {
|
||||
var data = '';
|
||||
res.on('data', function(chunk) {
|
||||
res.on('data', function (chunk) {
|
||||
data += chunk;
|
||||
});
|
||||
res.on('end', function() {
|
||||
res.on('end', function () {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
})
|
||||
.on('error', function() {
|
||||
.on('error', function () {
|
||||
reject('Failed to make an OAuth request');
|
||||
});
|
||||
if (request.body) {
|
||||
@@ -42,7 +39,7 @@ OAuth.prototype.send = function(method, path, params, body) {
|
||||
});
|
||||
};
|
||||
|
||||
OAuth.prototype.buildRequest = function(method, path, params, body) {
|
||||
OAuth.prototype.buildRequest = function (method, path, params, body) {
|
||||
if (path.indexOf('/') != 0) {
|
||||
path = '/' + path;
|
||||
}
|
||||
@@ -62,12 +59,7 @@ OAuth.prototype.buildRequest = function(method, path, params, body) {
|
||||
oauth_params['oauth_token'] = this.auth_token;
|
||||
}
|
||||
|
||||
request = OAuth.signRequest(
|
||||
request,
|
||||
oauth_params,
|
||||
this.consumer_secret,
|
||||
this.auth_token_secret
|
||||
);
|
||||
request = OAuth.signRequest(request, oauth_params, this.consumer_secret, this.auth_token_secret);
|
||||
|
||||
if (body && Object.keys(body).length > 0) {
|
||||
request.body = OAuth.buildParameterString(body);
|
||||
@@ -75,18 +67,18 @@ OAuth.prototype.buildRequest = function(method, path, params, body) {
|
||||
return request;
|
||||
};
|
||||
|
||||
OAuth.prototype.get = function(path, params) {
|
||||
OAuth.prototype.get = function (path, params) {
|
||||
return this.send('GET', path, params);
|
||||
};
|
||||
|
||||
OAuth.prototype.post = function(path, params, body) {
|
||||
OAuth.prototype.post = function (path, params, body) {
|
||||
return this.send('POST', path, params, body);
|
||||
};
|
||||
|
||||
/*
|
||||
Proper string %escape encoding
|
||||
*/
|
||||
OAuth.encode = function(str) {
|
||||
OAuth.encode = function (str) {
|
||||
// discuss at: http://phpjs.org/functions/rawurlencode/
|
||||
// original by: Brett Zamir (http://brett-zamir.me)
|
||||
// input by: travc
|
||||
@@ -126,25 +118,23 @@ OAuth.version = '1.0';
|
||||
/*
|
||||
Generate a nonce
|
||||
*/
|
||||
OAuth.nonce = function() {
|
||||
OAuth.nonce = function () {
|
||||
var text = '';
|
||||
var possible =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
for (var i = 0; i < 30; i++)
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
for (var i = 0; i < 30; i++) text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
OAuth.buildParameterString = function(obj) {
|
||||
OAuth.buildParameterString = function (obj) {
|
||||
// Sort keys and encode values
|
||||
if (obj) {
|
||||
var keys = Object.keys(obj).sort();
|
||||
|
||||
// Map key=value, join them by &
|
||||
return keys
|
||||
.map(function(key) {
|
||||
.map(function (key) {
|
||||
return key + '=' + OAuth.encode(obj[key]);
|
||||
})
|
||||
.join('&');
|
||||
@@ -157,33 +147,19 @@ OAuth.buildParameterString = function(obj) {
|
||||
Build the signature string from the object
|
||||
*/
|
||||
|
||||
OAuth.buildSignatureString = function(method, url, parameters) {
|
||||
return [
|
||||
method.toUpperCase(),
|
||||
OAuth.encode(url),
|
||||
OAuth.encode(parameters),
|
||||
].join('&');
|
||||
OAuth.buildSignatureString = function (method, url, parameters) {
|
||||
return [method.toUpperCase(), OAuth.encode(url), OAuth.encode(parameters)].join('&');
|
||||
};
|
||||
|
||||
/*
|
||||
Retuns encoded HMAC-SHA1 from key and text
|
||||
*/
|
||||
OAuth.signature = function(text, key) {
|
||||
OAuth.signature = function (text, key) {
|
||||
crypto = require('crypto');
|
||||
return OAuth.encode(
|
||||
crypto
|
||||
.createHmac('sha1', key)
|
||||
.update(text)
|
||||
.digest('base64')
|
||||
);
|
||||
return OAuth.encode(crypto.createHmac('sha1', key).update(text).digest('base64'));
|
||||
};
|
||||
|
||||
OAuth.signRequest = function(
|
||||
request,
|
||||
oauth_parameters,
|
||||
consumer_secret,
|
||||
auth_token_secret
|
||||
) {
|
||||
OAuth.signRequest = function (request, oauth_parameters, consumer_secret, auth_token_secret) {
|
||||
oauth_parameters = oauth_parameters || {};
|
||||
|
||||
// Set default values
|
||||
@@ -224,16 +200,9 @@ OAuth.signRequest = function(
|
||||
// Build the signature string
|
||||
var url = 'https://' + request.host + '' + request.path;
|
||||
|
||||
var signatureString = OAuth.buildSignatureString(
|
||||
request.method,
|
||||
url,
|
||||
parameterString
|
||||
);
|
||||
var signatureString = OAuth.buildSignatureString(request.method, url, parameterString);
|
||||
// Hash the signature string
|
||||
var signatureKey = [
|
||||
OAuth.encode(consumer_secret),
|
||||
OAuth.encode(auth_token_secret),
|
||||
].join('&');
|
||||
var signatureKey = [OAuth.encode(consumer_secret), OAuth.encode(auth_token_secret)].join('&');
|
||||
|
||||
var signature = OAuth.signature(signatureString, signatureKey);
|
||||
|
||||
@@ -246,7 +215,7 @@ OAuth.signRequest = function(
|
||||
// Set the authorization header
|
||||
var authHeader = Object.keys(oauth_parameters)
|
||||
.sort()
|
||||
.map(function(key) {
|
||||
.map(function (key) {
|
||||
var value = oauth_parameters[key];
|
||||
return key + '="' + value + '"';
|
||||
})
|
||||
|
||||
@@ -33,24 +33,15 @@ const getAppleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
|
||||
const getHeaderFromToken = token => {
|
||||
const decodedToken = jwt.decode(token, { complete: true });
|
||||
if (!decodedToken) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
`provided token does not decode as JWT`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `provided token does not decode as JWT`);
|
||||
}
|
||||
|
||||
return decodedToken.header;
|
||||
};
|
||||
|
||||
const verifyIdToken = async (
|
||||
{ token, id },
|
||||
{ clientId, cacheMaxEntries, cacheMaxAge }
|
||||
) => {
|
||||
const verifyIdToken = async ({ token, id }, { clientId, cacheMaxEntries, cacheMaxAge }) => {
|
||||
if (!token) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
`id token is invalid for this user.`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
|
||||
}
|
||||
|
||||
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
|
||||
@@ -60,11 +51,7 @@ const verifyIdToken = async (
|
||||
cacheMaxAge = cacheMaxAge || ONE_HOUR_IN_MS;
|
||||
cacheMaxEntries = cacheMaxEntries || 5;
|
||||
|
||||
const appleKey = await getAppleKeyByKeyId(
|
||||
keyId,
|
||||
cacheMaxEntries,
|
||||
cacheMaxAge
|
||||
);
|
||||
const appleKey = await getAppleKeyByKeyId(keyId, cacheMaxEntries, cacheMaxAge);
|
||||
const signingKey = appleKey.publicKey || appleKey.rsaPublicKey;
|
||||
|
||||
try {
|
||||
@@ -87,10 +74,7 @@ const verifyIdToken = async (
|
||||
}
|
||||
|
||||
if (jwtClaims.sub !== id) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
`auth data is invalid for this user.`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
|
||||
}
|
||||
return jwtClaims;
|
||||
};
|
||||
|
||||
@@ -19,20 +19,12 @@ function getAppSecretPath(authData, options = {}) {
|
||||
// Returns a promise that fulfills iff this user id is valid.
|
||||
function validateAuthData(authData, options) {
|
||||
return graphRequest(
|
||||
'me?fields=id&access_token=' +
|
||||
authData.access_token +
|
||||
getAppSecretPath(authData, options)
|
||||
'me?fields=id&access_token=' + authData.access_token + getAppSecretPath(authData, options)
|
||||
).then(data => {
|
||||
if (
|
||||
(data && data.id == authData.id) ||
|
||||
(process.env.TESTING && authData.id === 'test')
|
||||
) {
|
||||
if ((data && data.id == authData.id) || (process.env.TESTING && authData.id === 'test')) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Facebook auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,10 +35,7 @@ function validateAppId(appIds, authData, options) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (!appIds.length) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Facebook auth is not configured.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is not configured.');
|
||||
}
|
||||
return graphRequest(
|
||||
'app?access_token=' + access_token + getAppSecretPath(authData, options)
|
||||
@@ -54,10 +43,7 @@ function validateAppId(appIds, authData, options) {
|
||||
if (data && appIds.indexOf(data.id) != -1) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Facebook auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -96,20 +96,14 @@ function verifySignature(publicKey, authData) {
|
||||
verifier.update(authData.salt, 'base64');
|
||||
|
||||
if (!verifier.verify(publicKey, authData.signature, 'base64')) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Apple Game Center - invalid signature'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple Game Center - invalid signature');
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a promise that fulfills if this user id is valid.
|
||||
async function validateAuthData(authData) {
|
||||
if (!authData.id) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Apple Game Center - authData id missing'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple Game Center - authData id missing');
|
||||
}
|
||||
authData.playerId = authData.id;
|
||||
const publicKey = await getAppleCertificate(authData.publicKeyUrl);
|
||||
|
||||
@@ -8,10 +8,7 @@ function validateAuthData(authData) {
|
||||
if (data && data.id == authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Github auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Github auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -39,9 +39,7 @@ function getGoogleKeyByKeyId(keyId) {
|
||||
|
||||
if (expire) {
|
||||
cache = Object.assign({}, pems, {
|
||||
expiresAt: new Date(
|
||||
new Date().getTime() + Number(expire[1]) * 1000
|
||||
),
|
||||
expiresAt: new Date(new Date().getTime() + Number(expire[1]) * 1000),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -57,10 +55,7 @@ function getHeaderFromToken(token) {
|
||||
const decodedToken = jwt.decode(token, { complete: true });
|
||||
|
||||
if (!decodedToken) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
`provided token does not decode as JWT`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `provided token does not decode as JWT`);
|
||||
}
|
||||
|
||||
return decodedToken.header;
|
||||
@@ -68,10 +63,7 @@ function getHeaderFromToken(token) {
|
||||
|
||||
async function verifyIdToken({ id_token: token, id }, { clientId }) {
|
||||
if (!token) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
`id token is invalid for this user.`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
|
||||
}
|
||||
|
||||
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
|
||||
@@ -96,10 +88,7 @@ async function verifyIdToken({ id_token: token, id }, { clientId }) {
|
||||
}
|
||||
|
||||
if (jwtClaims.sub !== id) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
`auth data is invalid for this user.`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
|
||||
}
|
||||
|
||||
if (clientId && jwtClaims.aud !== clientId) {
|
||||
@@ -140,9 +129,7 @@ function rsaPublicKeyToPEM(modulusB64, exponentB64) {
|
||||
const encodedExplen = encodeLengthHex(explen);
|
||||
const encodedPubkey =
|
||||
'30' +
|
||||
encodeLengthHex(
|
||||
modlen + explen + encodedModlen.length / 2 + encodedExplen.length / 2 + 2
|
||||
) +
|
||||
encodeLengthHex(modlen + explen + encodedModlen.length / 2 + encodedExplen.length / 2 + 2) +
|
||||
'02' +
|
||||
encodedModlen +
|
||||
modulusHex +
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const https = require('https');
|
||||
|
||||
function makeCallback(resolve, reject, noJSON) {
|
||||
return function(res) {
|
||||
return function (res) {
|
||||
let data = '';
|
||||
res.on('data', chunk => {
|
||||
data += chunk;
|
||||
@@ -23,9 +23,7 @@ function makeCallback(resolve, reject, noJSON) {
|
||||
|
||||
function get(options, noJSON = false) {
|
||||
return new Promise((resolve, reject) => {
|
||||
https
|
||||
.get(options, makeCallback(resolve, reject, noJSON))
|
||||
.on('error', reject);
|
||||
https.get(options, makeCallback(resolve, reject, noJSON)).on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -92,11 +92,7 @@ function loadAuthAdapter(provider, authOptions) {
|
||||
|
||||
// Try the configuration methods
|
||||
if (providerOptions) {
|
||||
const optionalAdapter = loadAdapter(
|
||||
providerOptions,
|
||||
undefined,
|
||||
providerOptions
|
||||
);
|
||||
const optionalAdapter = loadAdapter(providerOptions, undefined, providerOptions);
|
||||
if (optionalAdapter) {
|
||||
['validateAuthData', 'validateAppId'].forEach(key => {
|
||||
if (optionalAdapter[key]) {
|
||||
@@ -128,10 +124,7 @@ module.exports = function (authOptions = {}, enableAnonymousUsers = true) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { adapter, appIds, providerOptions } = loadAuthAdapter(
|
||||
provider,
|
||||
authOptions
|
||||
);
|
||||
const { adapter, appIds, providerOptions } = loadAuthAdapter(provider, authOptions);
|
||||
|
||||
return authDataValidator(adapter, appIds, providerOptions);
|
||||
};
|
||||
|
||||
@@ -11,10 +11,7 @@ function validateAuthData(authData) {
|
||||
if (response && response.data && response.data.id == authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Instagram auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Instagram auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,19 +5,17 @@ const httpsRequest = require('./httpsRequest');
|
||||
|
||||
// Returns a promise that fulfills iff this user id is valid.
|
||||
function validateAuthData(authData, options) {
|
||||
return request(options.janrain_capture_host, authData.access_token).then(
|
||||
data => {
|
||||
//successful response will have a "stat" (status) of 'ok' and a result node that stores the uuid, because that's all we asked for
|
||||
//see: https://docs.janrain.com/api/registration/entity/#entity
|
||||
if (data && data.stat == 'ok' && data.result == authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Janrain capture auth is invalid for this user.'
|
||||
);
|
||||
return request(options.janrain_capture_host, authData.access_token).then(data => {
|
||||
//successful response will have a "stat" (status) of 'ok' and a result node that stores the uuid, because that's all we asked for
|
||||
//see: https://docs.janrain.com/api/registration/entity/#entity
|
||||
if (data && data.stat == 'ok' && data.result == authData.id) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Janrain capture auth is invalid for this user.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Returns a promise that fulfills iff this app id is valid.
|
||||
|
||||
@@ -37,12 +37,7 @@ const { Parse } = require('parse/node');
|
||||
const httpsRequest = require('./httpsRequest');
|
||||
|
||||
const arraysEqual = (_arr1, _arr2) => {
|
||||
if (
|
||||
!Array.isArray(_arr1) ||
|
||||
!Array.isArray(_arr2) ||
|
||||
_arr1.length !== _arr2.length
|
||||
)
|
||||
return false;
|
||||
if (!Array.isArray(_arr1) || !Array.isArray(_arr2) || _arr1.length !== _arr2.length) return false;
|
||||
|
||||
var arr1 = _arr1.concat().sort();
|
||||
var arr2 = _arr2.concat().sort();
|
||||
@@ -54,21 +49,12 @@ const arraysEqual = (_arr1, _arr2) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleAuth = async (
|
||||
{ access_token, id, roles, groups } = {},
|
||||
{ config } = {}
|
||||
) => {
|
||||
const handleAuth = async ({ access_token, id, roles, groups } = {}, { config } = {}) => {
|
||||
if (!(access_token && id)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Missing access token and/or User id'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Missing access token and/or User id');
|
||||
}
|
||||
if (!config || !(config['auth-server-url'] && config['realm'])) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Missing keycloak configuration'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Missing keycloak configuration');
|
||||
}
|
||||
try {
|
||||
const response = await httpsRequest.get({
|
||||
@@ -87,10 +73,7 @@ const handleAuth = async (
|
||||
) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Invalid authentication'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid authentication');
|
||||
} catch (e) {
|
||||
if (e instanceof Parse.Error) {
|
||||
throw e;
|
||||
|
||||
@@ -4,16 +4,12 @@ const Parse = require('parse/node').Parse;
|
||||
function validateAuthData(authData, options) {
|
||||
if (!optionsAreValid(options)) {
|
||||
return new Promise((_, reject) => {
|
||||
reject(
|
||||
new Parse.Error(
|
||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
||||
'LDAP auth configuration missing'
|
||||
)
|
||||
);
|
||||
reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP auth configuration missing'));
|
||||
});
|
||||
}
|
||||
const clientOptions = (options.url.startsWith("ldaps://")) ?
|
||||
{ url: options.url, tlsOptions: options.tlsOptions } : { url: options.url };
|
||||
const clientOptions = options.url.startsWith('ldaps://')
|
||||
? { url: options.url, tlsOptions: options.tlsOptions }
|
||||
: { url: options.url };
|
||||
|
||||
const client = ldapjs.createClient(clientOptions);
|
||||
const userCn =
|
||||
@@ -23,28 +19,31 @@ function validateAuthData(authData, options) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
client.bind(userCn, authData.password, ldapError => {
|
||||
delete(authData.password);
|
||||
delete authData.password;
|
||||
if (ldapError) {
|
||||
let error;
|
||||
switch (ldapError.code) {
|
||||
case 49:
|
||||
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAP: Wrong username or password');
|
||||
error = new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'LDAP: Wrong username or password'
|
||||
);
|
||||
break;
|
||||
case "DEPTH_ZERO_SELF_SIGNED_CERT":
|
||||
case 'DEPTH_ZERO_SELF_SIGNED_CERT':
|
||||
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAPS: Certificate mismatch');
|
||||
break;
|
||||
default:
|
||||
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAP: Somthing went wrong (' + ldapError.code + ')');
|
||||
error = new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'LDAP: Somthing went wrong (' + ldapError.code + ')'
|
||||
);
|
||||
}
|
||||
reject(error);
|
||||
client.destroy(ldapError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof options.groupCn === 'string' &&
|
||||
typeof options.groupFilter === 'string'
|
||||
) {
|
||||
if (typeof options.groupCn === 'string' && typeof options.groupFilter === 'string') {
|
||||
searchForGroup(client, options, authData.id, resolve, reject);
|
||||
} else {
|
||||
client.unbind();
|
||||
@@ -61,7 +60,7 @@ function optionsAreValid(options) {
|
||||
typeof options.suffix === 'string' &&
|
||||
typeof options.url === 'string' &&
|
||||
(options.url.startsWith('ldap://') ||
|
||||
options.url.startsWith('ldaps://') && typeof options.tlsOptions === 'object')
|
||||
(options.url.startsWith('ldaps://') && typeof options.tlsOptions === 'object'))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -76,12 +75,7 @@ function searchForGroup(client, options, id, resolve, reject) {
|
||||
if (searchError) {
|
||||
client.unbind();
|
||||
client.destroy();
|
||||
return reject(
|
||||
new Parse.Error(
|
||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
||||
'LDAP group search failed'
|
||||
)
|
||||
);
|
||||
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
|
||||
}
|
||||
res.on('searchEntry', entry => {
|
||||
if (entry.object.cn === options.groupCn) {
|
||||
@@ -96,20 +90,12 @@ function searchForGroup(client, options, id, resolve, reject) {
|
||||
client.unbind();
|
||||
client.destroy();
|
||||
return reject(
|
||||
new Parse.Error(
|
||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
||||
'LDAP: User not in group'
|
||||
)
|
||||
new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP: User not in group')
|
||||
);
|
||||
}
|
||||
});
|
||||
res.on('error', () => {
|
||||
return reject(
|
||||
new Parse.Error(
|
||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
||||
'LDAP group search failed'
|
||||
)
|
||||
);
|
||||
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,10 +8,7 @@ function validateAuthData(authData) {
|
||||
if (response && response.userId && response.userId === authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Line auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Line auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,17 +4,12 @@ const httpsRequest = require('./httpsRequest');
|
||||
|
||||
// Returns a promise that fulfills iff this user id is valid.
|
||||
function validateAuthData(authData) {
|
||||
return request('me', authData.access_token, authData.is_mobile_sdk).then(
|
||||
data => {
|
||||
if (data && data.id == authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Linkedin auth is invalid for this user.'
|
||||
);
|
||||
return request('me', authData.access_token, authData.is_mobile_sdk).then(data => {
|
||||
if (data && data.id == authData.id) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Linkedin auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
// Returns a promise that fulfills iff this app id is valid.
|
||||
|
||||
@@ -8,10 +8,7 @@ function validateAuthData(authData) {
|
||||
if (data && data.id == authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Meetup auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Meetup auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,17 +4,15 @@ const httpsRequest = require('./httpsRequest');
|
||||
|
||||
// Returns a promise that fulfills if this user mail is valid.
|
||||
function validateAuthData(authData) {
|
||||
return request('me', authData.access_token).then(
|
||||
response => {
|
||||
if (response && response.id && response.id == authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Microsoft Graph auth is invalid for this user.'
|
||||
);
|
||||
return request('me', authData.access_token).then(response => {
|
||||
if (response && response.id && response.id == authData.id) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Microsoft Graph auth is invalid for this user.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Returns a promise that fulfills if this app id is valid.
|
||||
|
||||
@@ -63,8 +63,7 @@ const INVALID_ACCESS_APPID =
|
||||
"OAuth2: the access_token's appID is empty or is not in the list of permitted appIDs in the auth configuration.";
|
||||
const MISSING_APPIDS =
|
||||
'OAuth2 configuration is missing the client app IDs ("appIds" config parameter).';
|
||||
const MISSING_URL =
|
||||
'OAuth2 token introspection endpoint URL is missing from configuration!';
|
||||
const MISSING_URL = 'OAuth2 token introspection endpoint URL is missing from configuration!';
|
||||
|
||||
// Returns a promise that fulfills if this user id is valid.
|
||||
function validateAuthData(authData, options) {
|
||||
|
||||
@@ -14,10 +14,7 @@ function validateAuthData(authData) {
|
||||
if (data && data.sub == authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'PhantAuth auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'PhantAuth auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,11 @@ var Parse = require('parse/node').Parse;
|
||||
|
||||
// Returns a promise that fulfills iff this user id is valid.
|
||||
function validateAuthData(authData) {
|
||||
return graphRequest('me?access_token=' + authData.access_token).then(function(
|
||||
data
|
||||
) {
|
||||
return graphRequest('me?access_token=' + authData.access_token).then(function (data) {
|
||||
if (data && data.openid == authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'qq auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'qq auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,21 +19,16 @@ function validateAppId() {
|
||||
|
||||
// A promisey wrapper for qq graph requests.
|
||||
function graphRequest(path) {
|
||||
return httpsRequest
|
||||
.get('https://graph.qq.com/oauth2.0/' + path, true)
|
||||
.then(data => {
|
||||
return parseResponseData(data);
|
||||
});
|
||||
return httpsRequest.get('https://graph.qq.com/oauth2.0/' + path, true).then(data => {
|
||||
return parseResponseData(data);
|
||||
});
|
||||
}
|
||||
|
||||
function parseResponseData(data) {
|
||||
const starPos = data.indexOf('(');
|
||||
const endPos = data.indexOf(')');
|
||||
if (starPos == -1 || endPos == -1) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'qq auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'qq auth is invalid for this user.');
|
||||
}
|
||||
data = data.substring(starPos + 1, endPos - 1);
|
||||
return JSON.parse(data);
|
||||
|
||||
@@ -8,10 +8,7 @@ function validateAuthData(authData) {
|
||||
if (data && data.id == authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Spotify auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Spotify auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,19 +16,13 @@ function validateAuthData(authData) {
|
||||
function validateAppId(appIds, authData) {
|
||||
var access_token = authData.access_token;
|
||||
if (!appIds.length) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Spotify auth is not configured.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Spotify auth is not configured.');
|
||||
}
|
||||
return request('me', access_token).then(data => {
|
||||
if (data && appIds.indexOf(data.id) != -1) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Spotify auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Spotify auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,7 @@ var Parse = require('parse/node').Parse;
|
||||
// Returns a promise that fulfills iff this user id is valid.
|
||||
function validateAuthData(authData, options) {
|
||||
if (!options) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
||||
'Twitter auth configuration missing'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Twitter auth configuration missing');
|
||||
}
|
||||
options = handleMultipleConfigurations(authData, options);
|
||||
var client = new OAuth(options);
|
||||
@@ -20,10 +17,7 @@ function validateAuthData(authData, options) {
|
||||
if (data && data.id_str == '' + authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Twitter auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Twitter auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,20 +30,14 @@ function handleMultipleConfigurations(authData, options) {
|
||||
if (Array.isArray(options)) {
|
||||
const consumer_key = authData.consumer_key;
|
||||
if (!consumer_key) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Twitter auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Twitter auth is invalid for this user.');
|
||||
}
|
||||
options = options.filter(option => {
|
||||
return option.consumer_key == consumer_key;
|
||||
});
|
||||
|
||||
if (options.length == 0) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Twitter auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Twitter auth is invalid for this user.');
|
||||
}
|
||||
options = options[0];
|
||||
}
|
||||
|
||||
@@ -11,10 +11,7 @@ function validateAuthData(authData, params) {
|
||||
if (response && response.access_token) {
|
||||
return request(
|
||||
'api.vk.com',
|
||||
'method/users.get?access_token=' +
|
||||
authData.access_token +
|
||||
'&v=' +
|
||||
params.apiVersion
|
||||
'method/users.get?access_token=' + authData.access_token + '&v=' + params.apiVersion
|
||||
).then(function (response) {
|
||||
if (
|
||||
response &&
|
||||
@@ -24,16 +21,10 @@ function validateAuthData(authData, params) {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Vk auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Vk auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Vk appIds or appSecret is incorrect.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Vk appIds or appSecret is incorrect.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,17 +4,14 @@ var Parse = require('parse/node').Parse;
|
||||
|
||||
// Returns a promise that fulfills iff this user id is valid.
|
||||
function validateAuthData(authData) {
|
||||
return graphRequest(
|
||||
'auth?access_token=' + authData.access_token + '&openid=' + authData.id
|
||||
).then(function(data) {
|
||||
if (data.errcode == 0) {
|
||||
return;
|
||||
return graphRequest('auth?access_token=' + authData.access_token + '&openid=' + authData.id).then(
|
||||
function (data) {
|
||||
if (data.errcode == 0) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'wechat auth is invalid for this user.');
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'wechat auth is invalid for this user.'
|
||||
);
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
// Returns a promise that fulfills if this app id is valid.
|
||||
|
||||
@@ -5,14 +5,11 @@ var querystring = require('querystring');
|
||||
|
||||
// Returns a promise that fulfills iff this user id is valid.
|
||||
function validateAuthData(authData) {
|
||||
return graphRequest(authData.access_token).then(function(data) {
|
||||
return graphRequest(authData.access_token).then(function (data) {
|
||||
if (data && data.uid == authData.id) {
|
||||
return;
|
||||
}
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'weibo auth is invalid for this user.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'weibo auth is invalid for this user.');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ function debug() {
|
||||
logger.debug.apply(logger, ['RedisCacheAdapter', ...arguments]);
|
||||
}
|
||||
|
||||
const isValidTTL = (ttl) => typeof ttl === 'number' && ttl > 0;
|
||||
const isValidTTL = ttl => typeof ttl === 'number' && ttl > 0;
|
||||
|
||||
export class RedisCacheAdapter {
|
||||
constructor(redisCtx, ttl = DEFAULT_REDIS_TTL) {
|
||||
@@ -22,8 +22,8 @@ export class RedisCacheAdapter {
|
||||
if (!this.client) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
this.client.quit((err) => {
|
||||
return new Promise(resolve => {
|
||||
this.client.quit(err => {
|
||||
if (err) {
|
||||
logger.error('RedisCacheAdapter error on shutdown', { error: err });
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export class RedisCacheAdapter {
|
||||
return this.queue.enqueue(
|
||||
key,
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
new Promise(resolve => {
|
||||
this.client.get(key, function (err, res) {
|
||||
debug('-> get', key, res);
|
||||
if (!res) {
|
||||
@@ -62,7 +62,7 @@ export class RedisCacheAdapter {
|
||||
return this.queue.enqueue(
|
||||
key,
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
new Promise(resolve => {
|
||||
this.client.set(key, value, function () {
|
||||
resolve();
|
||||
});
|
||||
@@ -77,7 +77,7 @@ export class RedisCacheAdapter {
|
||||
return this.queue.enqueue(
|
||||
key,
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
new Promise(resolve => {
|
||||
this.client.psetex(key, ttl, value, function () {
|
||||
resolve();
|
||||
});
|
||||
@@ -90,7 +90,7 @@ export class RedisCacheAdapter {
|
||||
return this.queue.enqueue(
|
||||
key,
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
new Promise(resolve => {
|
||||
this.client.del(key, function () {
|
||||
resolve();
|
||||
});
|
||||
@@ -103,7 +103,7 @@ export class RedisCacheAdapter {
|
||||
return this.queue.enqueue(
|
||||
FLUSH_DB_KEY,
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
new Promise(resolve => {
|
||||
this.client.flushdb(function () {
|
||||
resolve();
|
||||
});
|
||||
|
||||
@@ -38,12 +38,7 @@ export class FilesAdapter {
|
||||
*
|
||||
* @return {Promise} a promise that should fail if the storage didn't succeed
|
||||
*/
|
||||
createFile(
|
||||
filename: string,
|
||||
data,
|
||||
contentType: string,
|
||||
options: Object
|
||||
): Promise {}
|
||||
createFile(filename: string, data, contentType: string, options: Object): Promise {}
|
||||
|
||||
/** Responsible for deleting the specified file
|
||||
*
|
||||
@@ -111,10 +106,7 @@ export function validateFilename(filename): ?Parse.Error {
|
||||
|
||||
const regx = /^[_a-zA-Z0-9][a-zA-Z0-9@. ~_-]*$/;
|
||||
if (!filename.match(regx)) {
|
||||
return new Parse.Error(
|
||||
Parse.Error.INVALID_FILE_NAME,
|
||||
'Filename contains invalid characters.'
|
||||
);
|
||||
return new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename contains invalid characters.');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -28,11 +28,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
||||
this._algorithm = 'aes-256-gcm';
|
||||
this._encryptionKey =
|
||||
encryptionKey !== undefined
|
||||
? crypto
|
||||
.createHash('sha256')
|
||||
.update(String(encryptionKey))
|
||||
.digest('base64')
|
||||
.substr(0, 32)
|
||||
? crypto.createHash('sha256').update(String(encryptionKey)).digest('base64').substr(0, 32)
|
||||
: null;
|
||||
const defaultMongoOptions = {
|
||||
useNewUrlParser: true,
|
||||
@@ -43,13 +39,12 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
||||
|
||||
_connect() {
|
||||
if (!this._connectionPromise) {
|
||||
this._connectionPromise = MongoClient.connect(
|
||||
this._databaseURI,
|
||||
this._mongoOptions
|
||||
).then(client => {
|
||||
this._client = client;
|
||||
return client.db(client.s.options.dbName);
|
||||
});
|
||||
this._connectionPromise = MongoClient.connect(this._databaseURI, this._mongoOptions).then(
|
||||
client => {
|
||||
this._client = client;
|
||||
return client.db(client.s.options.dbName);
|
||||
}
|
||||
);
|
||||
}
|
||||
return this._connectionPromise;
|
||||
}
|
||||
@@ -68,11 +63,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
||||
if (this._encryptionKey !== null) {
|
||||
try {
|
||||
const iv = crypto.randomBytes(16);
|
||||
const cipher = crypto.createCipheriv(
|
||||
this._algorithm,
|
||||
this._encryptionKey,
|
||||
iv
|
||||
);
|
||||
const cipher = crypto.createCipheriv(this._algorithm, this._encryptionKey, iv);
|
||||
const encryptedResult = Buffer.concat([
|
||||
cipher.update(data),
|
||||
cipher.final(),
|
||||
@@ -126,16 +117,9 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
||||
const authTag = data.slice(authTagLocation);
|
||||
const iv = data.slice(ivLocation, authTagLocation);
|
||||
const encrypted = data.slice(0, ivLocation);
|
||||
const decipher = crypto.createDecipheriv(
|
||||
this._algorithm,
|
||||
this._encryptionKey,
|
||||
iv
|
||||
);
|
||||
const decipher = crypto.createDecipheriv(this._algorithm, this._encryptionKey, iv);
|
||||
decipher.setAuthTag(authTag);
|
||||
const decrypted = Buffer.concat([
|
||||
decipher.update(encrypted),
|
||||
decipher.final(),
|
||||
]);
|
||||
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
||||
return resolve(decrypted);
|
||||
} catch (err) {
|
||||
return reject(err);
|
||||
@@ -160,10 +144,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
||||
options.oldKey
|
||||
);
|
||||
} else {
|
||||
oldKeyFileAdapter = new GridFSBucketAdapter(
|
||||
this._databaseURI,
|
||||
this._mongoOptions
|
||||
);
|
||||
oldKeyFileAdapter = new GridFSBucketAdapter(this._databaseURI, this._mongoOptions);
|
||||
}
|
||||
if (options.fileNames !== undefined) {
|
||||
fileNames = options.fileNames;
|
||||
@@ -186,9 +167,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
||||
this.createFile(fileName, plainTextData)
|
||||
.then(() => {
|
||||
fileNamesRotated.push(fileName);
|
||||
fileNamesNotRotated = fileNamesNotRotated.filter(function (
|
||||
value
|
||||
) {
|
||||
fileNamesNotRotated = fileNamesNotRotated.filter(function (value) {
|
||||
return value !== fileName;
|
||||
});
|
||||
fileNameIndex += 1;
|
||||
@@ -223,13 +202,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
||||
}
|
||||
|
||||
getFileLocation(config, filename) {
|
||||
return (
|
||||
config.mount +
|
||||
'/files/' +
|
||||
config.applicationId +
|
||||
'/' +
|
||||
encodeURIComponent(filename)
|
||||
);
|
||||
return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
|
||||
}
|
||||
|
||||
async getMetadata(filename) {
|
||||
|
||||
@@ -30,13 +30,12 @@ export class GridStoreAdapter extends FilesAdapter {
|
||||
|
||||
_connect() {
|
||||
if (!this._connectionPromise) {
|
||||
this._connectionPromise = MongoClient.connect(
|
||||
this._databaseURI,
|
||||
this._mongoOptions
|
||||
).then(client => {
|
||||
this._client = client;
|
||||
return client.db(client.s.options.dbName);
|
||||
});
|
||||
this._connectionPromise = MongoClient.connect(this._databaseURI, this._mongoOptions).then(
|
||||
client => {
|
||||
this._client = client;
|
||||
return client.db(client.s.options.dbName);
|
||||
}
|
||||
);
|
||||
}
|
||||
return this._connectionPromise;
|
||||
}
|
||||
@@ -85,13 +84,7 @@ export class GridStoreAdapter extends FilesAdapter {
|
||||
}
|
||||
|
||||
getFileLocation(config, filename) {
|
||||
return (
|
||||
config.mount +
|
||||
'/files/' +
|
||||
config.applicationId +
|
||||
'/' +
|
||||
encodeURIComponent(filename)
|
||||
);
|
||||
return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
|
||||
}
|
||||
|
||||
async handleFileStream(filename: string, req, res, contentType) {
|
||||
@@ -152,14 +145,14 @@ function handleRangeRequest(stream, req, res, contentType) {
|
||||
'Content-Type': contentType,
|
||||
});
|
||||
|
||||
stream.seek(start, function() {
|
||||
stream.seek(start, function () {
|
||||
// Get gridFile stream
|
||||
const gridFileStream = stream.stream(true);
|
||||
let bufferAvail = 0;
|
||||
let remainingBytesToWrite = contentLength;
|
||||
let totalBytesWritten = 0;
|
||||
// Write to response
|
||||
gridFileStream.on('data', function(data) {
|
||||
gridFileStream.on('data', function (data) {
|
||||
bufferAvail += data.length;
|
||||
if (bufferAvail > 0) {
|
||||
// slice returns the same buffer if overflowing
|
||||
|
||||
@@ -20,11 +20,7 @@ function configureTransports(options) {
|
||||
{
|
||||
filename: 'parse-server.info',
|
||||
json: true,
|
||||
format: format.combine(
|
||||
format.timestamp(),
|
||||
format.splat(),
|
||||
format.json()
|
||||
),
|
||||
format: format.combine(format.timestamp(), format.splat(), format.json()),
|
||||
},
|
||||
options
|
||||
)
|
||||
@@ -37,11 +33,7 @@ function configureTransports(options) {
|
||||
{
|
||||
filename: 'parse-server.err',
|
||||
json: true,
|
||||
format: format.combine(
|
||||
format.timestamp(),
|
||||
format.splat(),
|
||||
format.json()
|
||||
),
|
||||
format: format.combine(format.timestamp(), format.splat(), format.json()),
|
||||
},
|
||||
options,
|
||||
{ level: 'error' }
|
||||
@@ -120,9 +112,7 @@ export function addTransport(transport) {
|
||||
|
||||
export function removeTransport(transport) {
|
||||
const matchingTransport = logger.transports.find(t1 => {
|
||||
return typeof transport === 'string'
|
||||
? t1.name === transport
|
||||
: t1 === transport;
|
||||
return typeof transport === 'string' ? t1.name === transport : t1 === transport;
|
||||
});
|
||||
|
||||
if (matchingTransport) {
|
||||
|
||||
@@ -28,8 +28,7 @@ export class WinstonLoggerAdapter extends LoggerAdapter {
|
||||
options = {};
|
||||
}
|
||||
// defaults to 7 days prior
|
||||
const from =
|
||||
options.from || new Date(Date.now() - 7 * MILLISECONDS_IN_A_DAY);
|
||||
const from = options.from || new Date(Date.now() - 7 * MILLISECONDS_IN_A_DAY);
|
||||
const until = options.until || new Date();
|
||||
const limit = options.size || 10;
|
||||
const order = options.order || 'desc';
|
||||
|
||||
@@ -15,17 +15,7 @@ export default class MongoCollection {
|
||||
// idea. Or even if this behavior is a good idea.
|
||||
find(
|
||||
query,
|
||||
{
|
||||
skip,
|
||||
limit,
|
||||
sort,
|
||||
keys,
|
||||
maxTimeMS,
|
||||
readPreference,
|
||||
hint,
|
||||
caseInsensitive,
|
||||
explain,
|
||||
} = {}
|
||||
{ skip, limit, sort, keys, maxTimeMS, readPreference, hint, caseInsensitive, explain } = {}
|
||||
) {
|
||||
// Support for Full Text Search - $text
|
||||
if (keys && keys.$score) {
|
||||
@@ -44,10 +34,7 @@ export default class MongoCollection {
|
||||
explain,
|
||||
}).catch(error => {
|
||||
// Check for "no geoindex" error
|
||||
if (
|
||||
error.code != 17007 &&
|
||||
!error.message.match(/unable to find index for .geoNear/)
|
||||
) {
|
||||
if (error.code != 17007 && !error.message.match(/unable to find index for .geoNear/)) {
|
||||
throw error;
|
||||
}
|
||||
// Figure out what key needs an index
|
||||
@@ -88,17 +75,7 @@ export default class MongoCollection {
|
||||
|
||||
_rawFind(
|
||||
query,
|
||||
{
|
||||
skip,
|
||||
limit,
|
||||
sort,
|
||||
keys,
|
||||
maxTimeMS,
|
||||
readPreference,
|
||||
hint,
|
||||
caseInsensitive,
|
||||
explain,
|
||||
} = {}
|
||||
{ skip, limit, sort, keys, maxTimeMS, readPreference, hint, caseInsensitive, explain } = {}
|
||||
) {
|
||||
let findOperation = this._mongoCollection.find(query, {
|
||||
skip,
|
||||
@@ -113,9 +90,7 @@ export default class MongoCollection {
|
||||
}
|
||||
|
||||
if (caseInsensitive) {
|
||||
findOperation = findOperation.collation(
|
||||
MongoCollection.caseInsensitiveCollation()
|
||||
);
|
||||
findOperation = findOperation.collation(MongoCollection.caseInsensitiveCollation());
|
||||
}
|
||||
|
||||
if (maxTimeMS) {
|
||||
|
||||
@@ -41,9 +41,7 @@ function mongoFieldToParseSchemaField(type) {
|
||||
|
||||
const nonFieldSchemaKeys = ['_id', '_metadata', '_client_permissions'];
|
||||
function mongoSchemaFieldsToParseSchemaFields(schema) {
|
||||
var fieldNames = Object.keys(schema).filter(
|
||||
(key) => nonFieldSchemaKeys.indexOf(key) === -1
|
||||
);
|
||||
var fieldNames = Object.keys(schema).filter(key => nonFieldSchemaKeys.indexOf(key) === -1);
|
||||
var response = fieldNames.reduce((obj, fieldName) => {
|
||||
obj[fieldName] = mongoFieldToParseSchemaField(schema[fieldName]);
|
||||
if (
|
||||
@@ -110,7 +108,7 @@ function mongoSchemaToParseSchema(mongoSchema) {
|
||||
function _mongoSchemaQueryFromNameQuery(name: string, query) {
|
||||
const object = { _id: name };
|
||||
if (query) {
|
||||
Object.keys(query).forEach((key) => {
|
||||
Object.keys(query).forEach(key => {
|
||||
object[key] = query[key];
|
||||
});
|
||||
}
|
||||
@@ -156,15 +154,13 @@ class MongoSchemaCollection {
|
||||
}
|
||||
|
||||
_fetchAllSchemasFrom_SCHEMA() {
|
||||
return this._collection
|
||||
._rawFind({})
|
||||
.then((schemas) => schemas.map(mongoSchemaToParseSchema));
|
||||
return this._collection._rawFind({}).then(schemas => schemas.map(mongoSchemaToParseSchema));
|
||||
}
|
||||
|
||||
_fetchOneSchemaFrom_SCHEMA(name: string) {
|
||||
return this._collection
|
||||
._rawFind(_mongoSchemaQueryFromNameQuery(name), { limit: 1 })
|
||||
.then((results) => {
|
||||
.then(results => {
|
||||
if (results.length === 1) {
|
||||
return mongoSchemaToParseSchema(results[0]);
|
||||
} else {
|
||||
@@ -175,22 +171,17 @@ class MongoSchemaCollection {
|
||||
|
||||
// Atomically find and delete an object based on query.
|
||||
findAndDeleteSchema(name: string) {
|
||||
return this._collection._mongoCollection.findOneAndDelete(
|
||||
_mongoSchemaQueryFromNameQuery(name)
|
||||
);
|
||||
return this._collection._mongoCollection.findOneAndDelete(_mongoSchemaQueryFromNameQuery(name));
|
||||
}
|
||||
|
||||
insertSchema(schema: any) {
|
||||
return this._collection
|
||||
.insertOne(schema)
|
||||
.then((result) => mongoSchemaToParseSchema(result.ops[0]))
|
||||
.catch((error) => {
|
||||
.then(result => mongoSchemaToParseSchema(result.ops[0]))
|
||||
.catch(error => {
|
||||
if (error.code === 11000) {
|
||||
//Mongo's duplicate key error
|
||||
throw new Parse.Error(
|
||||
Parse.Error.DUPLICATE_VALUE,
|
||||
'Class already exists.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Class already exists.');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
@@ -198,17 +189,11 @@ class MongoSchemaCollection {
|
||||
}
|
||||
|
||||
updateSchema(name: string, update) {
|
||||
return this._collection.updateOne(
|
||||
_mongoSchemaQueryFromNameQuery(name),
|
||||
update
|
||||
);
|
||||
return this._collection.updateOne(_mongoSchemaQueryFromNameQuery(name), update);
|
||||
}
|
||||
|
||||
upsertSchema(name: string, query: string, update) {
|
||||
return this._collection.upsertOne(
|
||||
_mongoSchemaQueryFromNameQuery(name, query),
|
||||
update
|
||||
);
|
||||
return this._collection.upsertOne(_mongoSchemaQueryFromNameQuery(name, query), update);
|
||||
}
|
||||
|
||||
// Add a field to the schema. If database does not support the field
|
||||
@@ -225,7 +210,7 @@ class MongoSchemaCollection {
|
||||
addFieldIfNotExists(className: string, fieldName: string, fieldType: string) {
|
||||
return this._fetchOneSchemaFrom_SCHEMA(className)
|
||||
.then(
|
||||
(schema) => {
|
||||
schema => {
|
||||
// If a field with this name already exists, it will be handled elsewhere.
|
||||
if (schema.fields[fieldName] != undefined) {
|
||||
return;
|
||||
@@ -235,8 +220,7 @@ class MongoSchemaCollection {
|
||||
// Make sure there are not other geopoint fields
|
||||
if (
|
||||
Object.keys(schema.fields).some(
|
||||
(existingField) =>
|
||||
schema.fields[existingField].type === 'GeoPoint'
|
||||
existingField => schema.fields[existingField].type === 'GeoPoint'
|
||||
)
|
||||
) {
|
||||
throw new Parse.Error(
|
||||
@@ -247,7 +231,7 @@ class MongoSchemaCollection {
|
||||
}
|
||||
return;
|
||||
},
|
||||
(error) => {
|
||||
error => {
|
||||
// If error is undefined, the schema doesn't exist, and we can create the schema with the field.
|
||||
// If some other error, reject with it.
|
||||
if (error === undefined) {
|
||||
|
||||
@@ -2,16 +2,8 @@
|
||||
import MongoCollection from './MongoCollection';
|
||||
import MongoSchemaCollection from './MongoSchemaCollection';
|
||||
import { StorageAdapter } from '../StorageAdapter';
|
||||
import type {
|
||||
SchemaType,
|
||||
QueryType,
|
||||
StorageClass,
|
||||
QueryOptions,
|
||||
} from '../StorageAdapter';
|
||||
import {
|
||||
parse as parseUrl,
|
||||
format as formatUrl,
|
||||
} from '../../../vendor/mongodbUrl';
|
||||
import type { SchemaType, QueryType, StorageClass, QueryOptions } from '../StorageAdapter';
|
||||
import { parse as parseUrl, format as formatUrl } from '../../../vendor/mongodbUrl';
|
||||
import {
|
||||
parseObjectToMongoObjectForCreate,
|
||||
mongoObjectToParseObject,
|
||||
@@ -45,9 +37,7 @@ const storageAdapterAllCollections = mongoAdapter => {
|
||||
}
|
||||
// TODO: If you have one app with a collection prefix that happens to be a prefix of another
|
||||
// apps prefix, this will go very very badly. We should fix that somehow.
|
||||
return (
|
||||
collection.collectionName.indexOf(mongoAdapter._collectionPrefix) == 0
|
||||
);
|
||||
return collection.collectionName.indexOf(mongoAdapter._collectionPrefix) == 0;
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -85,16 +75,13 @@ const mongoSchemaFromFieldsAndClassNameAndCLP = (
|
||||
|
||||
for (const fieldName in fields) {
|
||||
const { type, targetClass, ...fieldOptions } = fields[fieldName];
|
||||
mongoObject[
|
||||
fieldName
|
||||
] = MongoSchemaCollection.parseFieldTypeToMongoFieldType({
|
||||
mongoObject[fieldName] = MongoSchemaCollection.parseFieldTypeToMongoFieldType({
|
||||
type,
|
||||
targetClass,
|
||||
});
|
||||
if (fieldOptions && Object.keys(fieldOptions).length > 0) {
|
||||
mongoObject._metadata = mongoObject._metadata || {};
|
||||
mongoObject._metadata.fields_options =
|
||||
mongoObject._metadata.fields_options || {};
|
||||
mongoObject._metadata.fields_options = mongoObject._metadata.fields_options || {};
|
||||
mongoObject._metadata.fields_options[fieldName] = fieldOptions;
|
||||
}
|
||||
}
|
||||
@@ -108,11 +95,7 @@ const mongoSchemaFromFieldsAndClassNameAndCLP = (
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
indexes &&
|
||||
typeof indexes === 'object' &&
|
||||
Object.keys(indexes).length > 0
|
||||
) {
|
||||
if (indexes && typeof indexes === 'object' && Object.keys(indexes).length > 0) {
|
||||
mongoObject._metadata = mongoObject._metadata || {};
|
||||
mongoObject._metadata.indexes = indexes;
|
||||
}
|
||||
@@ -137,11 +120,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
_maxTimeMS: ?number;
|
||||
canSortOnJoinTables: boolean;
|
||||
|
||||
constructor({
|
||||
uri = defaults.DefaultMongoURI,
|
||||
collectionPrefix = '',
|
||||
mongoOptions = {},
|
||||
}: any) {
|
||||
constructor({ uri = defaults.DefaultMongoURI, collectionPrefix = '', mongoOptions = {} }: any) {
|
||||
this._uri = uri;
|
||||
this._collectionPrefix = collectionPrefix;
|
||||
this._mongoOptions = mongoOptions;
|
||||
@@ -225,9 +204,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
classExists(name: string) {
|
||||
return this.connect()
|
||||
.then(() => {
|
||||
return this.database
|
||||
.listCollections({ name: this._collectionPrefix + name })
|
||||
.toArray();
|
||||
return this.database.listCollections({ name: this._collectionPrefix + name }).toArray();
|
||||
})
|
||||
.then(collections => {
|
||||
return collections.length > 0;
|
||||
@@ -262,10 +239,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
Object.keys(submittedIndexes).forEach(name => {
|
||||
const field = submittedIndexes[name];
|
||||
if (existingIndexes[name] && field.__op !== 'Delete') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_QUERY,
|
||||
`Index ${name} exists, cannot update.`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, `Index ${name} exists, cannot update.`);
|
||||
}
|
||||
if (!existingIndexes[name] && field.__op === 'Delete') {
|
||||
throw new Parse.Error(
|
||||
@@ -349,26 +323,15 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
schema.indexes
|
||||
);
|
||||
mongoObject._id = className;
|
||||
return this.setIndexesWithSchemaFormat(
|
||||
className,
|
||||
schema.indexes,
|
||||
{},
|
||||
schema.fields
|
||||
)
|
||||
return this.setIndexesWithSchemaFormat(className, schema.indexes, {}, schema.fields)
|
||||
.then(() => this._schemaCollection())
|
||||
.then(schemaCollection => schemaCollection.insertSchema(mongoObject))
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
addFieldIfNotExists(
|
||||
className: string,
|
||||
fieldName: string,
|
||||
type: any
|
||||
): Promise<void> {
|
||||
addFieldIfNotExists(className: string, fieldName: string, type: any): Promise<void> {
|
||||
return this._schemaCollection()
|
||||
.then(schemaCollection =>
|
||||
schemaCollection.addFieldIfNotExists(className, fieldName, type)
|
||||
)
|
||||
.then(schemaCollection => schemaCollection.addFieldIfNotExists(className, fieldName, type))
|
||||
.then(() => this.createIndexesIfNeeded(className, fieldName, type))
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
@@ -388,9 +351,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
})
|
||||
// We've dropped the collection, now remove the _SCHEMA document
|
||||
.then(() => this._schemaCollection())
|
||||
.then(schemaCollection =>
|
||||
schemaCollection.findAndDeleteSchema(className)
|
||||
)
|
||||
.then(schemaCollection => schemaCollection.findAndDeleteSchema(className))
|
||||
.catch(err => this.handleError(err))
|
||||
);
|
||||
}
|
||||
@@ -398,9 +359,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
deleteAllClasses(fast: boolean) {
|
||||
return storageAdapterAllCollections(this).then(collections =>
|
||||
Promise.all(
|
||||
collections.map(collection =>
|
||||
fast ? collection.deleteMany({}) : collection.drop()
|
||||
)
|
||||
collections.map(collection => (fast ? collection.deleteMany({}) : collection.drop()))
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -450,13 +409,9 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
collection.updateMany(collectionFilter, collectionUpdate)
|
||||
)
|
||||
.then(collection => collection.updateMany(collectionFilter, collectionUpdate))
|
||||
.then(() => this._schemaCollection())
|
||||
.then(schemaCollection =>
|
||||
schemaCollection.updateSchema(className, schemaUpdate)
|
||||
)
|
||||
.then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate))
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
@@ -465,9 +420,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
// rejection reason are TBD.
|
||||
getAllClasses(): Promise<StorageClass[]> {
|
||||
return this._schemaCollection()
|
||||
.then(schemasCollection =>
|
||||
schemasCollection._fetchAllSchemasFrom_SCHEMA()
|
||||
)
|
||||
.then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA())
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
@@ -476,31 +429,18 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
// undefined as the reason.
|
||||
getClass(className: string): Promise<StorageClass> {
|
||||
return this._schemaCollection()
|
||||
.then(schemasCollection =>
|
||||
schemasCollection._fetchOneSchemaFrom_SCHEMA(className)
|
||||
)
|
||||
.then(schemasCollection => schemasCollection._fetchOneSchemaFrom_SCHEMA(className))
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
// TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema,
|
||||
// and should infer from the type. Or maybe does need the schema for validations. Or maybe needs
|
||||
// the schema only for the legacy mongo format. We'll figure that out later.
|
||||
createObject(
|
||||
className: string,
|
||||
schema: SchemaType,
|
||||
object: any,
|
||||
transactionalSession: ?any
|
||||
) {
|
||||
createObject(className: string, schema: SchemaType, object: any, transactionalSession: ?any) {
|
||||
schema = convertParseSchemaToMongoSchema(schema);
|
||||
const mongoObject = parseObjectToMongoObjectForCreate(
|
||||
className,
|
||||
object,
|
||||
schema
|
||||
);
|
||||
const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
collection.insertOne(mongoObject, transactionalSession)
|
||||
)
|
||||
.then(collection => collection.insertOne(mongoObject, transactionalSession))
|
||||
.catch(error => {
|
||||
if (error.code === 11000) {
|
||||
// Duplicate value
|
||||
@@ -510,9 +450,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
);
|
||||
err.underlyingError = error;
|
||||
if (error.message) {
|
||||
const matches = error.message.match(
|
||||
/index:[\sa-zA-Z0-9_\-\.]+\$?([a-zA-Z_-]+)_1/
|
||||
);
|
||||
const matches = error.message.match(/index:[\sa-zA-Z0-9_\-\.]+\$?([a-zA-Z_-]+)_1/);
|
||||
if (matches && Array.isArray(matches)) {
|
||||
err.userInfo = { duplicated_field: matches[1] };
|
||||
}
|
||||
@@ -543,18 +481,12 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
.then(
|
||||
({ result }) => {
|
||||
if (result.n === 0) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Object not found.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
() => {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
||||
'Database adapter error'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error');
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -571,9 +503,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
const mongoUpdate = transformUpdate(className, update, schema);
|
||||
const mongoWhere = transformWhere(className, query, schema);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
collection.updateMany(mongoWhere, mongoUpdate, transactionalSession)
|
||||
)
|
||||
.then(collection => collection.updateMany(mongoWhere, mongoUpdate, transactionalSession))
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
@@ -621,9 +551,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
const mongoUpdate = transformUpdate(className, update, schema);
|
||||
const mongoWhere = transformWhere(className, query, schema);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
collection.upsertOne(mongoWhere, mongoUpdate, transactionalSession)
|
||||
)
|
||||
.then(collection => collection.upsertOne(mongoWhere, mongoUpdate, transactionalSession))
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
@@ -632,16 +560,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
className: string,
|
||||
schema: SchemaType,
|
||||
query: QueryType,
|
||||
{
|
||||
skip,
|
||||
limit,
|
||||
sort,
|
||||
keys,
|
||||
readPreference,
|
||||
hint,
|
||||
caseInsensitive,
|
||||
explain,
|
||||
}: QueryOptions
|
||||
{ skip, limit, sort, keys, readPreference, hint, caseInsensitive, explain }: QueryOptions
|
||||
): Promise<any> {
|
||||
schema = convertParseSchemaToMongoSchema(schema);
|
||||
const mongoWhere = transformWhere(className, query, schema);
|
||||
@@ -689,9 +608,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
if (explain) {
|
||||
return objects;
|
||||
}
|
||||
return objects.map(object =>
|
||||
mongoObjectToParseObject(className, object, schema)
|
||||
);
|
||||
return objects.map(object => mongoObjectToParseObject(className, object, schema));
|
||||
})
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
@@ -706,18 +623,14 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
): Promise<any> {
|
||||
schema = convertParseSchemaToMongoSchema(schema);
|
||||
const indexCreationRequest = {};
|
||||
const mongoFieldNames = fieldNames.map(fieldName =>
|
||||
transformKey(className, fieldName, schema)
|
||||
);
|
||||
const mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema));
|
||||
mongoFieldNames.forEach(fieldName => {
|
||||
indexCreationRequest[fieldName] =
|
||||
options.indexType !== undefined ? options.indexType : 1;
|
||||
indexCreationRequest[fieldName] = options.indexType !== undefined ? options.indexType : 1;
|
||||
});
|
||||
|
||||
const defaultOptions: Object = { background: true, sparse: true };
|
||||
const indexNameOptions: Object = indexName ? { name: indexName } : {};
|
||||
const ttlOptions: Object =
|
||||
options.ttl !== undefined ? { expireAfterSeconds: options.ttl } : {};
|
||||
const ttlOptions: Object = options.ttl !== undefined ? { expireAfterSeconds: options.ttl } : {};
|
||||
const caseInsensitiveOptions: Object = caseInsensitive
|
||||
? { collation: MongoCollection.caseInsensitiveCollation() }
|
||||
: {};
|
||||
@@ -732,10 +645,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
.then(
|
||||
collection =>
|
||||
new Promise((resolve, reject) =>
|
||||
collection._mongoCollection.createIndex(
|
||||
indexCreationRequest,
|
||||
indexOptions,
|
||||
error => (error ? reject(error) : resolve())
|
||||
collection._mongoCollection.createIndex(indexCreationRequest, indexOptions, error =>
|
||||
error ? reject(error) : resolve()
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -747,23 +658,15 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
// As such, we shouldn't expose this function to users of parse until we have an out-of-band
|
||||
// Way of determining if a field is nullable. Undefined doesn't count against uniqueness,
|
||||
// which is why we use sparse indexes.
|
||||
ensureUniqueness(
|
||||
className: string,
|
||||
schema: SchemaType,
|
||||
fieldNames: string[]
|
||||
) {
|
||||
ensureUniqueness(className: string, schema: SchemaType, fieldNames: string[]) {
|
||||
schema = convertParseSchemaToMongoSchema(schema);
|
||||
const indexCreationRequest = {};
|
||||
const mongoFieldNames = fieldNames.map(fieldName =>
|
||||
transformKey(className, fieldName, schema)
|
||||
);
|
||||
const mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema));
|
||||
mongoFieldNames.forEach(fieldName => {
|
||||
indexCreationRequest[fieldName] = 1;
|
||||
});
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
collection._ensureSparseUniqueIndexInBackground(indexCreationRequest)
|
||||
)
|
||||
.then(collection => collection._ensureSparseUniqueIndexInBackground(indexCreationRequest))
|
||||
.catch(error => {
|
||||
if (error.code === 11000) {
|
||||
throw new Parse.Error(
|
||||
@@ -808,23 +711,14 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
distinct(
|
||||
className: string,
|
||||
schema: SchemaType,
|
||||
query: QueryType,
|
||||
fieldName: string
|
||||
) {
|
||||
distinct(className: string, schema: SchemaType, query: QueryType, fieldName: string) {
|
||||
schema = convertParseSchemaToMongoSchema(schema);
|
||||
const isPointerField =
|
||||
schema.fields[fieldName] && schema.fields[fieldName].type === 'Pointer';
|
||||
const isPointerField = schema.fields[fieldName] && schema.fields[fieldName].type === 'Pointer';
|
||||
const transformField = transformKey(className, fieldName, schema);
|
||||
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
collection.distinct(
|
||||
transformField,
|
||||
transformWhere(className, query, schema)
|
||||
)
|
||||
collection.distinct(transformField, transformWhere(className, query, schema))
|
||||
)
|
||||
.then(objects => {
|
||||
objects = objects.filter(obj => obj != null);
|
||||
@@ -862,16 +756,10 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
stage.$match = this._parseAggregateArgs(schema, stage.$match);
|
||||
}
|
||||
if (stage.$project) {
|
||||
stage.$project = this._parseAggregateProjectArgs(
|
||||
schema,
|
||||
stage.$project
|
||||
);
|
||||
stage.$project = this._parseAggregateProjectArgs(schema, stage.$project);
|
||||
}
|
||||
if (stage.$geoNear && stage.$geoNear.query) {
|
||||
stage.$geoNear.query = this._parseAggregateArgs(
|
||||
schema,
|
||||
stage.$geoNear.query
|
||||
);
|
||||
stage.$geoNear.query = this._parseAggregateArgs(schema, stage.$geoNear.query);
|
||||
}
|
||||
return stage;
|
||||
});
|
||||
@@ -894,8 +782,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
if (
|
||||
result._id == null ||
|
||||
result._id == undefined ||
|
||||
(['object', 'string'].includes(typeof result._id) &&
|
||||
_.isEmpty(result._id))
|
||||
(['object', 'string'].includes(typeof result._id) && _.isEmpty(result._id))
|
||||
) {
|
||||
result._id = null;
|
||||
}
|
||||
@@ -905,11 +792,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
return results;
|
||||
})
|
||||
.then(objects =>
|
||||
objects.map(object =>
|
||||
mongoObjectToParseObject(className, object, schema)
|
||||
)
|
||||
)
|
||||
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)))
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
@@ -945,20 +828,12 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
// Pass objects down to MongoDB...this is more than likely an $exists operator.
|
||||
returnValue[`_p_${field}`] = pipeline[field];
|
||||
} else {
|
||||
returnValue[
|
||||
`_p_${field}`
|
||||
] = `${schema.fields[field].targetClass}$${pipeline[field]}`;
|
||||
returnValue[`_p_${field}`] = `${schema.fields[field].targetClass}$${pipeline[field]}`;
|
||||
}
|
||||
} else if (
|
||||
schema.fields[field] &&
|
||||
schema.fields[field].type === 'Date'
|
||||
) {
|
||||
} else if (schema.fields[field] && schema.fields[field].type === 'Date') {
|
||||
returnValue[field] = this._convertToDate(pipeline[field]);
|
||||
} else {
|
||||
returnValue[field] = this._parseAggregateArgs(
|
||||
schema,
|
||||
pipeline[field]
|
||||
);
|
||||
returnValue[field] = this._parseAggregateArgs(schema, pipeline[field]);
|
||||
}
|
||||
|
||||
if (field === 'objectId') {
|
||||
@@ -1011,16 +886,11 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
// updatedAt or objectId and change it accordingly.
|
||||
_parseAggregateGroupArgs(schema: any, pipeline: any): any {
|
||||
if (Array.isArray(pipeline)) {
|
||||
return pipeline.map(value =>
|
||||
this._parseAggregateGroupArgs(schema, value)
|
||||
);
|
||||
return pipeline.map(value => this._parseAggregateGroupArgs(schema, value));
|
||||
} else if (typeof pipeline === 'object') {
|
||||
const returnValue = {};
|
||||
for (const field in pipeline) {
|
||||
returnValue[field] = this._parseAggregateGroupArgs(
|
||||
schema,
|
||||
pipeline[field]
|
||||
);
|
||||
returnValue[field] = this._parseAggregateGroupArgs(schema, pipeline[field]);
|
||||
}
|
||||
return returnValue;
|
||||
} else if (typeof pipeline === 'string') {
|
||||
@@ -1077,10 +947,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
case '':
|
||||
break;
|
||||
default:
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_QUERY,
|
||||
'Not supported read preference.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Not supported read preference.');
|
||||
}
|
||||
return readPreference;
|
||||
}
|
||||
@@ -1111,11 +978,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
createTextIndexesIfNeeded(
|
||||
className: string,
|
||||
query: QueryType,
|
||||
schema: any
|
||||
): Promise<void> {
|
||||
createTextIndexesIfNeeded(className: string, query: QueryType, schema: any): Promise<void> {
|
||||
for (const fieldName in query) {
|
||||
if (!query[fieldName] || !query[fieldName].$text) {
|
||||
continue;
|
||||
|
||||
@@ -20,27 +20,16 @@ const transformKey = (className, fieldName, schema) => {
|
||||
return 'times_used';
|
||||
}
|
||||
|
||||
if (
|
||||
schema.fields[fieldName] &&
|
||||
schema.fields[fieldName].__type == 'Pointer'
|
||||
) {
|
||||
if (schema.fields[fieldName] && schema.fields[fieldName].__type == 'Pointer') {
|
||||
fieldName = '_p_' + fieldName;
|
||||
} else if (
|
||||
schema.fields[fieldName] &&
|
||||
schema.fields[fieldName].type == 'Pointer'
|
||||
) {
|
||||
} else if (schema.fields[fieldName] && schema.fields[fieldName].type == 'Pointer') {
|
||||
fieldName = '_p_' + fieldName;
|
||||
}
|
||||
|
||||
return fieldName;
|
||||
};
|
||||
|
||||
const transformKeyValueForUpdate = (
|
||||
className,
|
||||
restKey,
|
||||
restValue,
|
||||
parseFormatSchema
|
||||
) => {
|
||||
const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSchema) => {
|
||||
// Check if the schema is known since it's a built-in field.
|
||||
var key = restKey;
|
||||
var timeField = false;
|
||||
@@ -109,11 +98,8 @@ const transformKeyValueForUpdate = (
|
||||
}
|
||||
|
||||
if (
|
||||
(parseFormatSchema.fields[key] &&
|
||||
parseFormatSchema.fields[key].type === 'Pointer') ||
|
||||
(!parseFormatSchema.fields[key] &&
|
||||
restValue &&
|
||||
restValue.__type == 'Pointer')
|
||||
(parseFormatSchema.fields[key] && parseFormatSchema.fields[key].type === 'Pointer') ||
|
||||
(!parseFormatSchema.fields[key] && restValue && restValue.__type == 'Pointer')
|
||||
) {
|
||||
key = '_p_' + key;
|
||||
}
|
||||
@@ -179,7 +165,7 @@ const isAllValuesRegexOrNone = values => {
|
||||
};
|
||||
|
||||
const isAnyValueRegex = values => {
|
||||
return values.some(function(value) {
|
||||
return values.some(function (value) {
|
||||
return isRegex(value);
|
||||
});
|
||||
};
|
||||
@@ -292,9 +278,7 @@ function transformQueryKeyValue(className, key, value, schema, count = false) {
|
||||
case '$nor':
|
||||
return {
|
||||
key: key,
|
||||
value: value.map(subQuery =>
|
||||
transformWhere(className, subQuery, schema, count)
|
||||
),
|
||||
value: value.map(subQuery => transformWhere(className, subQuery, schema, count)),
|
||||
};
|
||||
case 'lastUsed':
|
||||
if (valueAsDate(value)) {
|
||||
@@ -315,17 +299,13 @@ function transformQueryKeyValue(className, key, value, schema, count = false) {
|
||||
}
|
||||
}
|
||||
|
||||
const expectedTypeIsArray =
|
||||
schema && schema.fields[key] && schema.fields[key].type === 'Array';
|
||||
const expectedTypeIsArray = schema && schema.fields[key] && schema.fields[key].type === 'Array';
|
||||
|
||||
const expectedTypeIsPointer =
|
||||
schema && schema.fields[key] && schema.fields[key].type === 'Pointer';
|
||||
|
||||
const field = schema && schema.fields[key];
|
||||
if (
|
||||
expectedTypeIsPointer ||
|
||||
(!schema && value && value.__type === 'Pointer')
|
||||
) {
|
||||
if (expectedTypeIsPointer || (!schema && value && value.__type === 'Pointer')) {
|
||||
key = '_p_' + key;
|
||||
}
|
||||
|
||||
@@ -362,23 +342,13 @@ function transformQueryKeyValue(className, key, value, schema, count = false) {
|
||||
function transformWhere(className, restWhere, schema, count = false) {
|
||||
const mongoWhere = {};
|
||||
for (const restKey in restWhere) {
|
||||
const out = transformQueryKeyValue(
|
||||
className,
|
||||
restKey,
|
||||
restWhere[restKey],
|
||||
schema,
|
||||
count
|
||||
);
|
||||
const out = transformQueryKeyValue(className, restKey, restWhere[restKey], schema, count);
|
||||
mongoWhere[out.key] = out.value;
|
||||
}
|
||||
return mongoWhere;
|
||||
}
|
||||
|
||||
const parseObjectKeyValueToMongoObjectKeyValue = (
|
||||
restKey,
|
||||
restValue,
|
||||
schema
|
||||
) => {
|
||||
const parseObjectKeyValueToMongoObjectKeyValue = (restKey, restValue, schema) => {
|
||||
// Check if the schema is known since it's a built-in field.
|
||||
let transformedValue;
|
||||
let coercedToDate;
|
||||
@@ -388,37 +358,27 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
|
||||
case 'expiresAt':
|
||||
transformedValue = transformTopLevelAtom(restValue);
|
||||
coercedToDate =
|
||||
typeof transformedValue === 'string'
|
||||
? new Date(transformedValue)
|
||||
: transformedValue;
|
||||
typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
|
||||
return { key: 'expiresAt', value: coercedToDate };
|
||||
case '_email_verify_token_expires_at':
|
||||
transformedValue = transformTopLevelAtom(restValue);
|
||||
coercedToDate =
|
||||
typeof transformedValue === 'string'
|
||||
? new Date(transformedValue)
|
||||
: transformedValue;
|
||||
typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
|
||||
return { key: '_email_verify_token_expires_at', value: coercedToDate };
|
||||
case '_account_lockout_expires_at':
|
||||
transformedValue = transformTopLevelAtom(restValue);
|
||||
coercedToDate =
|
||||
typeof transformedValue === 'string'
|
||||
? new Date(transformedValue)
|
||||
: transformedValue;
|
||||
typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
|
||||
return { key: '_account_lockout_expires_at', value: coercedToDate };
|
||||
case '_perishable_token_expires_at':
|
||||
transformedValue = transformTopLevelAtom(restValue);
|
||||
coercedToDate =
|
||||
typeof transformedValue === 'string'
|
||||
? new Date(transformedValue)
|
||||
: transformedValue;
|
||||
typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
|
||||
return { key: '_perishable_token_expires_at', value: coercedToDate };
|
||||
case '_password_changed_at':
|
||||
transformedValue = transformTopLevelAtom(restValue);
|
||||
coercedToDate =
|
||||
typeof transformedValue === 'string'
|
||||
? new Date(transformedValue)
|
||||
: transformedValue;
|
||||
typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
|
||||
return { key: '_password_changed_at', value: coercedToDate };
|
||||
case '_failed_login_count':
|
||||
case '_rperm':
|
||||
@@ -432,10 +392,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
|
||||
default:
|
||||
// Auth data should have been transformed already
|
||||
if (restKey.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_KEY_NAME,
|
||||
'can only query on ' + restKey
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'can only query on ' + restKey);
|
||||
}
|
||||
// Trust that the auth data has been transformed and save it directly
|
||||
if (restKey.match(/^_auth_data_[a-zA-Z0-9_]+$/)) {
|
||||
@@ -473,9 +430,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
|
||||
}
|
||||
|
||||
// Handle normal objects by recursing
|
||||
if (
|
||||
Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))
|
||||
) {
|
||||
if (Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_NESTED_KEY,
|
||||
"Nested keys should not contain the '$' or '.' characters"
|
||||
@@ -504,15 +459,11 @@ const parseObjectToMongoObjectForCreate = (className, restCreate, schema) => {
|
||||
|
||||
// Use the legacy mongo format for createdAt and updatedAt
|
||||
if (mongoCreate.createdAt) {
|
||||
mongoCreate._created_at = new Date(
|
||||
mongoCreate.createdAt.iso || mongoCreate.createdAt
|
||||
);
|
||||
mongoCreate._created_at = new Date(mongoCreate.createdAt.iso || mongoCreate.createdAt);
|
||||
delete mongoCreate.createdAt;
|
||||
}
|
||||
if (mongoCreate.updatedAt) {
|
||||
mongoCreate._updated_at = new Date(
|
||||
mongoCreate.updatedAt.iso || mongoCreate.updatedAt
|
||||
);
|
||||
mongoCreate._updated_at = new Date(mongoCreate.updatedAt.iso || mongoCreate.updatedAt);
|
||||
delete mongoCreate.updatedAt;
|
||||
}
|
||||
|
||||
@@ -593,22 +544,14 @@ function CannotTransform() {}
|
||||
|
||||
const transformInteriorAtom = atom => {
|
||||
// TODO: check validity harder for the __type-defined types
|
||||
if (
|
||||
typeof atom === 'object' &&
|
||||
atom &&
|
||||
!(atom instanceof Date) &&
|
||||
atom.__type === 'Pointer'
|
||||
) {
|
||||
if (typeof atom === 'object' && atom && !(atom instanceof Date) && atom.__type === 'Pointer') {
|
||||
return {
|
||||
__type: 'Pointer',
|
||||
className: atom.className,
|
||||
objectId: atom.objectId,
|
||||
};
|
||||
} else if (typeof atom === 'function' || typeof atom === 'symbol') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`cannot transform value: ${atom}`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `cannot transform value: ${atom}`);
|
||||
} else if (DateCoder.isValidJSON(atom)) {
|
||||
return DateCoder.JSONToDatabase(atom);
|
||||
} else if (BytesCoder.isValidJSON(atom)) {
|
||||
@@ -640,10 +583,7 @@ function transformTopLevelAtom(atom, field) {
|
||||
return atom;
|
||||
case 'symbol':
|
||||
case 'function':
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`cannot transform value: ${atom}`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `cannot transform value: ${atom}`);
|
||||
case 'object':
|
||||
if (atom instanceof Date) {
|
||||
// Technically dates are not rest format, but, it seems pretty
|
||||
@@ -822,16 +762,11 @@ function transformConstraint(constraint, field, count = false) {
|
||||
if (typeof constraint !== 'object' || !constraint) {
|
||||
return CannotTransform;
|
||||
}
|
||||
const transformFunction = inArray
|
||||
? transformInteriorAtom
|
||||
: transformTopLevelAtom;
|
||||
const transformFunction = inArray ? transformInteriorAtom : transformTopLevelAtom;
|
||||
const transformer = atom => {
|
||||
const result = transformFunction(atom, field);
|
||||
if (result === CannotTransform) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad atom: ${JSON.stringify(atom)}`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad atom: ${JSON.stringify(atom)}`);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -839,9 +774,7 @@ function transformConstraint(constraint, field, count = false) {
|
||||
// This is a hack so that:
|
||||
// $regex is handled before $options
|
||||
// $nearSphere is handled before $maxDistance
|
||||
var keys = Object.keys(constraint)
|
||||
.sort()
|
||||
.reverse();
|
||||
var keys = Object.keys(constraint).sort().reverse();
|
||||
var answer = {};
|
||||
for (var key of keys) {
|
||||
switch (key) {
|
||||
@@ -892,10 +825,7 @@ function transformConstraint(constraint, field, count = false) {
|
||||
case '$nin': {
|
||||
const arr = constraint[key];
|
||||
if (!(arr instanceof Array)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
'bad ' + key + ' value'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value');
|
||||
}
|
||||
answer[key] = _.flatMap(arr, value => {
|
||||
return (atom => {
|
||||
@@ -911,10 +841,7 @@ function transformConstraint(constraint, field, count = false) {
|
||||
case '$all': {
|
||||
const arr = constraint[key];
|
||||
if (!(arr instanceof Array)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
'bad ' + key + ' value'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value');
|
||||
}
|
||||
answer[key] = arr.map(transformInteriorAtom);
|
||||
|
||||
@@ -939,10 +866,7 @@ function transformConstraint(constraint, field, count = false) {
|
||||
case '$containedBy': {
|
||||
const arr = constraint[key];
|
||||
if (!(arr instanceof Array)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad $containedBy: should be an array`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $containedBy: should be an array`);
|
||||
}
|
||||
answer.$elemMatch = {
|
||||
$nin: arr.map(transformer),
|
||||
@@ -956,33 +880,21 @@ function transformConstraint(constraint, field, count = false) {
|
||||
case '$text': {
|
||||
const search = constraint[key].$search;
|
||||
if (typeof search !== 'object') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad $text: $search, should be object`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $search, should be object`);
|
||||
}
|
||||
if (!search.$term || typeof search.$term !== 'string') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad $text: $term, should be string`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $term, should be string`);
|
||||
} else {
|
||||
answer[key] = {
|
||||
$search: search.$term,
|
||||
};
|
||||
}
|
||||
if (search.$language && typeof search.$language !== 'string') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad $text: $language, should be string`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $language, should be string`);
|
||||
} else if (search.$language) {
|
||||
answer[key].$language = search.$language;
|
||||
}
|
||||
if (
|
||||
search.$caseSensitive &&
|
||||
typeof search.$caseSensitive !== 'boolean'
|
||||
) {
|
||||
if (search.$caseSensitive && typeof search.$caseSensitive !== 'boolean') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad $text: $caseSensitive, should be boolean`
|
||||
@@ -990,10 +902,7 @@ function transformConstraint(constraint, field, count = false) {
|
||||
} else if (search.$caseSensitive) {
|
||||
answer[key].$caseSensitive = search.$caseSensitive;
|
||||
}
|
||||
if (
|
||||
search.$diacriticSensitive &&
|
||||
typeof search.$diacriticSensitive !== 'boolean'
|
||||
) {
|
||||
if (search.$diacriticSensitive && typeof search.$diacriticSensitive !== 'boolean') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad $text: $diacriticSensitive, should be boolean`
|
||||
@@ -1007,10 +916,7 @@ function transformConstraint(constraint, field, count = false) {
|
||||
const point = constraint[key];
|
||||
if (count) {
|
||||
answer.$geoWithin = {
|
||||
$centerSphere: [
|
||||
[point.longitude, point.latitude],
|
||||
constraint.$maxDistance,
|
||||
],
|
||||
$centerSphere: [[point.longitude, point.latitude], constraint.$maxDistance],
|
||||
};
|
||||
} else {
|
||||
answer[key] = [point.longitude, point.latitude];
|
||||
@@ -1046,10 +952,7 @@ function transformConstraint(constraint, field, count = false) {
|
||||
case '$within':
|
||||
var box = constraint[key]['$box'];
|
||||
if (!box || box.length != 2) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
'malformatted $within arg'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'malformatted $within arg');
|
||||
}
|
||||
answer[key] = {
|
||||
$box: [
|
||||
@@ -1092,10 +995,7 @@ function transformConstraint(constraint, field, count = false) {
|
||||
return point;
|
||||
}
|
||||
if (!GeoPointCoder.isValidJSON(point)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
'bad $geoWithin value'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value');
|
||||
} else {
|
||||
Parse.GeoPoint._validate(point.latitude, point.longitude);
|
||||
}
|
||||
@@ -1156,10 +1056,7 @@ function transformConstraint(constraint, field, count = false) {
|
||||
}
|
||||
default:
|
||||
if (key.match(/^\$+/)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
'bad constraint: ' + key
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad constraint: ' + key);
|
||||
}
|
||||
return CannotTransform;
|
||||
}
|
||||
@@ -1188,10 +1085,7 @@ function transformUpdateOperator({ __op, amount, objects }, flatten) {
|
||||
|
||||
case 'Increment':
|
||||
if (typeof amount !== 'number') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
'incrementing must provide a number'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'incrementing must provide a number');
|
||||
}
|
||||
if (flatten) {
|
||||
return amount;
|
||||
@@ -1202,10 +1096,7 @@ function transformUpdateOperator({ __op, amount, objects }, flatten) {
|
||||
case 'Add':
|
||||
case 'AddUnique':
|
||||
if (!(objects instanceof Array)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
'objects to add must be an array'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
|
||||
}
|
||||
var toAdd = objects.map(transformInteriorAtom);
|
||||
if (flatten) {
|
||||
@@ -1220,10 +1111,7 @@ function transformUpdateOperator({ __op, amount, objects }, flatten) {
|
||||
|
||||
case 'Remove':
|
||||
if (!(objects instanceof Array)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
'objects to remove must be an array'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to remove must be an array');
|
||||
}
|
||||
var toRemove = objects.map(transformInteriorAtom);
|
||||
if (flatten) {
|
||||
@@ -1379,15 +1267,11 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
|
||||
break;
|
||||
case 'updatedAt':
|
||||
case '_updated_at':
|
||||
restObject['updatedAt'] = Parse._encode(
|
||||
new Date(mongoObject[key])
|
||||
).iso;
|
||||
restObject['updatedAt'] = Parse._encode(new Date(mongoObject[key])).iso;
|
||||
break;
|
||||
case 'createdAt':
|
||||
case '_created_at':
|
||||
restObject['createdAt'] = Parse._encode(
|
||||
new Date(mongoObject[key])
|
||||
).iso;
|
||||
restObject['createdAt'] = Parse._encode(new Date(mongoObject[key])).iso;
|
||||
break;
|
||||
case 'expiresAt':
|
||||
case '_expiresAt':
|
||||
@@ -1395,9 +1279,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
|
||||
break;
|
||||
case 'lastUsed':
|
||||
case '_last_used':
|
||||
restObject['lastUsed'] = Parse._encode(
|
||||
new Date(mongoObject[key])
|
||||
).iso;
|
||||
restObject['lastUsed'] = Parse._encode(new Date(mongoObject[key])).iso;
|
||||
break;
|
||||
case 'timesUsed':
|
||||
case 'times_used':
|
||||
@@ -1445,11 +1327,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
|
||||
if (mongoObject[key] === null) {
|
||||
break;
|
||||
}
|
||||
restObject[newKey] = transformPointerString(
|
||||
schema,
|
||||
newKey,
|
||||
mongoObject[key]
|
||||
);
|
||||
restObject[newKey] = transformPointerString(schema, newKey, mongoObject[key]);
|
||||
break;
|
||||
} else if (key[0] == '_' && key != '__type') {
|
||||
throw 'bad key in untransform: ' + key;
|
||||
@@ -1488,9 +1366,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
restObject[key] = nestedMongoObjectToNestedParseObject(
|
||||
mongoObject[key]
|
||||
);
|
||||
restObject[key] = nestedMongoObjectToNestedParseObject(mongoObject[key]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1518,16 +1394,12 @@ var DateCoder = {
|
||||
},
|
||||
|
||||
isValidJSON(value) {
|
||||
return (
|
||||
typeof value === 'object' && value !== null && value.__type === 'Date'
|
||||
);
|
||||
return typeof value === 'object' && value !== null && value.__type === 'Date';
|
||||
},
|
||||
};
|
||||
|
||||
var BytesCoder = {
|
||||
base64Pattern: new RegExp(
|
||||
'^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'
|
||||
),
|
||||
base64Pattern: new RegExp('^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'),
|
||||
isBase64Value(object) {
|
||||
if (typeof object !== 'string') {
|
||||
return false;
|
||||
@@ -1557,9 +1429,7 @@ var BytesCoder = {
|
||||
},
|
||||
|
||||
isValidJSON(value) {
|
||||
return (
|
||||
typeof value === 'object' && value !== null && value.__type === 'Bytes'
|
||||
);
|
||||
return typeof value === 'object' && value !== null && value.__type === 'Bytes';
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1581,9 +1451,7 @@ var GeoPointCoder = {
|
||||
},
|
||||
|
||||
isValidJSON(value) {
|
||||
return (
|
||||
typeof value === 'object' && value !== null && value.__type === 'GeoPoint'
|
||||
);
|
||||
return typeof value === 'object' && value !== null && value.__type === 'GeoPoint';
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1648,9 +1516,7 @@ var PolygonCoder = {
|
||||
},
|
||||
|
||||
isValidJSON(value) {
|
||||
return (
|
||||
typeof value === 'object' && value !== null && value.__type === 'Polygon'
|
||||
);
|
||||
return typeof value === 'object' && value !== null && value.__type === 'Polygon';
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1671,9 +1537,7 @@ var FileCoder = {
|
||||
},
|
||||
|
||||
isValidJSON(value) {
|
||||
return (
|
||||
typeof value === 'object' && value !== null && value.__type === 'File'
|
||||
);
|
||||
return typeof value === 'object' && value !== null && value.__type === 'File';
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -9,9 +9,7 @@ function getDatabaseOptionsFromURI(uri) {
|
||||
|
||||
databaseOptions.host = parsedURI.hostname || 'localhost';
|
||||
databaseOptions.port = parsedURI.port ? parseInt(parsedURI.port) : 5432;
|
||||
databaseOptions.database = parsedURI.pathname
|
||||
? parsedURI.pathname.substr(1)
|
||||
: undefined;
|
||||
databaseOptions.database = parsedURI.pathname ? parsedURI.pathname.substr(1) : undefined;
|
||||
|
||||
databaseOptions.user = authParts.length > 0 ? authParts[0] : '';
|
||||
databaseOptions.password = authParts.length > 1 ? authParts[1] : '';
|
||||
@@ -55,14 +53,11 @@ function getDatabaseOptionsFromURI(uri) {
|
||||
}
|
||||
|
||||
databaseOptions.binary =
|
||||
queryParams.binary && queryParams.binary.toLowerCase() === 'true'
|
||||
? true
|
||||
: false;
|
||||
queryParams.binary && queryParams.binary.toLowerCase() === 'true' ? true : false;
|
||||
|
||||
databaseOptions.client_encoding = queryParams.client_encoding;
|
||||
databaseOptions.application_name = queryParams.application_name;
|
||||
databaseOptions.fallback_application_name =
|
||||
queryParams.fallback_application_name;
|
||||
databaseOptions.fallback_application_name = queryParams.fallback_application_name;
|
||||
|
||||
if (queryParams.poolSize) {
|
||||
databaseOptions.poolSize = parseInt(queryParams.poolSize) || 10;
|
||||
@@ -77,8 +72,7 @@ function getDatabaseOptionsFromURI(uri) {
|
||||
databaseOptions.idleTimeoutMillis = parseInt(queryParams.idleTimeoutMillis);
|
||||
}
|
||||
if (queryParams.keepAlive) {
|
||||
databaseOptions.keepAlive =
|
||||
queryParams.keepAlive.toLowerCase() === 'true' ? true : false;
|
||||
databaseOptions.keepAlive = queryParams.keepAlive.toLowerCase() === 'true' ? true : false;
|
||||
}
|
||||
|
||||
return databaseOptions;
|
||||
|
||||
@@ -253,12 +253,7 @@ interface WhereClause {
|
||||
sorts: Array<any>;
|
||||
}
|
||||
|
||||
const buildWhereClause = ({
|
||||
schema,
|
||||
query,
|
||||
index,
|
||||
caseInsensitive,
|
||||
}): WhereClause => {
|
||||
const buildWhereClause = ({ schema, query, index, caseInsensitive }): WhereClause => {
|
||||
const patterns = [];
|
||||
let values = [];
|
||||
const sorts = [];
|
||||
@@ -266,9 +261,7 @@ const buildWhereClause = ({
|
||||
schema = toPostgresSchema(schema);
|
||||
for (const fieldName in query) {
|
||||
const isArrayField =
|
||||
schema.fields &&
|
||||
schema.fields[fieldName] &&
|
||||
schema.fields[fieldName].type === 'Array';
|
||||
schema.fields && schema.fields[fieldName] && schema.fields[fieldName].type === 'Array';
|
||||
const initialPatternsLength = patterns.length;
|
||||
const fieldValue = query[fieldName];
|
||||
|
||||
@@ -284,10 +277,7 @@ const buildWhereClause = ({
|
||||
if (authDataMatch) {
|
||||
// TODO: Handle querying by _auth_data_provider, authData is stored in authData field
|
||||
continue;
|
||||
} else if (
|
||||
caseInsensitive &&
|
||||
(fieldName === 'username' || fieldName === 'email')
|
||||
) {
|
||||
} else if (caseInsensitive && (fieldName === 'username' || fieldName === 'email')) {
|
||||
patterns.push(`LOWER($${index}:name) = LOWER($${index + 1})`);
|
||||
values.push(fieldName, fieldValue);
|
||||
index += 2;
|
||||
@@ -324,10 +314,7 @@ const buildWhereClause = ({
|
||||
} else if (typeof fieldValue === 'boolean') {
|
||||
patterns.push(`$${index}:name = $${index + 1}`);
|
||||
// Can't cast boolean to double precision
|
||||
if (
|
||||
schema.fields[fieldName] &&
|
||||
schema.fields[fieldName].type === 'Number'
|
||||
) {
|
||||
if (schema.fields[fieldName] && schema.fields[fieldName].type === 'Number') {
|
||||
// Should always return zero results
|
||||
const MAX_INT_PLUS_ONE = 9223372036854775808;
|
||||
values.push(fieldName, MAX_INT_PLUS_ONE);
|
||||
@@ -377,9 +364,7 @@ const buildWhereClause = ({
|
||||
// if not null, we need to manually exclude null
|
||||
if (fieldValue.$ne.__type === 'GeoPoint') {
|
||||
patterns.push(
|
||||
`($${index}:name <> POINT($${index + 1}, $${
|
||||
index + 2
|
||||
}) OR $${index}:name IS NULL)`
|
||||
`($${index}:name <> POINT($${index + 1}, $${index + 2}) OR $${index}:name IS NULL)`
|
||||
);
|
||||
} else {
|
||||
if (fieldName.indexOf('.') >= 0) {
|
||||
@@ -388,9 +373,7 @@ const buildWhereClause = ({
|
||||
`(${constraintFieldName} <> $${index} OR ${constraintFieldName} IS NULL)`
|
||||
);
|
||||
} else {
|
||||
patterns.push(
|
||||
`($${index}:name <> $${index + 1} OR $${index}:name IS NULL)`
|
||||
);
|
||||
patterns.push(`($${index}:name <> $${index + 1} OR $${index}:name IS NULL)`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -421,8 +404,7 @@ const buildWhereClause = ({
|
||||
}
|
||||
}
|
||||
}
|
||||
const isInOrNin =
|
||||
Array.isArray(fieldValue.$in) || Array.isArray(fieldValue.$nin);
|
||||
const isInOrNin = Array.isArray(fieldValue.$in) || Array.isArray(fieldValue.$nin);
|
||||
if (
|
||||
Array.isArray(fieldValue.$in) &&
|
||||
isArrayField &&
|
||||
@@ -441,9 +423,7 @@ const buildWhereClause = ({
|
||||
}
|
||||
});
|
||||
if (allowNull) {
|
||||
patterns.push(
|
||||
`($${index}:name IS NULL OR $${index}:name && ARRAY[${inPatterns.join()}])`
|
||||
);
|
||||
patterns.push(`($${index}:name IS NULL OR $${index}:name && ARRAY[${inPatterns.join()}])`);
|
||||
} else {
|
||||
patterns.push(`$${index}:name && ARRAY[${inPatterns.join()}]`);
|
||||
}
|
||||
@@ -453,9 +433,7 @@ const buildWhereClause = ({
|
||||
const not = notIn ? ' NOT ' : '';
|
||||
if (baseArray.length > 0) {
|
||||
if (isArrayField) {
|
||||
patterns.push(
|
||||
`${not} array_contains($${index}:name, $${index + 1})`
|
||||
);
|
||||
patterns.push(`${not} array_contains($${index}:name, $${index + 1})`);
|
||||
values.push(fieldName, JSON.stringify(baseArray));
|
||||
index += 2;
|
||||
} else {
|
||||
@@ -518,13 +496,9 @@ const buildWhereClause = ({
|
||||
const value = processRegexPattern(fieldValue.$all[i].$regex);
|
||||
fieldValue.$all[i] = value.substring(1) + '%';
|
||||
}
|
||||
patterns.push(
|
||||
`array_contains_all_regex($${index}:name, $${index + 1}::jsonb)`
|
||||
);
|
||||
patterns.push(`array_contains_all_regex($${index}:name, $${index + 1}::jsonb)`);
|
||||
} else {
|
||||
patterns.push(
|
||||
`array_contains_all($${index}:name, $${index + 1}::jsonb)`
|
||||
);
|
||||
patterns.push(`array_contains_all($${index}:name, $${index + 1}::jsonb)`);
|
||||
}
|
||||
values.push(fieldName, JSON.stringify(fieldValue.$all));
|
||||
index += 2;
|
||||
@@ -549,10 +523,7 @@ const buildWhereClause = ({
|
||||
if (fieldValue.$containedBy) {
|
||||
const arr = fieldValue.$containedBy;
|
||||
if (!(arr instanceof Array)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad $containedBy: should be an array`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $containedBy: should be an array`);
|
||||
}
|
||||
|
||||
patterns.push(`$${index}:name <@ $${index + 1}::jsonb`);
|
||||
@@ -564,22 +535,13 @@ const buildWhereClause = ({
|
||||
const search = fieldValue.$text.$search;
|
||||
let language = 'english';
|
||||
if (typeof search !== 'object') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad $text: $search, should be object`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $search, should be object`);
|
||||
}
|
||||
if (!search.$term || typeof search.$term !== 'string') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad $text: $term, should be string`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $term, should be string`);
|
||||
}
|
||||
if (search.$language && typeof search.$language !== 'string') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad $text: $language, should be string`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $language, should be string`);
|
||||
} else if (search.$language) {
|
||||
language = search.$language;
|
||||
}
|
||||
@@ -594,10 +556,7 @@ const buildWhereClause = ({
|
||||
`bad $text: $caseSensitive not supported, please use $regex or create a separate lower case column.`
|
||||
);
|
||||
}
|
||||
if (
|
||||
search.$diacriticSensitive &&
|
||||
typeof search.$diacriticSensitive !== 'boolean'
|
||||
) {
|
||||
if (search.$diacriticSensitive && typeof search.$diacriticSensitive !== 'boolean') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`bad $text: $diacriticSensitive, should be boolean`
|
||||
@@ -609,9 +568,7 @@ const buildWhereClause = ({
|
||||
);
|
||||
}
|
||||
patterns.push(
|
||||
`to_tsvector($${index}, $${index + 1}:name) @@ to_tsquery($${
|
||||
index + 2
|
||||
}, $${index + 3})`
|
||||
`to_tsvector($${index}, $${index + 1}:name) @@ to_tsquery($${index + 2}, $${index + 3})`
|
||||
);
|
||||
values.push(language, fieldName, language, search.$term);
|
||||
index += 4;
|
||||
@@ -716,10 +673,7 @@ const buildWhereClause = ({
|
||||
return `(${point[0]}, ${point[1]})`;
|
||||
}
|
||||
if (typeof point !== 'object' || point.__type !== 'GeoPoint') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
'bad $geoWithin value'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value');
|
||||
} else {
|
||||
Parse.GeoPoint._validate(point.latitude, point.longitude);
|
||||
}
|
||||
@@ -830,9 +784,7 @@ const buildWhereClause = ({
|
||||
if (initialPatternsLength === patterns.length) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OPERATION_FORBIDDEN,
|
||||
`Postgres doesn't support this query type yet ${JSON.stringify(
|
||||
fieldValue
|
||||
)}`
|
||||
`Postgres doesn't support this query type yet ${JSON.stringify(fieldValue)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -903,12 +855,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
const self = this;
|
||||
await this._client.task('set-class-level-permissions', async t => {
|
||||
await self._ensureSchemaCollectionExists(t);
|
||||
const values = [
|
||||
className,
|
||||
'schema',
|
||||
'classLevelPermissions',
|
||||
JSON.stringify(CLPs),
|
||||
];
|
||||
const values = [className, 'schema', 'classLevelPermissions', JSON.stringify(CLPs)];
|
||||
await t.none(
|
||||
`UPDATE "_SCHEMA" SET $2:name = json_object_set_key($2:name, $3::text, $4::jsonb) WHERE "className" = $1`,
|
||||
values
|
||||
@@ -936,10 +883,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
Object.keys(submittedIndexes).forEach(name => {
|
||||
const field = submittedIndexes[name];
|
||||
if (existingIndexes[name] && field.__op !== 'Delete') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_QUERY,
|
||||
`Index ${name} exists, cannot update.`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, `Index ${name} exists, cannot update.`);
|
||||
}
|
||||
if (!existingIndexes[name] && field.__op === 'Delete') {
|
||||
throw new Parse.Error(
|
||||
@@ -990,24 +934,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
'INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)',
|
||||
{ className, schema }
|
||||
);
|
||||
await this.setIndexesWithSchemaFormat(
|
||||
className,
|
||||
schema.indexes,
|
||||
{},
|
||||
schema.fields,
|
||||
t
|
||||
);
|
||||
await this.setIndexesWithSchemaFormat(className, schema.indexes, {}, schema.fields, t);
|
||||
return toParseSchema(schema);
|
||||
})
|
||||
.catch(err => {
|
||||
if (
|
||||
err.code === PostgresUniqueIndexViolationError &&
|
||||
err.detail.includes(className)
|
||||
) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.DUPLICATE_VALUE,
|
||||
`Class ${className} already exists.`
|
||||
);
|
||||
if (err.code === PostgresUniqueIndexViolationError && err.detail.includes(className)) {
|
||||
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, `Class ${className} already exists.`);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
@@ -1093,24 +1025,14 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
const newColumns = Object.keys(schema.fields)
|
||||
.filter(item => columns.indexOf(item) === -1)
|
||||
.map(fieldName =>
|
||||
self.addFieldIfNotExists(
|
||||
className,
|
||||
fieldName,
|
||||
schema.fields[fieldName],
|
||||
t
|
||||
)
|
||||
self.addFieldIfNotExists(className, fieldName, schema.fields[fieldName], t)
|
||||
);
|
||||
|
||||
await t.batch(newColumns);
|
||||
});
|
||||
}
|
||||
|
||||
async addFieldIfNotExists(
|
||||
className: string,
|
||||
fieldName: string,
|
||||
type: any,
|
||||
conn: any
|
||||
) {
|
||||
async addFieldIfNotExists(className: string, fieldName: string, type: any, conn: any) {
|
||||
// TODO: Must be revised for invalid logic...
|
||||
debug('addFieldIfNotExists', { className, fieldName, type });
|
||||
conn = conn || this._client;
|
||||
@@ -1128,11 +1050,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
);
|
||||
} catch (error) {
|
||||
if (error.code === PostgresRelationDoesNotExistError) {
|
||||
return self.createClass(
|
||||
className,
|
||||
{ fields: { [fieldName]: type } },
|
||||
t
|
||||
);
|
||||
return self.createClass(className, { fields: { [fieldName]: type } }, t);
|
||||
}
|
||||
if (error.code !== PostgresDuplicateColumnError) {
|
||||
throw error;
|
||||
@@ -1234,11 +1152,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
// may do so.
|
||||
|
||||
// Returns a Promise.
|
||||
async deleteFields(
|
||||
className: string,
|
||||
schema: SchemaType,
|
||||
fieldNames: string[]
|
||||
): Promise<void> {
|
||||
async deleteFields(className: string, schema: SchemaType, fieldNames: string[]): Promise<void> {
|
||||
debug('deleteFields', className, fieldNames);
|
||||
fieldNames = fieldNames.reduce((list: Array<string>, fieldName: string) => {
|
||||
const field = schema.fields[fieldName];
|
||||
@@ -1257,15 +1171,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
.join(', DROP COLUMN');
|
||||
|
||||
await this._client.tx('delete-fields', async t => {
|
||||
await t.none(
|
||||
'UPDATE "_SCHEMA" SET "schema" = $<schema> WHERE "className" = $<className>',
|
||||
{ schema, className }
|
||||
);
|
||||
await t.none('UPDATE "_SCHEMA" SET "schema" = $<schema> WHERE "className" = $<className>', {
|
||||
schema,
|
||||
className,
|
||||
});
|
||||
if (values.length > 1) {
|
||||
await t.none(
|
||||
`ALTER TABLE $1:name DROP COLUMN IF EXISTS ${columns}`,
|
||||
values
|
||||
);
|
||||
await t.none(`ALTER TABLE $1:name DROP COLUMN IF EXISTS ${columns}`, values);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1412,10 +1323,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
const fieldName = columnsArray[index];
|
||||
if (['_rperm', '_wperm'].indexOf(fieldName) >= 0) {
|
||||
termination = '::text[]';
|
||||
} else if (
|
||||
schema.fields[fieldName] &&
|
||||
schema.fields[fieldName].type === 'Array'
|
||||
) {
|
||||
} else if (schema.fields[fieldName] && schema.fields[fieldName].type === 'Array') {
|
||||
termination = '::jsonb';
|
||||
}
|
||||
return `$${index + 2 + columnsArray.length}${termination}`;
|
||||
@@ -1427,18 +1335,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
return `POINT($${l}, $${l + 1})`;
|
||||
});
|
||||
|
||||
const columnsPattern = columnsArray
|
||||
.map((col, index) => `$${index + 2}:name`)
|
||||
.join();
|
||||
const columnsPattern = columnsArray.map((col, index) => `$${index + 2}:name`).join();
|
||||
const valuesPattern = initialValues.concat(geoPointsInjects).join();
|
||||
|
||||
const qs = `INSERT INTO $1:name (${columnsPattern}) VALUES (${valuesPattern})`;
|
||||
const values = [className, ...columnsArray, ...valuesArray];
|
||||
debug(qs, values);
|
||||
const promise = (transactionalSession
|
||||
? transactionalSession.t
|
||||
: this._client
|
||||
)
|
||||
const promise = (transactionalSession ? transactionalSession.t : this._client)
|
||||
.none(qs, values)
|
||||
.then(() => ({ ops: [object] }))
|
||||
.catch(error => {
|
||||
@@ -1488,17 +1391,11 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
const qs = `WITH deleted AS (DELETE FROM $1:name WHERE ${where.pattern} RETURNING *) SELECT count(*) FROM deleted`;
|
||||
debug(qs, values);
|
||||
const promise = (transactionalSession
|
||||
? transactionalSession.t
|
||||
: this._client
|
||||
)
|
||||
const promise = (transactionalSession ? transactionalSession.t : this._client)
|
||||
.one(qs, values, a => +a.count)
|
||||
.then(count => {
|
||||
if (count === 0) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Object not found.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
|
||||
} else {
|
||||
return count;
|
||||
}
|
||||
@@ -1523,13 +1420,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
transactionalSession: ?any
|
||||
): Promise<any> {
|
||||
debug('findOneAndUpdate', className, query, update);
|
||||
return this.updateObjectsByQuery(
|
||||
className,
|
||||
schema,
|
||||
query,
|
||||
update,
|
||||
transactionalSession
|
||||
).then(val => val[0]);
|
||||
return this.updateObjectsByQuery(className, schema, query, update, transactionalSession).then(
|
||||
val => val[0]
|
||||
);
|
||||
}
|
||||
|
||||
// Apply the update to all objects that match the given Parse Query.
|
||||
@@ -1592,39 +1485,28 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
const fieldNameIndex = index;
|
||||
index += 1;
|
||||
values.push(fieldName);
|
||||
const update = Object.keys(fieldValue).reduce(
|
||||
(lastKey: string, key: string) => {
|
||||
const str = generate(
|
||||
lastKey,
|
||||
`$${index}::text`,
|
||||
`$${index + 1}::jsonb`
|
||||
);
|
||||
index += 2;
|
||||
let value = fieldValue[key];
|
||||
if (value) {
|
||||
if (value.__op === 'Delete') {
|
||||
value = null;
|
||||
} else {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
const update = Object.keys(fieldValue).reduce((lastKey: string, key: string) => {
|
||||
const str = generate(lastKey, `$${index}::text`, `$${index + 1}::jsonb`);
|
||||
index += 2;
|
||||
let value = fieldValue[key];
|
||||
if (value) {
|
||||
if (value.__op === 'Delete') {
|
||||
value = null;
|
||||
} else {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
values.push(key, value);
|
||||
return str;
|
||||
},
|
||||
lastKey
|
||||
);
|
||||
}
|
||||
values.push(key, value);
|
||||
return str;
|
||||
}, lastKey);
|
||||
updatePatterns.push(`$${fieldNameIndex}:name = ${update}`);
|
||||
} else if (fieldValue.__op === 'Increment') {
|
||||
updatePatterns.push(
|
||||
`$${index}:name = COALESCE($${index}:name, 0) + $${index + 1}`
|
||||
);
|
||||
updatePatterns.push(`$${index}:name = COALESCE($${index}:name, 0) + $${index + 1}`);
|
||||
values.push(fieldName, fieldValue.amount);
|
||||
index += 2;
|
||||
} else if (fieldValue.__op === 'Add') {
|
||||
updatePatterns.push(
|
||||
`$${index}:name = array_add(COALESCE($${index}:name, '[]'::jsonb), $${
|
||||
index + 1
|
||||
}::jsonb)`
|
||||
`$${index}:name = array_add(COALESCE($${index}:name, '[]'::jsonb), $${index + 1}::jsonb)`
|
||||
);
|
||||
values.push(fieldName, JSON.stringify(fieldValue.objects));
|
||||
index += 2;
|
||||
@@ -1678,9 +1560,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
values.push(fieldName, toPostgresValue(fieldValue));
|
||||
index += 2;
|
||||
} else if (fieldValue.__type === 'GeoPoint') {
|
||||
updatePatterns.push(
|
||||
`$${index}:name = POINT($${index + 1}, $${index + 2})`
|
||||
);
|
||||
updatePatterns.push(`$${index}:name = POINT($${index + 1}, $${index + 2})`);
|
||||
values.push(fieldName, fieldValue.longitude, fieldValue.latitude);
|
||||
index += 3;
|
||||
} else if (fieldValue.__type === 'Polygon') {
|
||||
@@ -1745,12 +1625,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
})
|
||||
.map(k => k.split('.')[1]);
|
||||
|
||||
const deletePatterns = keysToDelete.reduce(
|
||||
(p: string, c: string, i: number) => {
|
||||
return p + ` - '$${index + 1 + i}:value'`;
|
||||
},
|
||||
''
|
||||
);
|
||||
const deletePatterns = keysToDelete.reduce((p: string, c: string, i: number) => {
|
||||
return p + ` - '$${index + 1 + i}:value'`;
|
||||
}, '');
|
||||
// Override Object
|
||||
let updateObject = "'{}'::jsonb";
|
||||
|
||||
@@ -1799,14 +1676,10 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
values.push(...where.values);
|
||||
|
||||
const whereClause =
|
||||
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||
const whereClause = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||
const qs = `UPDATE $1:name SET ${updatePatterns.join()} ${whereClause} RETURNING *`;
|
||||
debug('update: ', qs, values);
|
||||
const promise = (transactionalSession
|
||||
? transactionalSession.t
|
||||
: this._client
|
||||
).any(qs, values);
|
||||
const promise = (transactionalSession ? transactionalSession.t : this._client).any(qs, values);
|
||||
if (transactionalSession) {
|
||||
transactionalSession.batch.push(promise);
|
||||
}
|
||||
@@ -1823,23 +1696,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
) {
|
||||
debug('upsertOneObject', { className, query, update });
|
||||
const createValue = Object.assign({}, query, update);
|
||||
return this.createObject(
|
||||
className,
|
||||
schema,
|
||||
createValue,
|
||||
transactionalSession
|
||||
).catch(error => {
|
||||
return this.createObject(className, schema, createValue, transactionalSession).catch(error => {
|
||||
// ignore duplicate value errors as it's upsert
|
||||
if (error.code !== Parse.Error.DUPLICATE_VALUE) {
|
||||
throw error;
|
||||
}
|
||||
return this.findOneAndUpdate(
|
||||
className,
|
||||
schema,
|
||||
query,
|
||||
update,
|
||||
transactionalSession
|
||||
);
|
||||
return this.findOneAndUpdate(className, schema, query, update, transactionalSession);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1868,8 +1730,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
values.push(...where.values);
|
||||
|
||||
const wherePattern =
|
||||
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||
const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||
const limitPattern = hasLimit ? `LIMIT $${values.length + 1}` : '';
|
||||
if (hasLimit) {
|
||||
values.push(limit);
|
||||
@@ -1892,10 +1753,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
return `${transformKey} DESC`;
|
||||
})
|
||||
.join();
|
||||
sortPattern =
|
||||
sort !== undefined && Object.keys(sort).length > 0
|
||||
? `ORDER BY ${sorting}`
|
||||
: '';
|
||||
sortPattern = sort !== undefined && Object.keys(sort).length > 0 ? `ORDER BY ${sorting}` : '';
|
||||
}
|
||||
if (where.sorts && Object.keys((where.sorts: any)).length > 0) {
|
||||
sortPattern = `ORDER BY ${where.sorts.join()}`;
|
||||
@@ -1926,9 +1784,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
|
||||
const originalQuery = `SELECT ${columns} FROM $1:name ${wherePattern} ${sortPattern} ${limitPattern} ${skipPattern}`;
|
||||
const qs = explain
|
||||
? this.createExplainableQuery(originalQuery)
|
||||
: originalQuery;
|
||||
const qs = explain ? this.createExplainableQuery(originalQuery) : originalQuery;
|
||||
debug(qs, values);
|
||||
return this._client
|
||||
.any(qs, values)
|
||||
@@ -1943,9 +1799,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
if (explain) {
|
||||
return results;
|
||||
}
|
||||
return results.map(object =>
|
||||
this.postgresObjectToParseObject(className, object, schema)
|
||||
);
|
||||
return results.map(object => this.postgresObjectToParseObject(className, object, schema));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1977,10 +1831,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
let coords = object[fieldName];
|
||||
coords = coords.substr(2, coords.length - 4).split('),(');
|
||||
coords = coords.map(point => {
|
||||
return [
|
||||
parseFloat(point.split(',')[1]),
|
||||
parseFloat(point.split(',')[0]),
|
||||
];
|
||||
return [parseFloat(point.split(',')[1]), parseFloat(point.split(',')[0])];
|
||||
});
|
||||
object[fieldName] = {
|
||||
__type: 'Polygon',
|
||||
@@ -2052,37 +1903,26 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
// As such, we shouldn't expose this function to users of parse until we have an out-of-band
|
||||
// Way of determining if a field is nullable. Undefined doesn't count against uniqueness,
|
||||
// which is why we use sparse indexes.
|
||||
async ensureUniqueness(
|
||||
className: string,
|
||||
schema: SchemaType,
|
||||
fieldNames: string[]
|
||||
) {
|
||||
async ensureUniqueness(className: string, schema: SchemaType, fieldNames: string[]) {
|
||||
const constraintName = `${className}_unique_${fieldNames.sort().join('_')}`;
|
||||
const constraintPatterns = fieldNames.map(
|
||||
(fieldName, index) => `$${index + 3}:name`
|
||||
);
|
||||
const constraintPatterns = fieldNames.map((fieldName, index) => `$${index + 3}:name`);
|
||||
const qs = `CREATE UNIQUE INDEX IF NOT EXISTS $2:name ON $1:name(${constraintPatterns.join()})`;
|
||||
return this._client
|
||||
.none(qs, [className, constraintName, ...fieldNames])
|
||||
.catch(error => {
|
||||
if (
|
||||
error.code === PostgresDuplicateRelationError &&
|
||||
error.message.includes(constraintName)
|
||||
) {
|
||||
// Index already exists. Ignore error.
|
||||
} else if (
|
||||
error.code === PostgresUniqueIndexViolationError &&
|
||||
error.message.includes(constraintName)
|
||||
) {
|
||||
// Cast the error into the proper parse error
|
||||
throw new Parse.Error(
|
||||
Parse.Error.DUPLICATE_VALUE,
|
||||
'A duplicate value for a field with unique values was provided'
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
return this._client.none(qs, [className, constraintName, ...fieldNames]).catch(error => {
|
||||
if (error.code === PostgresDuplicateRelationError && error.message.includes(constraintName)) {
|
||||
// Index already exists. Ignore error.
|
||||
} else if (
|
||||
error.code === PostgresUniqueIndexViolationError &&
|
||||
error.message.includes(constraintName)
|
||||
) {
|
||||
// Cast the error into the proper parse error
|
||||
throw new Parse.Error(
|
||||
Parse.Error.DUPLICATE_VALUE,
|
||||
'A duplicate value for a field with unique values was provided'
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Executes a count.
|
||||
@@ -2103,15 +1943,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
values.push(...where.values);
|
||||
|
||||
const wherePattern =
|
||||
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||
const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||
let qs = '';
|
||||
|
||||
if (where.pattern.length > 0 || !estimate) {
|
||||
qs = `SELECT count(*) FROM $1:name ${wherePattern}`;
|
||||
} else {
|
||||
qs =
|
||||
'SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = $1';
|
||||
qs = 'SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = $1';
|
||||
}
|
||||
|
||||
return this._client
|
||||
@@ -2130,12 +1968,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
}
|
||||
|
||||
async distinct(
|
||||
className: string,
|
||||
schema: SchemaType,
|
||||
query: QueryType,
|
||||
fieldName: string
|
||||
) {
|
||||
async distinct(className: string, schema: SchemaType, query: QueryType, fieldName: string) {
|
||||
debug('distinct', className, query);
|
||||
let field = fieldName;
|
||||
let column = fieldName;
|
||||
@@ -2145,13 +1978,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
column = fieldName.split('.')[0];
|
||||
}
|
||||
const isArrayField =
|
||||
schema.fields &&
|
||||
schema.fields[fieldName] &&
|
||||
schema.fields[fieldName].type === 'Array';
|
||||
schema.fields && schema.fields[fieldName] && schema.fields[fieldName].type === 'Array';
|
||||
const isPointerField =
|
||||
schema.fields &&
|
||||
schema.fields[fieldName] &&
|
||||
schema.fields[fieldName].type === 'Pointer';
|
||||
schema.fields && schema.fields[fieldName] && schema.fields[fieldName].type === 'Pointer';
|
||||
const values = [field, column, className];
|
||||
const where = buildWhereClause({
|
||||
schema,
|
||||
@@ -2161,8 +1990,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
values.push(...where.values);
|
||||
|
||||
const wherePattern =
|
||||
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||
const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||
const transformer = isArrayField ? 'jsonb_array_elements' : 'ON';
|
||||
let qs = `SELECT DISTINCT ${transformer}($1:name) $2:name FROM $3:name ${wherePattern}`;
|
||||
if (isNested) {
|
||||
@@ -2195,9 +2023,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
return results.map(object => object[column][child]);
|
||||
})
|
||||
.then(results =>
|
||||
results.map(object =>
|
||||
this.postgresObjectToParseObject(className, object, schema)
|
||||
)
|
||||
results.map(object => this.postgresObjectToParseObject(className, object, schema))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2235,11 +2061,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
field === '_id' &&
|
||||
typeof value === 'object' &&
|
||||
Object.keys(value).length !== 0
|
||||
) {
|
||||
if (field === '_id' && typeof value === 'object' && Object.keys(value).length !== 0) {
|
||||
groupValues = value;
|
||||
const groupByFields = [];
|
||||
for (const alias in value) {
|
||||
@@ -2261,9 +2083,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
columns.push(
|
||||
`EXTRACT(${
|
||||
mongoAggregateToPostgres[operation]
|
||||
} FROM $${index}:name AT TIME ZONE 'UTC') AS $${
|
||||
index + 1
|
||||
}:name`
|
||||
} FROM $${index}:name AT TIME ZONE 'UTC') AS $${index + 1}:name`
|
||||
);
|
||||
values.push(source, alias);
|
||||
index += 2;
|
||||
@@ -2323,10 +2143,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
if (stage.$match) {
|
||||
const patterns = [];
|
||||
const orOrAnd = Object.prototype.hasOwnProperty.call(
|
||||
stage.$match,
|
||||
'$or'
|
||||
)
|
||||
const orOrAnd = Object.prototype.hasOwnProperty.call(stage.$match, '$or')
|
||||
? ' OR '
|
||||
: ' AND ';
|
||||
|
||||
@@ -2345,9 +2162,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
Object.keys(ParseToPosgresComparator).forEach(cmp => {
|
||||
if (value[cmp]) {
|
||||
const pgComparator = ParseToPosgresComparator[cmp];
|
||||
matchPatterns.push(
|
||||
`$${index}:name ${pgComparator} $${index + 1}`
|
||||
);
|
||||
matchPatterns.push(`$${index}:name ${pgComparator} $${index + 1}`);
|
||||
values.push(field, toPostgresValue(value[cmp]));
|
||||
index += 2;
|
||||
}
|
||||
@@ -2355,18 +2170,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
if (matchPatterns.length > 0) {
|
||||
patterns.push(`(${matchPatterns.join(' AND ')})`);
|
||||
}
|
||||
if (
|
||||
schema.fields[field] &&
|
||||
schema.fields[field].type &&
|
||||
matchPatterns.length === 0
|
||||
) {
|
||||
if (schema.fields[field] && schema.fields[field].type && matchPatterns.length === 0) {
|
||||
patterns.push(`$${index}:name = $${index + 1}`);
|
||||
values.push(field, value);
|
||||
index += 2;
|
||||
}
|
||||
}
|
||||
wherePattern =
|
||||
patterns.length > 0 ? `WHERE ${patterns.join(` ${orOrAnd} `)}` : '';
|
||||
wherePattern = patterns.length > 0 ? `WHERE ${patterns.join(` ${orOrAnd} `)}` : '';
|
||||
}
|
||||
if (stage.$limit) {
|
||||
limitPattern = `LIMIT $${index}`;
|
||||
@@ -2390,8 +2200,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
})
|
||||
.join();
|
||||
values.push(...keys);
|
||||
sortPattern =
|
||||
sort !== undefined && sorting.length > 0 ? `ORDER BY ${sorting}` : '';
|
||||
sortPattern = sort !== undefined && sorting.length > 0 ? `ORDER BY ${sorting}` : '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2406,17 +2215,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
const originalQuery = `SELECT ${columns
|
||||
.filter(Boolean)
|
||||
.join()} FROM $1:name ${wherePattern} ${skipPattern} ${groupPattern} ${sortPattern} ${limitPattern}`;
|
||||
const qs = explain
|
||||
? this.createExplainableQuery(originalQuery)
|
||||
: originalQuery;
|
||||
const qs = explain ? this.createExplainableQuery(originalQuery) : originalQuery;
|
||||
debug(qs, values);
|
||||
return this._client.any(qs, values).then(a => {
|
||||
if (explain) {
|
||||
return a;
|
||||
}
|
||||
const results = a.map(object =>
|
||||
this.postgresObjectToParseObject(className, object, schema)
|
||||
);
|
||||
const results = a.map(object => this.postgresObjectToParseObject(className, object, schema));
|
||||
results.forEach(result => {
|
||||
if (!Object.prototype.hasOwnProperty.call(result, 'objectId')) {
|
||||
result.objectId = null;
|
||||
@@ -2474,11 +2279,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
}
|
||||
|
||||
async createIndexes(
|
||||
className: string,
|
||||
indexes: any,
|
||||
conn: ?any
|
||||
): Promise<void> {
|
||||
async createIndexes(className: string, indexes: any, conn: ?any): Promise<void> {
|
||||
return (conn || this._client).tx(t =>
|
||||
t.batch(
|
||||
indexes.map(i => {
|
||||
@@ -2498,9 +2299,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
type: any,
|
||||
conn: ?any
|
||||
): Promise<void> {
|
||||
await (
|
||||
conn || this._client
|
||||
).none('CREATE INDEX IF NOT EXISTS $1:name ON $2:name ($3:name)', [
|
||||
await (conn || this._client).none('CREATE INDEX IF NOT EXISTS $1:name ON $2:name ($3:name)', [
|
||||
fieldName,
|
||||
className,
|
||||
type,
|
||||
@@ -2512,9 +2311,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
query: 'DROP INDEX $1:name',
|
||||
values: i,
|
||||
}));
|
||||
await (conn || this._client).tx(t =>
|
||||
t.none(this._pgp.helpers.concat(queries))
|
||||
);
|
||||
await (conn || this._client).tx(t => t.none(this._pgp.helpers.concat(queries)));
|
||||
}
|
||||
|
||||
async getIndexes(className: string) {
|
||||
@@ -2547,18 +2344,14 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
|
||||
commitTransactionalSession(transactionalSession: any): Promise<void> {
|
||||
transactionalSession.resolve(
|
||||
transactionalSession.t.batch(transactionalSession.batch)
|
||||
);
|
||||
transactionalSession.resolve(transactionalSession.t.batch(transactionalSession.batch));
|
||||
return transactionalSession.result;
|
||||
}
|
||||
|
||||
abortTransactionalSession(transactionalSession: any): Promise<void> {
|
||||
const result = transactionalSession.result.catch();
|
||||
transactionalSession.batch.push(Promise.reject());
|
||||
transactionalSession.resolve(
|
||||
transactionalSession.t.batch(transactionalSession.batch)
|
||||
);
|
||||
transactionalSession.resolve(transactionalSession.t.batch(transactionalSession.batch));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2575,41 +2368,34 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
const indexNameOptions: Object =
|
||||
indexName != null ? { name: indexName } : { name: defaultIndexName };
|
||||
const constraintPatterns = caseInsensitive
|
||||
? fieldNames.map(
|
||||
(fieldName, index) => `lower($${index + 3}:name) varchar_pattern_ops`
|
||||
)
|
||||
? fieldNames.map((fieldName, index) => `lower($${index + 3}:name) varchar_pattern_ops`)
|
||||
: fieldNames.map((fieldName, index) => `$${index + 3}:name`);
|
||||
const qs = `CREATE INDEX IF NOT EXISTS $1:name ON $2:name (${constraintPatterns.join()})`;
|
||||
await conn
|
||||
.none(qs, [indexNameOptions.name, className, ...fieldNames])
|
||||
.catch(error => {
|
||||
if (
|
||||
error.code === PostgresDuplicateRelationError &&
|
||||
error.message.includes(indexNameOptions.name)
|
||||
) {
|
||||
// Index already exists. Ignore error.
|
||||
} else if (
|
||||
error.code === PostgresUniqueIndexViolationError &&
|
||||
error.message.includes(indexNameOptions.name)
|
||||
) {
|
||||
// Cast the error into the proper parse error
|
||||
throw new Parse.Error(
|
||||
Parse.Error.DUPLICATE_VALUE,
|
||||
'A duplicate value for a field with unique values was provided'
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
await conn.none(qs, [indexNameOptions.name, className, ...fieldNames]).catch(error => {
|
||||
if (
|
||||
error.code === PostgresDuplicateRelationError &&
|
||||
error.message.includes(indexNameOptions.name)
|
||||
) {
|
||||
// Index already exists. Ignore error.
|
||||
} else if (
|
||||
error.code === PostgresUniqueIndexViolationError &&
|
||||
error.message.includes(indexNameOptions.name)
|
||||
) {
|
||||
// Cast the error into the proper parse error
|
||||
throw new Parse.Error(
|
||||
Parse.Error.DUPLICATE_VALUE,
|
||||
'A duplicate value for a field with unique values was provided'
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function convertPolygonToSQL(polygon) {
|
||||
if (polygon.length < 3) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_JSON,
|
||||
`Polygon must have at least 3 values`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `Polygon must have at least 3 values`);
|
||||
}
|
||||
if (
|
||||
polygon[0][0] !== polygon[polygon.length - 1][0] ||
|
||||
@@ -2757,9 +2543,7 @@ function literalizeRegexPart(s: string) {
|
||||
|
||||
var GeoPointCoder = {
|
||||
isValidJSON(value) {
|
||||
return (
|
||||
typeof value === 'object' && value !== null && value.__type === 'GeoPoint'
|
||||
);
|
||||
return typeof value === 'object' && value !== null && value.__type === 'GeoPoint';
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -34,18 +34,10 @@ export interface StorageAdapter {
|
||||
classExists(className: string): Promise<boolean>;
|
||||
setClassLevelPermissions(className: string, clps: any): Promise<void>;
|
||||
createClass(className: string, schema: SchemaType): Promise<void>;
|
||||
addFieldIfNotExists(
|
||||
className: string,
|
||||
fieldName: string,
|
||||
type: any
|
||||
): Promise<void>;
|
||||
addFieldIfNotExists(className: string, fieldName: string, type: any): Promise<void>;
|
||||
deleteClass(className: string): Promise<void>;
|
||||
deleteAllClasses(fast: boolean): Promise<void>;
|
||||
deleteFields(
|
||||
className: string,
|
||||
schema: SchemaType,
|
||||
fieldNames: Array<string>
|
||||
): Promise<void>;
|
||||
deleteFields(className: string, schema: SchemaType, fieldNames: Array<string>): Promise<void>;
|
||||
getAllClasses(): Promise<StorageClass[]>;
|
||||
getClass(className: string): Promise<StorageClass>;
|
||||
createObject(
|
||||
@@ -95,11 +87,7 @@ export interface StorageAdapter {
|
||||
caseSensitive?: boolean,
|
||||
options?: Object
|
||||
): Promise<any>;
|
||||
ensureUniqueness(
|
||||
className: string,
|
||||
schema: SchemaType,
|
||||
fieldNames: Array<string>
|
||||
): Promise<void>;
|
||||
ensureUniqueness(className: string, schema: SchemaType, fieldNames: Array<string>): Promise<void>;
|
||||
count(
|
||||
className: string,
|
||||
schema: SchemaType,
|
||||
|
||||
@@ -5,40 +5,17 @@ const createObject = async (className, fields, config, auth, info) => {
|
||||
fields = {};
|
||||
}
|
||||
|
||||
return (
|
||||
await rest.create(
|
||||
config,
|
||||
auth,
|
||||
className,
|
||||
fields,
|
||||
info.clientSDK,
|
||||
info.context
|
||||
)
|
||||
).response;
|
||||
return (await rest.create(config, auth, className, fields, info.clientSDK, info.context))
|
||||
.response;
|
||||
};
|
||||
|
||||
const updateObject = async (
|
||||
className,
|
||||
objectId,
|
||||
fields,
|
||||
config,
|
||||
auth,
|
||||
info
|
||||
) => {
|
||||
const updateObject = async (className, objectId, fields, config, auth, info) => {
|
||||
if (!fields) {
|
||||
fields = {};
|
||||
}
|
||||
|
||||
return (
|
||||
await rest.update(
|
||||
config,
|
||||
auth,
|
||||
className,
|
||||
{ objectId },
|
||||
fields,
|
||||
info.clientSDK,
|
||||
info.context
|
||||
)
|
||||
await rest.update(config, auth, className, { objectId }, fields, info.clientSDK, info.context)
|
||||
).response;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ const load = parseGraphQLSchema => {
|
||||
parseGraphQLSchema.addGraphQLQuery(
|
||||
'health',
|
||||
{
|
||||
description:
|
||||
'The health query can be used to check if the server is up and running.',
|
||||
description: 'The health query can be used to check if the server is up and running.',
|
||||
type: new GraphQLNonNull(GraphQLBoolean),
|
||||
resolve: () => true,
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@ class TypeValidationError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
const parseStringValue = (value) => {
|
||||
const parseStringValue = value => {
|
||||
if (typeof value === 'string') {
|
||||
return value;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ const parseStringValue = (value) => {
|
||||
throw new TypeValidationError(value, 'String');
|
||||
};
|
||||
|
||||
const parseIntValue = (value) => {
|
||||
const parseIntValue = value => {
|
||||
if (typeof value === 'string') {
|
||||
const int = Number(value);
|
||||
if (Number.isInteger(int)) {
|
||||
@@ -42,7 +42,7 @@ const parseIntValue = (value) => {
|
||||
throw new TypeValidationError(value, 'Int');
|
||||
};
|
||||
|
||||
const parseFloatValue = (value) => {
|
||||
const parseFloatValue = value => {
|
||||
if (typeof value === 'string') {
|
||||
const float = Number(value);
|
||||
if (!isNaN(float)) {
|
||||
@@ -53,7 +53,7 @@ const parseFloatValue = (value) => {
|
||||
throw new TypeValidationError(value, 'Float');
|
||||
};
|
||||
|
||||
const parseBooleanValue = (value) => {
|
||||
const parseBooleanValue = value => {
|
||||
if (typeof value === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
@@ -61,7 +61,7 @@ const parseBooleanValue = (value) => {
|
||||
throw new TypeValidationError(value, 'Boolean');
|
||||
};
|
||||
|
||||
const parseValue = (value) => {
|
||||
const parseValue = value => {
|
||||
switch (value.kind) {
|
||||
case Kind.STRING:
|
||||
return parseStringValue(value.value);
|
||||
@@ -86,15 +86,15 @@ const parseValue = (value) => {
|
||||
}
|
||||
};
|
||||
|
||||
const parseListValues = (values) => {
|
||||
const parseListValues = values => {
|
||||
if (Array.isArray(values)) {
|
||||
return values.map((value) => parseValue(value));
|
||||
return values.map(value => parseValue(value));
|
||||
}
|
||||
|
||||
throw new TypeValidationError(values, 'List');
|
||||
};
|
||||
|
||||
const parseObjectFields = (fields) => {
|
||||
const parseObjectFields = fields => {
|
||||
if (Array.isArray(fields)) {
|
||||
return fields.reduce(
|
||||
(object, field) => ({
|
||||
@@ -112,15 +112,14 @@ const ANY = new GraphQLScalarType({
|
||||
name: 'Any',
|
||||
description:
|
||||
'The Any scalar type is used in operations and types that involve any type of value.',
|
||||
parseValue: (value) => value,
|
||||
serialize: (value) => value,
|
||||
parseLiteral: (ast) => parseValue(ast),
|
||||
parseValue: value => value,
|
||||
serialize: value => value,
|
||||
parseLiteral: ast => parseValue(ast),
|
||||
});
|
||||
|
||||
const OBJECT = new GraphQLScalarType({
|
||||
name: 'Object',
|
||||
description:
|
||||
'The Object scalar type is used in operations and types that involve objects.',
|
||||
description: 'The Object scalar type is used in operations and types that involve objects.',
|
||||
parseValue(value) {
|
||||
if (typeof value === 'object') {
|
||||
return value;
|
||||
@@ -144,7 +143,7 @@ const OBJECT = new GraphQLScalarType({
|
||||
},
|
||||
});
|
||||
|
||||
const parseDateIsoValue = (value) => {
|
||||
const parseDateIsoValue = value => {
|
||||
if (typeof value === 'string') {
|
||||
const date = new Date(value);
|
||||
if (!isNaN(date)) {
|
||||
@@ -157,7 +156,7 @@ const parseDateIsoValue = (value) => {
|
||||
throw new TypeValidationError(value, 'Date');
|
||||
};
|
||||
|
||||
const serializeDateIso = (value) => {
|
||||
const serializeDateIso = value => {
|
||||
if (typeof value === 'string') {
|
||||
return value;
|
||||
}
|
||||
@@ -168,7 +167,7 @@ const serializeDateIso = (value) => {
|
||||
throw new TypeValidationError(value, 'Date');
|
||||
};
|
||||
|
||||
const parseDateIsoLiteral = (ast) => {
|
||||
const parseDateIsoLiteral = ast => {
|
||||
if (ast.kind === Kind.STRING) {
|
||||
return parseDateIsoValue(ast.value);
|
||||
}
|
||||
@@ -178,19 +177,14 @@ const parseDateIsoLiteral = (ast) => {
|
||||
|
||||
const DATE = new GraphQLScalarType({
|
||||
name: 'Date',
|
||||
description:
|
||||
'The Date scalar type is used in operations and types that involve dates.',
|
||||
description: 'The Date scalar type is used in operations and types that involve dates.',
|
||||
parseValue(value) {
|
||||
if (typeof value === 'string' || value instanceof Date) {
|
||||
return {
|
||||
__type: 'Date',
|
||||
iso: parseDateIsoValue(value),
|
||||
};
|
||||
} else if (
|
||||
typeof value === 'object' &&
|
||||
value.__type === 'Date' &&
|
||||
value.iso
|
||||
) {
|
||||
} else if (typeof value === 'object' && value.__type === 'Date' && value.iso) {
|
||||
return {
|
||||
__type: value.__type,
|
||||
iso: parseDateIsoValue(value.iso),
|
||||
@@ -202,11 +196,7 @@ const DATE = new GraphQLScalarType({
|
||||
serialize(value) {
|
||||
if (typeof value === 'string' || value instanceof Date) {
|
||||
return serializeDateIso(value);
|
||||
} else if (
|
||||
typeof value === 'object' &&
|
||||
value.__type === 'Date' &&
|
||||
value.iso
|
||||
) {
|
||||
} else if (typeof value === 'object' && value.__type === 'Date' && value.iso) {
|
||||
return serializeDateIso(value.iso);
|
||||
}
|
||||
|
||||
@@ -219,8 +209,8 @@ const DATE = new GraphQLScalarType({
|
||||
iso: parseDateIsoLiteral(ast),
|
||||
};
|
||||
} else if (ast.kind === Kind.OBJECT) {
|
||||
const __type = ast.fields.find((field) => field.name.value === '__type');
|
||||
const iso = ast.fields.find((field) => field.name.value === 'iso');
|
||||
const __type = ast.fields.find(field => field.name.value === '__type');
|
||||
const iso = ast.fields.find(field => field.name.value === 'iso');
|
||||
if (__type && __type.value && __type.value.value === 'Date' && iso) {
|
||||
return {
|
||||
__type: __type.value.value,
|
||||
@@ -273,8 +263,8 @@ const BYTES = new GraphQLScalarType({
|
||||
base64: ast.value,
|
||||
};
|
||||
} else if (ast.kind === Kind.OBJECT) {
|
||||
const __type = ast.fields.find((field) => field.name.value === '__type');
|
||||
const base64 = ast.fields.find((field) => field.name.value === 'base64');
|
||||
const __type = ast.fields.find(field => field.name.value === '__type');
|
||||
const base64 = ast.fields.find(field => field.name.value === 'base64');
|
||||
if (
|
||||
__type &&
|
||||
__type.value &&
|
||||
@@ -294,7 +284,7 @@ const BYTES = new GraphQLScalarType({
|
||||
},
|
||||
});
|
||||
|
||||
const parseFileValue = (value) => {
|
||||
const parseFileValue = value => {
|
||||
if (typeof value === 'string') {
|
||||
return {
|
||||
__type: 'File',
|
||||
@@ -314,10 +304,9 @@ const parseFileValue = (value) => {
|
||||
|
||||
const FILE = new GraphQLScalarType({
|
||||
name: 'File',
|
||||
description:
|
||||
'The File scalar type is used in operations and types that involve files.',
|
||||
description: 'The File scalar type is used in operations and types that involve files.',
|
||||
parseValue: parseFileValue,
|
||||
serialize: (value) => {
|
||||
serialize: value => {
|
||||
if (typeof value === 'string') {
|
||||
return value;
|
||||
} else if (
|
||||
@@ -335,9 +324,9 @@ const FILE = new GraphQLScalarType({
|
||||
if (ast.kind === Kind.STRING) {
|
||||
return parseFileValue(ast.value);
|
||||
} else if (ast.kind === Kind.OBJECT) {
|
||||
const __type = ast.fields.find((field) => field.name.value === '__type');
|
||||
const name = ast.fields.find((field) => field.name.value === 'name');
|
||||
const url = ast.fields.find((field) => field.name.value === 'url');
|
||||
const __type = ast.fields.find(field => field.name.value === '__type');
|
||||
const name = ast.fields.find(field => field.name.value === 'name');
|
||||
const url = ast.fields.find(field => field.name.value === 'url');
|
||||
if (__type && __type.value && name && name.value) {
|
||||
return parseFileValue({
|
||||
__type: __type.value.value,
|
||||
@@ -353,8 +342,7 @@ const FILE = new GraphQLScalarType({
|
||||
|
||||
const FILE_INFO = new GraphQLObjectType({
|
||||
name: 'FileInfo',
|
||||
description:
|
||||
'The FileInfo object type is used to return the information about files.',
|
||||
description: 'The FileInfo object type is used to return the information about files.',
|
||||
fields: {
|
||||
name: {
|
||||
description: 'This is the file name.',
|
||||
@@ -407,8 +395,7 @@ const GEO_POINT_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const GEO_POINT = new GraphQLObjectType({
|
||||
name: 'GeoPoint',
|
||||
description:
|
||||
'The GeoPoint object type is used to return the information about geo point fields.',
|
||||
description: 'The GeoPoint object type is used to return the information about geo point fields.',
|
||||
fields: GEO_POINT_FIELDS,
|
||||
});
|
||||
|
||||
@@ -444,13 +431,11 @@ const ROLE_ACL_INPUT = new GraphQLInputObjectType({
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
},
|
||||
read: {
|
||||
description:
|
||||
'Allow users who are members of the role to read the current object.',
|
||||
description: 'Allow users who are members of the role to read the current object.',
|
||||
type: new GraphQLNonNull(GraphQLBoolean),
|
||||
},
|
||||
write: {
|
||||
description:
|
||||
'Allow users who are members of the role to write on the current object.',
|
||||
description: 'Allow users who are members of the role to write on the current object.',
|
||||
type: new GraphQLNonNull(GraphQLBoolean),
|
||||
},
|
||||
},
|
||||
@@ -521,13 +506,11 @@ const ROLE_ACL = new GraphQLObjectType({
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
},
|
||||
read: {
|
||||
description:
|
||||
'Allow users who are members of the role to read the current object.',
|
||||
description: 'Allow users who are members of the role to read the current object.',
|
||||
type: new GraphQLNonNull(GraphQLBoolean),
|
||||
},
|
||||
write: {
|
||||
description:
|
||||
'Allow users who are members of the role to write on the current object.',
|
||||
description: 'Allow users who are members of the role to write on the current object.',
|
||||
type: new GraphQLNonNull(GraphQLBoolean),
|
||||
},
|
||||
},
|
||||
@@ -557,7 +540,7 @@ const ACL = new GraphQLObjectType({
|
||||
type: new GraphQLList(new GraphQLNonNull(USER_ACL)),
|
||||
resolve(p) {
|
||||
const users = [];
|
||||
Object.keys(p).forEach((rule) => {
|
||||
Object.keys(p).forEach(rule => {
|
||||
if (rule !== '*' && rule.indexOf('role:') !== 0) {
|
||||
users.push({
|
||||
userId: toGlobalId('_User', rule),
|
||||
@@ -574,7 +557,7 @@ const ACL = new GraphQLObjectType({
|
||||
type: new GraphQLList(new GraphQLNonNull(ROLE_ACL)),
|
||||
resolve(p) {
|
||||
const roles = [];
|
||||
Object.keys(p).forEach((rule) => {
|
||||
Object.keys(p).forEach(rule => {
|
||||
if (rule.indexOf('role:') === 0) {
|
||||
roles.push({
|
||||
roleName: rule.replace('role:', ''),
|
||||
@@ -610,8 +593,7 @@ const CLASS_NAME_ATT = {
|
||||
};
|
||||
|
||||
const GLOBAL_OR_OBJECT_ID_ATT = {
|
||||
description:
|
||||
'This is the object id. You can use either the global or the object id.',
|
||||
description: 'This is the object id. You can use either the global or the object id.',
|
||||
type: OBJECT_ID,
|
||||
};
|
||||
|
||||
@@ -686,8 +668,7 @@ const READ_PREFERENCE_ATT = {
|
||||
};
|
||||
|
||||
const INCLUDE_READ_PREFERENCE_ATT = {
|
||||
description:
|
||||
'The read preference for the queries to be executed to include fields.',
|
||||
description: 'The read preference for the queries to be executed to include fields.',
|
||||
type: READ_PREFERENCE,
|
||||
};
|
||||
|
||||
@@ -713,8 +694,7 @@ const READ_OPTIONS_ATT = {
|
||||
};
|
||||
|
||||
const WHERE_ATT = {
|
||||
description:
|
||||
'These are the conditions that the objects need to match in order to be found',
|
||||
description: 'These are the conditions that the objects need to match in order to be found',
|
||||
type: OBJECT,
|
||||
};
|
||||
|
||||
@@ -736,8 +716,7 @@ const COUNT_ATT = {
|
||||
|
||||
const SEARCH_INPUT = new GraphQLInputObjectType({
|
||||
name: 'SearchInput',
|
||||
description:
|
||||
'The SearchInput type is used to specifiy a search operation on a full text search.',
|
||||
description: 'The SearchInput type is used to specifiy a search operation on a full text search.',
|
||||
fields: {
|
||||
term: {
|
||||
description: 'This is the term to be searched.',
|
||||
@@ -749,13 +728,11 @@ const SEARCH_INPUT = new GraphQLInputObjectType({
|
||||
type: GraphQLString,
|
||||
},
|
||||
caseSensitive: {
|
||||
description:
|
||||
'This is the flag to enable or disable case sensitive search.',
|
||||
description: 'This is the flag to enable or disable case sensitive search.',
|
||||
type: GraphQLBoolean,
|
||||
},
|
||||
diacriticSensitive: {
|
||||
description:
|
||||
'This is the flag to enable or disable diacritic sensitive search.',
|
||||
description: 'This is the flag to enable or disable diacritic sensitive search.',
|
||||
type: GraphQLBoolean,
|
||||
},
|
||||
},
|
||||
@@ -763,8 +740,7 @@ const SEARCH_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const TEXT_INPUT = new GraphQLInputObjectType({
|
||||
name: 'TextInput',
|
||||
description:
|
||||
'The TextInput type is used to specify a text operation on a constraint.',
|
||||
description: 'The TextInput type is used to specify a text operation on a constraint.',
|
||||
fields: {
|
||||
search: {
|
||||
description: 'This is the search to be executed.',
|
||||
@@ -775,8 +751,7 @@ const TEXT_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const BOX_INPUT = new GraphQLInputObjectType({
|
||||
name: 'BoxInput',
|
||||
description:
|
||||
'The BoxInput type is used to specifiy a box operation on a within geo query.',
|
||||
description: 'The BoxInput type is used to specifiy a box operation on a within geo query.',
|
||||
fields: {
|
||||
bottomLeft: {
|
||||
description: 'This is the bottom left coordinates of the box.',
|
||||
@@ -791,8 +766,7 @@ const BOX_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const WITHIN_INPUT = new GraphQLInputObjectType({
|
||||
name: 'WithinInput',
|
||||
description:
|
||||
'The WithinInput type is used to specify a within operation on a constraint.',
|
||||
description: 'The WithinInput type is used to specify a within operation on a constraint.',
|
||||
fields: {
|
||||
box: {
|
||||
description: 'This is the box to be specified.',
|
||||
@@ -819,8 +793,7 @@ const CENTER_SPHERE_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const GEO_WITHIN_INPUT = new GraphQLInputObjectType({
|
||||
name: 'GeoWithinInput',
|
||||
description:
|
||||
'The GeoWithinInput type is used to specify a geoWithin operation on a constraint.',
|
||||
description: 'The GeoWithinInput type is used to specify a geoWithin operation on a constraint.',
|
||||
fields: {
|
||||
polygon: {
|
||||
description: 'This is the polygon to be specified.',
|
||||
@@ -845,49 +818,49 @@ const GEO_INTERSECTS_INPUT = new GraphQLInputObjectType({
|
||||
},
|
||||
});
|
||||
|
||||
const equalTo = (type) => ({
|
||||
const equalTo = type => ({
|
||||
description:
|
||||
'This is the equalTo operator to specify a constraint to select the objects where the value of a field equals to a specified value.',
|
||||
type,
|
||||
});
|
||||
|
||||
const notEqualTo = (type) => ({
|
||||
const notEqualTo = type => ({
|
||||
description:
|
||||
'This is the notEqualTo operator to specify a constraint to select the objects where the value of a field do not equal to a specified value.',
|
||||
type,
|
||||
});
|
||||
|
||||
const lessThan = (type) => ({
|
||||
const lessThan = type => ({
|
||||
description:
|
||||
'This is the lessThan operator to specify a constraint to select the objects where the value of a field is less than a specified value.',
|
||||
type,
|
||||
});
|
||||
|
||||
const lessThanOrEqualTo = (type) => ({
|
||||
const lessThanOrEqualTo = type => ({
|
||||
description:
|
||||
'This is the lessThanOrEqualTo operator to specify a constraint to select the objects where the value of a field is less than or equal to a specified value.',
|
||||
type,
|
||||
});
|
||||
|
||||
const greaterThan = (type) => ({
|
||||
const greaterThan = type => ({
|
||||
description:
|
||||
'This is the greaterThan operator to specify a constraint to select the objects where the value of a field is greater than a specified value.',
|
||||
type,
|
||||
});
|
||||
|
||||
const greaterThanOrEqualTo = (type) => ({
|
||||
const greaterThanOrEqualTo = type => ({
|
||||
description:
|
||||
'This is the greaterThanOrEqualTo operator to specify a constraint to select the objects where the value of a field is greater than or equal to a specified value.',
|
||||
type,
|
||||
});
|
||||
|
||||
const inOp = (type) => ({
|
||||
const inOp = type => ({
|
||||
description:
|
||||
'This is the in operator to specify a constraint to select the objects where the value of a field equals any value in the specified array.',
|
||||
type: new GraphQLList(type),
|
||||
});
|
||||
|
||||
const notIn = (type) => ({
|
||||
const notIn = type => ({
|
||||
description:
|
||||
'This is the notIn operator to specify a constraint to select the objects where the value of a field do not equal any value in the specified array.',
|
||||
type: new GraphQLList(type),
|
||||
@@ -913,8 +886,7 @@ const options = {
|
||||
|
||||
const SUBQUERY_INPUT = new GraphQLInputObjectType({
|
||||
name: 'SubqueryInput',
|
||||
description:
|
||||
'The SubqueryInput type is used to specify a sub query to another class.',
|
||||
description: 'The SubqueryInput type is used to specify a sub query to another class.',
|
||||
fields: {
|
||||
className: CLASS_NAME_ATT,
|
||||
where: Object.assign({}, WHERE_ATT, {
|
||||
@@ -988,8 +960,7 @@ const STRING_WHERE_INPUT = new GraphQLInputObjectType({
|
||||
matchesRegex,
|
||||
options,
|
||||
text: {
|
||||
description:
|
||||
'This is the $text operator to specify a full text search constraint.',
|
||||
description: 'This is the $text operator to specify a full text search constraint.',
|
||||
type: TEXT_INPUT,
|
||||
},
|
||||
inQueryKey,
|
||||
@@ -1225,27 +1196,21 @@ let ARRAY_RESULT;
|
||||
|
||||
const loadArrayResult = (parseGraphQLSchema, parseClasses) => {
|
||||
const classTypes = parseClasses
|
||||
.filter((parseClass) =>
|
||||
parseGraphQLSchema.parseClassTypes[parseClass.className]
|
||||
.classGraphQLOutputType
|
||||
? true
|
||||
: false
|
||||
.filter(parseClass =>
|
||||
parseGraphQLSchema.parseClassTypes[parseClass.className].classGraphQLOutputType ? true : false
|
||||
)
|
||||
.map(
|
||||
(parseClass) =>
|
||||
parseGraphQLSchema.parseClassTypes[parseClass.className]
|
||||
.classGraphQLOutputType
|
||||
parseClass => parseGraphQLSchema.parseClassTypes[parseClass.className].classGraphQLOutputType
|
||||
);
|
||||
ARRAY_RESULT = new GraphQLUnionType({
|
||||
name: 'ArrayResult',
|
||||
description:
|
||||
'Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments',
|
||||
types: () => [ELEMENT, ...classTypes],
|
||||
resolveType: (value) => {
|
||||
resolveType: value => {
|
||||
if (value.__type === 'Object' && value.className && value.objectId) {
|
||||
if (parseGraphQLSchema.parseClassTypes[value.className]) {
|
||||
return parseGraphQLSchema.parseClassTypes[value.className]
|
||||
.classGraphQLOutputType;
|
||||
return parseGraphQLSchema.parseClassTypes[value.className].classGraphQLOutputType;
|
||||
} else {
|
||||
return ELEMENT;
|
||||
}
|
||||
@@ -1257,7 +1222,7 @@ const loadArrayResult = (parseGraphQLSchema, parseClasses) => {
|
||||
parseGraphQLSchema.graphQLTypes.push(ARRAY_RESULT);
|
||||
};
|
||||
|
||||
const load = (parseGraphQLSchema) => {
|
||||
const load = parseGraphQLSchema => {
|
||||
parseGraphQLSchema.addGraphQLType(GraphQLUpload, true);
|
||||
parseGraphQLSchema.addGraphQLType(ANY, true);
|
||||
parseGraphQLSchema.addGraphQLType(OBJECT, true);
|
||||
|
||||
@@ -39,8 +39,7 @@ const load = parseGraphQLSchema => {
|
||||
}
|
||||
},
|
||||
obj => {
|
||||
return parseGraphQLSchema.parseClassTypes[obj.className]
|
||||
.classGraphQLOutputType;
|
||||
return parseGraphQLSchema.parseClassTypes[obj.className].classGraphQLOutputType;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ const handleUpload = async (upload, config) => {
|
||||
const chunks = [];
|
||||
stream
|
||||
.on('error', reject)
|
||||
.on('data', (chunk) => chunks.push(chunk))
|
||||
.on('data', chunk => chunks.push(chunk))
|
||||
.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
});
|
||||
}
|
||||
@@ -28,35 +28,23 @@ const handleUpload = async (upload, config) => {
|
||||
}
|
||||
|
||||
if (!filename.match(/^[_a-zA-Z0-9][a-zA-Z0-9@\.\ ~_-]*$/)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_FILE_NAME,
|
||||
'Filename contains invalid characters.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename contains invalid characters.');
|
||||
}
|
||||
|
||||
try {
|
||||
return {
|
||||
fileInfo: await config.filesController.createFile(
|
||||
config,
|
||||
filename,
|
||||
data,
|
||||
mimetype
|
||||
),
|
||||
fileInfo: await config.filesController.createFile(config, filename, data, mimetype),
|
||||
};
|
||||
} catch (e) {
|
||||
logger.error('Error creating a file: ', e);
|
||||
throw new Parse.Error(
|
||||
Parse.Error.FILE_SAVE_ERROR,
|
||||
`Could not store file: ${filename}.`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `Could not store file: ${filename}.`);
|
||||
}
|
||||
};
|
||||
|
||||
const load = (parseGraphQLSchema) => {
|
||||
const load = parseGraphQLSchema => {
|
||||
const createMutation = mutationWithClientMutationId({
|
||||
name: 'CreateFile',
|
||||
description:
|
||||
'The createFile mutation can be used to create and upload a new file.',
|
||||
description: 'The createFile mutation can be used to create and upload a new file.',
|
||||
inputFields: {
|
||||
upload: {
|
||||
description: 'This is the new file to be created and uploaded.',
|
||||
@@ -80,18 +68,9 @@ const load = (parseGraphQLSchema) => {
|
||||
},
|
||||
});
|
||||
|
||||
parseGraphQLSchema.addGraphQLType(
|
||||
createMutation.args.input.type.ofType,
|
||||
true,
|
||||
true
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLType(createMutation.args.input.type.ofType, true, true);
|
||||
parseGraphQLSchema.addGraphQLType(createMutation.type, true, true);
|
||||
parseGraphQLSchema.addGraphQLMutation(
|
||||
'createFile',
|
||||
createMutation,
|
||||
true,
|
||||
true
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLMutation('createFile', createMutation, true, true);
|
||||
};
|
||||
|
||||
export { load, handleUpload };
|
||||
|
||||
@@ -24,8 +24,7 @@ const load = parseGraphQLSchema => {
|
||||
|
||||
const callCloudCodeMutation = mutationWithClientMutationId({
|
||||
name: 'CallCloudCode',
|
||||
description:
|
||||
'The callCloudCode mutation can be used to invoke a cloud code function.',
|
||||
description: 'The callCloudCode mutation can be used to invoke a cloud code function.',
|
||||
inputFields: {
|
||||
functionName: {
|
||||
description: 'This is the function to be called.',
|
||||
@@ -38,8 +37,7 @@ const load = parseGraphQLSchema => {
|
||||
},
|
||||
outputFields: {
|
||||
result: {
|
||||
description:
|
||||
'This is the result value of the cloud code function execution.',
|
||||
description: 'This is the result value of the cloud code function execution.',
|
||||
type: defaultGraphQLTypes.ANY,
|
||||
},
|
||||
},
|
||||
@@ -49,15 +47,17 @@ const load = parseGraphQLSchema => {
|
||||
const { config, auth, info } = context;
|
||||
|
||||
return {
|
||||
result: (await FunctionsRouter.handleCloudFunction({
|
||||
params: {
|
||||
functionName,
|
||||
},
|
||||
config,
|
||||
auth,
|
||||
info,
|
||||
body: params,
|
||||
})).response.result,
|
||||
result: (
|
||||
await FunctionsRouter.handleCloudFunction({
|
||||
params: {
|
||||
functionName,
|
||||
},
|
||||
config,
|
||||
auth,
|
||||
info,
|
||||
body: params,
|
||||
})
|
||||
).response.result,
|
||||
};
|
||||
} catch (e) {
|
||||
parseGraphQLSchema.handleError(e);
|
||||
@@ -65,18 +65,9 @@ const load = parseGraphQLSchema => {
|
||||
},
|
||||
});
|
||||
|
||||
parseGraphQLSchema.addGraphQLType(
|
||||
callCloudCodeMutation.args.input.type.ofType,
|
||||
true,
|
||||
true
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLType(callCloudCodeMutation.args.input.type.ofType, true, true);
|
||||
parseGraphQLSchema.addGraphQLType(callCloudCodeMutation.type, true, true);
|
||||
parseGraphQLSchema.addGraphQLMutation(
|
||||
'callCloudCode',
|
||||
callCloudCodeMutation,
|
||||
true,
|
||||
true
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLMutation('callCloudCode', callCloudCodeMutation, true, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,10 +2,7 @@ import { GraphQLNonNull } from 'graphql';
|
||||
import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay';
|
||||
import getFieldNames from 'graphql-list-fields';
|
||||
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
|
||||
import {
|
||||
extractKeysAndInclude,
|
||||
getParseClassMutationConfig,
|
||||
} from '../parseGraphQLUtils';
|
||||
import { extractKeysAndInclude, getParseClassMutationConfig } from '../parseGraphQLUtils';
|
||||
import * as objectsMutations from '../helpers/objectsMutations';
|
||||
import * as objectsQueries from '../helpers/objectsQueries';
|
||||
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
|
||||
@@ -18,17 +15,10 @@ const getOnlyRequiredFields = (
|
||||
includedFieldsString,
|
||||
nativeObjectFields
|
||||
) => {
|
||||
const includedFields = includedFieldsString
|
||||
? includedFieldsString.split(',')
|
||||
: [];
|
||||
const selectedFields = selectedFieldsString
|
||||
? selectedFieldsString.split(',')
|
||||
: [];
|
||||
const includedFields = includedFieldsString ? includedFieldsString.split(',') : [];
|
||||
const selectedFields = selectedFieldsString ? selectedFieldsString.split(',') : [];
|
||||
const missingFields = selectedFields
|
||||
.filter(
|
||||
field =>
|
||||
!nativeObjectFields.includes(field) || includedFields.includes(field)
|
||||
)
|
||||
.filter(field => !nativeObjectFields.includes(field) || includedFields.includes(field))
|
||||
.join(',');
|
||||
if (!missingFields.length) {
|
||||
return { needGet: false, keys: '' };
|
||||
@@ -37,15 +27,10 @@ const getOnlyRequiredFields = (
|
||||
}
|
||||
};
|
||||
|
||||
const load = function(
|
||||
parseGraphQLSchema,
|
||||
parseClass,
|
||||
parseClassConfig: ?ParseGraphQLClassConfig
|
||||
) {
|
||||
const load = function (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseGraphQLClassConfig) {
|
||||
const className = parseClass.className;
|
||||
const graphQLClassName = transformClassNameToGraphQL(className);
|
||||
const getGraphQLQueryName =
|
||||
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
||||
const getGraphQLQueryName = graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
||||
|
||||
const {
|
||||
create: isCreateEnabled = true,
|
||||
@@ -63,24 +48,20 @@ const load = function(
|
||||
} = parseGraphQLSchema.parseClassTypes[className];
|
||||
|
||||
if (isCreateEnabled) {
|
||||
const createGraphQLMutationName =
|
||||
createAlias || `create${graphQLClassName}`;
|
||||
const createGraphQLMutationName = createAlias || `create${graphQLClassName}`;
|
||||
const createGraphQLMutation = mutationWithClientMutationId({
|
||||
name: `Create${graphQLClassName}`,
|
||||
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${graphQLClassName} class.`,
|
||||
inputFields: {
|
||||
fields: {
|
||||
description:
|
||||
'These are the fields that will be used to create the new object.',
|
||||
description: 'These are the fields that will be used to create the new object.',
|
||||
type: classGraphQLCreateType || defaultGraphQLTypes.OBJECT,
|
||||
},
|
||||
},
|
||||
outputFields: {
|
||||
[getGraphQLQueryName]: {
|
||||
description: 'This is the created object.',
|
||||
type: new GraphQLNonNull(
|
||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
||||
),
|
||||
type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
|
||||
},
|
||||
},
|
||||
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
||||
@@ -106,12 +87,12 @@ const load = function(
|
||||
.filter(field => field.startsWith(`${getGraphQLQueryName}.`))
|
||||
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
||||
const { keys, include } = extractKeysAndInclude(selectedFields);
|
||||
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(
|
||||
fields,
|
||||
keys,
|
||||
include,
|
||||
['id', 'objectId', 'createdAt', 'updatedAt']
|
||||
);
|
||||
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(fields, keys, include, [
|
||||
'id',
|
||||
'objectId',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
]);
|
||||
const needToGetAllKeys = objectsQueries.needToGetAllKeys(
|
||||
parseClass.fields,
|
||||
keys,
|
||||
@@ -160,38 +141,29 @@ const load = function(
|
||||
});
|
||||
|
||||
if (
|
||||
parseGraphQLSchema.addGraphQLType(
|
||||
createGraphQLMutation.args.input.type.ofType
|
||||
) &&
|
||||
parseGraphQLSchema.addGraphQLType(createGraphQLMutation.args.input.type.ofType) &&
|
||||
parseGraphQLSchema.addGraphQLType(createGraphQLMutation.type)
|
||||
) {
|
||||
parseGraphQLSchema.addGraphQLMutation(
|
||||
createGraphQLMutationName,
|
||||
createGraphQLMutation
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLMutation(createGraphQLMutationName, createGraphQLMutation);
|
||||
}
|
||||
}
|
||||
|
||||
if (isUpdateEnabled) {
|
||||
const updateGraphQLMutationName =
|
||||
updateAlias || `update${graphQLClassName}`;
|
||||
const updateGraphQLMutationName = updateAlias || `update${graphQLClassName}`;
|
||||
const updateGraphQLMutation = mutationWithClientMutationId({
|
||||
name: `Update${graphQLClassName}`,
|
||||
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`,
|
||||
inputFields: {
|
||||
id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT,
|
||||
fields: {
|
||||
description:
|
||||
'These are the fields that will be used to update the object.',
|
||||
description: 'These are the fields that will be used to update the object.',
|
||||
type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT,
|
||||
},
|
||||
},
|
||||
outputFields: {
|
||||
[getGraphQLQueryName]: {
|
||||
description: 'This is the updated object.',
|
||||
type: new GraphQLNonNull(
|
||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
||||
),
|
||||
type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
|
||||
},
|
||||
},
|
||||
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
||||
@@ -225,12 +197,11 @@ const load = function(
|
||||
.filter(field => field.startsWith(`${getGraphQLQueryName}.`))
|
||||
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
||||
const { keys, include } = extractKeysAndInclude(selectedFields);
|
||||
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(
|
||||
fields,
|
||||
keys,
|
||||
include,
|
||||
['id', 'objectId', 'updatedAt']
|
||||
);
|
||||
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(fields, keys, include, [
|
||||
'id',
|
||||
'objectId',
|
||||
'updatedAt',
|
||||
]);
|
||||
const needToGetAllKeys = objectsQueries.needToGetAllKeys(
|
||||
parseClass.fields,
|
||||
keys,
|
||||
@@ -279,21 +250,15 @@ const load = function(
|
||||
});
|
||||
|
||||
if (
|
||||
parseGraphQLSchema.addGraphQLType(
|
||||
updateGraphQLMutation.args.input.type.ofType
|
||||
) &&
|
||||
parseGraphQLSchema.addGraphQLType(updateGraphQLMutation.args.input.type.ofType) &&
|
||||
parseGraphQLSchema.addGraphQLType(updateGraphQLMutation.type)
|
||||
) {
|
||||
parseGraphQLSchema.addGraphQLMutation(
|
||||
updateGraphQLMutationName,
|
||||
updateGraphQLMutation
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLMutation(updateGraphQLMutationName, updateGraphQLMutation);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDestroyEnabled) {
|
||||
const deleteGraphQLMutationName =
|
||||
destroyAlias || `delete${graphQLClassName}`;
|
||||
const deleteGraphQLMutationName = destroyAlias || `delete${graphQLClassName}`;
|
||||
const deleteGraphQLMutation = mutationWithClientMutationId({
|
||||
name: `Delete${graphQLClassName}`,
|
||||
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`,
|
||||
@@ -303,9 +268,7 @@ const load = function(
|
||||
outputFields: {
|
||||
[getGraphQLQueryName]: {
|
||||
description: 'This is the deleted object.',
|
||||
type: new GraphQLNonNull(
|
||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
||||
),
|
||||
type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
|
||||
},
|
||||
},
|
||||
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
||||
@@ -324,11 +287,7 @@ const load = function(
|
||||
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
||||
const { keys, include } = extractKeysAndInclude(selectedFields);
|
||||
let optimizedObject = {};
|
||||
if (
|
||||
keys &&
|
||||
keys.split(',').filter(key => !['id', 'objectId'].includes(key))
|
||||
.length > 0
|
||||
) {
|
||||
if (keys && keys.split(',').filter(key => !['id', 'objectId'].includes(key)).length > 0) {
|
||||
optimizedObject = await objectsQueries.getObject(
|
||||
className,
|
||||
id,
|
||||
@@ -342,13 +301,7 @@ const load = function(
|
||||
parseGraphQLSchema.parseClasses
|
||||
);
|
||||
}
|
||||
await objectsMutations.deleteObject(
|
||||
className,
|
||||
id,
|
||||
config,
|
||||
auth,
|
||||
info
|
||||
);
|
||||
await objectsMutations.deleteObject(className, id, config, auth, info);
|
||||
return {
|
||||
[getGraphQLQueryName]: {
|
||||
objectId: id,
|
||||
@@ -362,15 +315,10 @@ const load = function(
|
||||
});
|
||||
|
||||
if (
|
||||
parseGraphQLSchema.addGraphQLType(
|
||||
deleteGraphQLMutation.args.input.type.ofType
|
||||
) &&
|
||||
parseGraphQLSchema.addGraphQLType(deleteGraphQLMutation.args.input.type.ofType) &&
|
||||
parseGraphQLSchema.addGraphQLType(deleteGraphQLMutation.type)
|
||||
) {
|
||||
parseGraphQLSchema.addGraphQLMutation(
|
||||
deleteGraphQLMutationName,
|
||||
deleteGraphQLMutation
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLMutation(deleteGraphQLMutationName, deleteGraphQLMutation);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,20 +8,11 @@ import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLControlle
|
||||
import { transformClassNameToGraphQL } from '../transformers/className';
|
||||
import { extractKeysAndInclude } from '../parseGraphQLUtils';
|
||||
|
||||
const getParseClassQueryConfig = function (
|
||||
parseClassConfig: ?ParseGraphQLClassConfig
|
||||
) {
|
||||
const getParseClassQueryConfig = function (parseClassConfig: ?ParseGraphQLClassConfig) {
|
||||
return (parseClassConfig && parseClassConfig.query) || {};
|
||||
};
|
||||
|
||||
const getQuery = async (
|
||||
parseClass,
|
||||
_source,
|
||||
args,
|
||||
context,
|
||||
queryInfo,
|
||||
parseClasses
|
||||
) => {
|
||||
const getQuery = async (parseClass, _source, args, context, queryInfo, parseClasses) => {
|
||||
let { id } = args;
|
||||
const { options } = args;
|
||||
const { readPreference, includeReadPreference } = options || {};
|
||||
@@ -50,11 +41,7 @@ const getQuery = async (
|
||||
);
|
||||
};
|
||||
|
||||
const load = function (
|
||||
parseGraphQLSchema,
|
||||
parseClass,
|
||||
parseClassConfig: ?ParseGraphQLClassConfig
|
||||
) {
|
||||
const load = function (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseGraphQLClassConfig) {
|
||||
const className = parseClass.className;
|
||||
const graphQLClassName = transformClassNameToGraphQL(className);
|
||||
const {
|
||||
@@ -71,8 +58,7 @@ const load = function (
|
||||
} = parseGraphQLSchema.parseClassTypes[className];
|
||||
|
||||
if (isGetEnabled) {
|
||||
const lowerCaseClassName =
|
||||
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
||||
const lowerCaseClassName = graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
||||
|
||||
const getGraphQLQueryName = getAlias || lowerCaseClassName;
|
||||
|
||||
@@ -82,9 +68,7 @@ const load = function (
|
||||
id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT,
|
||||
options: defaultGraphQLTypes.READ_OPTIONS_ATT,
|
||||
},
|
||||
type: new GraphQLNonNull(
|
||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
||||
),
|
||||
type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
|
||||
async resolve(_source, args, context, queryInfo) {
|
||||
try {
|
||||
return await getQuery(
|
||||
@@ -103,41 +87,25 @@ const load = function (
|
||||
}
|
||||
|
||||
if (isFindEnabled) {
|
||||
const lowerCaseClassName =
|
||||
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
||||
const lowerCaseClassName = graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
||||
|
||||
const findGraphQLQueryName = findAlias || pluralize(lowerCaseClassName);
|
||||
|
||||
parseGraphQLSchema.addGraphQLQuery(findGraphQLQueryName, {
|
||||
description: `The ${findGraphQLQueryName} query can be used to find objects of the ${graphQLClassName} class.`,
|
||||
args: classGraphQLFindArgs,
|
||||
type: new GraphQLNonNull(
|
||||
classGraphQLFindResultType || defaultGraphQLTypes.OBJECT
|
||||
),
|
||||
type: new GraphQLNonNull(classGraphQLFindResultType || defaultGraphQLTypes.OBJECT),
|
||||
async resolve(_source, args, context, queryInfo) {
|
||||
try {
|
||||
const {
|
||||
where,
|
||||
order,
|
||||
skip,
|
||||
first,
|
||||
after,
|
||||
last,
|
||||
before,
|
||||
options,
|
||||
} = args;
|
||||
const {
|
||||
readPreference,
|
||||
includeReadPreference,
|
||||
subqueryReadPreference,
|
||||
} = options || {};
|
||||
const { where, order, skip, first, after, last, before, options } = args;
|
||||
const { readPreference, includeReadPreference, subqueryReadPreference } = options || {};
|
||||
const { config, auth, info } = context;
|
||||
const selectedFields = getFieldNames(queryInfo);
|
||||
|
||||
const { keys, include } = extractKeysAndInclude(
|
||||
selectedFields
|
||||
.filter((field) => field.startsWith('edges.node.'))
|
||||
.map((field) => field.replace('edges.node.', ''))
|
||||
.filter(field => field.startsWith('edges.node.'))
|
||||
.map(field => field.replace('edges.node.', ''))
|
||||
);
|
||||
const parseOrder = order && order.join(',');
|
||||
|
||||
|
||||
@@ -8,11 +8,7 @@ import {
|
||||
GraphQLBoolean,
|
||||
GraphQLEnumType,
|
||||
} from 'graphql';
|
||||
import {
|
||||
globalIdField,
|
||||
connectionArgs,
|
||||
connectionDefinitions,
|
||||
} from 'graphql-relay';
|
||||
import { globalIdField, connectionArgs, connectionDefinitions } from 'graphql-relay';
|
||||
import getFieldNames from 'graphql-list-fields';
|
||||
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
|
||||
import * as objectsQueries from '../helpers/objectsQueries';
|
||||
@@ -21,14 +17,9 @@ import { transformClassNameToGraphQL } from '../transformers/className';
|
||||
import { transformInputTypeToGraphQL } from '../transformers/inputType';
|
||||
import { transformOutputTypeToGraphQL } from '../transformers/outputType';
|
||||
import { transformConstraintTypeToGraphQL } from '../transformers/constraintType';
|
||||
import {
|
||||
extractKeysAndInclude,
|
||||
getParseClassMutationConfig,
|
||||
} from '../parseGraphQLUtils';
|
||||
import { extractKeysAndInclude, getParseClassMutationConfig } from '../parseGraphQLUtils';
|
||||
|
||||
const getParseClassTypeConfig = function (
|
||||
parseClassConfig: ?ParseGraphQLClassConfig
|
||||
) {
|
||||
const getParseClassTypeConfig = function (parseClassConfig: ?ParseGraphQLClassConfig) {
|
||||
return (parseClassConfig && parseClassConfig.type) || {};
|
||||
};
|
||||
|
||||
@@ -51,22 +42,19 @@ const getInputFieldsAndConstraints = function (
|
||||
let classSortFields;
|
||||
|
||||
// All allowed customs fields
|
||||
const classCustomFields = classFields.filter((field) => {
|
||||
return (
|
||||
!Object.keys(defaultGraphQLTypes.PARSE_OBJECT_FIELDS).includes(field) &&
|
||||
field !== 'id'
|
||||
);
|
||||
const classCustomFields = classFields.filter(field => {
|
||||
return !Object.keys(defaultGraphQLTypes.PARSE_OBJECT_FIELDS).includes(field) && field !== 'id';
|
||||
});
|
||||
|
||||
if (allowedInputFields && allowedInputFields.create) {
|
||||
classCreateFields = classCustomFields.filter((field) => {
|
||||
classCreateFields = classCustomFields.filter(field => {
|
||||
return allowedInputFields.create.includes(field);
|
||||
});
|
||||
} else {
|
||||
classCreateFields = classCustomFields;
|
||||
}
|
||||
if (allowedInputFields && allowedInputFields.update) {
|
||||
classUpdateFields = classCustomFields.filter((field) => {
|
||||
classUpdateFields = classCustomFields.filter(field => {
|
||||
return allowedInputFields.update.includes(field);
|
||||
});
|
||||
} else {
|
||||
@@ -74,7 +62,7 @@ const getInputFieldsAndConstraints = function (
|
||||
}
|
||||
|
||||
if (allowedOutputFields) {
|
||||
classOutputFields = classCustomFields.filter((field) => {
|
||||
classOutputFields = classCustomFields.filter(field => {
|
||||
return allowedOutputFields.includes(field);
|
||||
});
|
||||
} else {
|
||||
@@ -82,13 +70,11 @@ const getInputFieldsAndConstraints = function (
|
||||
}
|
||||
// Filters the "password" field from class _User
|
||||
if (parseClass.className === '_User') {
|
||||
classOutputFields = classOutputFields.filter(
|
||||
(outputField) => outputField !== 'password'
|
||||
);
|
||||
classOutputFields = classOutputFields.filter(outputField => outputField !== 'password');
|
||||
}
|
||||
|
||||
if (allowedConstraintFields) {
|
||||
classConstraintFields = classCustomFields.filter((field) => {
|
||||
classConstraintFields = classCustomFields.filter(field => {
|
||||
return allowedConstraintFields.includes(field);
|
||||
});
|
||||
} else {
|
||||
@@ -107,7 +93,7 @@ const getInputFieldsAndConstraints = function (
|
||||
});
|
||||
}
|
||||
} else {
|
||||
classSortFields = classFields.map((field) => {
|
||||
classSortFields = classFields.map(field => {
|
||||
return { field, asc: true, desc: true };
|
||||
});
|
||||
}
|
||||
@@ -121,11 +107,7 @@ const getInputFieldsAndConstraints = function (
|
||||
};
|
||||
};
|
||||
|
||||
const load = (
|
||||
parseGraphQLSchema,
|
||||
parseClass,
|
||||
parseClassConfig: ?ParseGraphQLClassConfig
|
||||
) => {
|
||||
const load = (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseGraphQLClassConfig) => {
|
||||
const className = parseClass.className;
|
||||
const graphQLClassName = transformClassNameToGraphQL(className);
|
||||
const {
|
||||
@@ -159,8 +141,7 @@ const load = (
|
||||
[field]: {
|
||||
description: `This is the object ${field}.`,
|
||||
type:
|
||||
(className === '_User' &&
|
||||
(field === 'username' || field === 'password')) ||
|
||||
(className === '_User' && (field === 'username' || field === 'password')) ||
|
||||
parseClass.fields[field].required
|
||||
? new GraphQLNonNull(type)
|
||||
: type,
|
||||
@@ -175,9 +156,7 @@ const load = (
|
||||
}
|
||||
),
|
||||
});
|
||||
classGraphQLCreateType = parseGraphQLSchema.addGraphQLType(
|
||||
classGraphQLCreateType
|
||||
);
|
||||
classGraphQLCreateType = parseGraphQLSchema.addGraphQLType(classGraphQLCreateType);
|
||||
|
||||
const classGraphQLUpdateTypeName = `Update${graphQLClassName}FieldsInput`;
|
||||
let classGraphQLUpdateType = new GraphQLInputObjectType({
|
||||
@@ -208,9 +187,7 @@ const load = (
|
||||
}
|
||||
),
|
||||
});
|
||||
classGraphQLUpdateType = parseGraphQLSchema.addGraphQLType(
|
||||
classGraphQLUpdateType
|
||||
);
|
||||
classGraphQLUpdateType = parseGraphQLSchema.addGraphQLType(classGraphQLUpdateType);
|
||||
|
||||
const classGraphQLPointerTypeName = `${graphQLClassName}PointerInput`;
|
||||
let classGraphQLPointerType = new GraphQLInputObjectType({
|
||||
@@ -233,8 +210,7 @@ const load = (
|
||||
},
|
||||
});
|
||||
classGraphQLPointerType =
|
||||
parseGraphQLSchema.addGraphQLType(classGraphQLPointerType) ||
|
||||
defaultGraphQLTypes.OBJECT;
|
||||
parseGraphQLSchema.addGraphQLType(classGraphQLPointerType) || defaultGraphQLTypes.OBJECT;
|
||||
|
||||
const classGraphQLRelationTypeName = `${graphQLClassName}RelationInput`;
|
||||
let classGraphQLRelationType = new GraphQLInputObjectType({
|
||||
@@ -261,8 +237,7 @@ const load = (
|
||||
},
|
||||
});
|
||||
classGraphQLRelationType =
|
||||
parseGraphQLSchema.addGraphQLType(classGraphQLRelationType) ||
|
||||
defaultGraphQLTypes.OBJECT;
|
||||
parseGraphQLSchema.addGraphQLType(classGraphQLRelationType) || defaultGraphQLTypes.OBJECT;
|
||||
|
||||
const classGraphQLConstraintsTypeName = `${graphQLClassName}WhereInput`;
|
||||
let classGraphQLConstraintsType = new GraphQLInputObjectType({
|
||||
@@ -310,8 +285,7 @@ const load = (
|
||||
}),
|
||||
});
|
||||
classGraphQLConstraintsType =
|
||||
parseGraphQLSchema.addGraphQLType(classGraphQLConstraintsType) ||
|
||||
defaultGraphQLTypes.OBJECT;
|
||||
parseGraphQLSchema.addGraphQLType(classGraphQLConstraintsType) || defaultGraphQLTypes.OBJECT;
|
||||
|
||||
const classGraphQLRelationConstraintsTypeName = `${graphQLClassName}RelationWhereInput`;
|
||||
let classGraphQLRelationConstraintsType = new GraphQLInputObjectType({
|
||||
@@ -319,8 +293,7 @@ const load = (
|
||||
description: `The ${classGraphQLRelationConstraintsTypeName} input type is used in operations that involve filtering objects of ${graphQLClassName} class.`,
|
||||
fields: () => ({
|
||||
have: {
|
||||
description:
|
||||
'Run a relational/pointer query where at least one child object can match.',
|
||||
description: 'Run a relational/pointer query where at least one child object can match.',
|
||||
type: classGraphQLConstraintsType,
|
||||
},
|
||||
haveNot: {
|
||||
@@ -357,14 +330,11 @@ const load = (
|
||||
return updatedSortFields;
|
||||
}, {}),
|
||||
});
|
||||
classGraphQLOrderType = parseGraphQLSchema.addGraphQLType(
|
||||
classGraphQLOrderType
|
||||
);
|
||||
classGraphQLOrderType = parseGraphQLSchema.addGraphQLType(classGraphQLOrderType);
|
||||
|
||||
const classGraphQLFindArgs = {
|
||||
where: {
|
||||
description:
|
||||
'These are the conditions that the objects need to match in order to be found.',
|
||||
description: 'These are the conditions that the objects need to match in order to be found.',
|
||||
type: classGraphQLConstraintsType,
|
||||
},
|
||||
order: {
|
||||
@@ -378,12 +348,9 @@ const load = (
|
||||
options: defaultGraphQLTypes.READ_OPTIONS_ATT,
|
||||
};
|
||||
const classGraphQLOutputTypeName = `${graphQLClassName}`;
|
||||
const interfaces = [
|
||||
defaultGraphQLTypes.PARSE_OBJECT,
|
||||
parseGraphQLSchema.relayNodeInterface,
|
||||
];
|
||||
const interfaces = [defaultGraphQLTypes.PARSE_OBJECT, parseGraphQLSchema.relayNodeInterface];
|
||||
const parseObjectFields = {
|
||||
id: globalIdField(className, (obj) => obj.objectId),
|
||||
id: globalIdField(className, obj => obj.objectId),
|
||||
...defaultGraphQLTypes.PARSE_OBJECT_FIELDS,
|
||||
};
|
||||
const outputFields = () => {
|
||||
@@ -395,44 +362,26 @@ const load = (
|
||||
);
|
||||
if (parseClass.fields[field].type === 'Relation') {
|
||||
const targetParseClassTypes =
|
||||
parseGraphQLSchema.parseClassTypes[
|
||||
parseClass.fields[field].targetClass
|
||||
];
|
||||
const args = targetParseClassTypes
|
||||
? targetParseClassTypes.classGraphQLFindArgs
|
||||
: undefined;
|
||||
parseGraphQLSchema.parseClassTypes[parseClass.fields[field].targetClass];
|
||||
const args = targetParseClassTypes ? targetParseClassTypes.classGraphQLFindArgs : undefined;
|
||||
return {
|
||||
...fields,
|
||||
[field]: {
|
||||
description: `This is the object ${field}.`,
|
||||
args,
|
||||
type: parseClass.fields[field].required
|
||||
? new GraphQLNonNull(type)
|
||||
: type,
|
||||
type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
|
||||
async resolve(source, args, context, queryInfo) {
|
||||
try {
|
||||
const {
|
||||
where,
|
||||
order,
|
||||
skip,
|
||||
first,
|
||||
after,
|
||||
last,
|
||||
before,
|
||||
options,
|
||||
} = args;
|
||||
const {
|
||||
readPreference,
|
||||
includeReadPreference,
|
||||
subqueryReadPreference,
|
||||
} = options || {};
|
||||
const { where, order, skip, first, after, last, before, options } = args;
|
||||
const { readPreference, includeReadPreference, subqueryReadPreference } =
|
||||
options || {};
|
||||
const { config, auth, info } = context;
|
||||
const selectedFields = getFieldNames(queryInfo);
|
||||
|
||||
const { keys, include } = extractKeysAndInclude(
|
||||
selectedFields
|
||||
.filter((field) => field.startsWith('edges.node.'))
|
||||
.map((field) => field.replace('edges.node.', ''))
|
||||
.filter(field => field.startsWith('edges.node.'))
|
||||
.map(field => field.replace('edges.node.', ''))
|
||||
);
|
||||
const parseOrder = order && order.join(',');
|
||||
|
||||
@@ -478,12 +427,10 @@ const load = (
|
||||
...fields,
|
||||
[field]: {
|
||||
description: `This is the object ${field}.`,
|
||||
type: parseClass.fields[field].required
|
||||
? new GraphQLNonNull(type)
|
||||
: type,
|
||||
type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
|
||||
async resolve(source) {
|
||||
if (source[field] && source[field].coordinates) {
|
||||
return source[field].coordinates.map((coordinate) => ({
|
||||
return source[field].coordinates.map(coordinate => ({
|
||||
latitude: coordinate[0],
|
||||
longitude: coordinate[1],
|
||||
}));
|
||||
@@ -498,17 +445,11 @@ const load = (
|
||||
...fields,
|
||||
[field]: {
|
||||
description: `Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments`,
|
||||
type: parseClass.fields[field].required
|
||||
? new GraphQLNonNull(type)
|
||||
: type,
|
||||
type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
|
||||
async resolve(source) {
|
||||
if (!source[field]) return null;
|
||||
return source[field].map(async (elem) => {
|
||||
if (
|
||||
elem.className &&
|
||||
elem.objectId &&
|
||||
elem.__type === 'Object'
|
||||
) {
|
||||
return source[field].map(async elem => {
|
||||
if (elem.className && elem.objectId && elem.__type === 'Object') {
|
||||
return elem;
|
||||
} else {
|
||||
return { value: elem };
|
||||
@@ -522,9 +463,7 @@ const load = (
|
||||
...fields,
|
||||
[field]: {
|
||||
description: `This is the object ${field}.`,
|
||||
type: parseClass.fields[field].required
|
||||
? new GraphQLNonNull(type)
|
||||
: type,
|
||||
type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
@@ -538,9 +477,7 @@ const load = (
|
||||
interfaces,
|
||||
fields: outputFields,
|
||||
});
|
||||
classGraphQLOutputType = parseGraphQLSchema.addGraphQLType(
|
||||
classGraphQLOutputType
|
||||
);
|
||||
classGraphQLOutputType = parseGraphQLSchema.addGraphQLType(classGraphQLOutputType);
|
||||
|
||||
const { connectionType, edgeType } = connectionDefinitions({
|
||||
name: graphQLClassName,
|
||||
|
||||
@@ -21,15 +21,17 @@ const load = parseGraphQLSchema => {
|
||||
functionName = this.args.to;
|
||||
}
|
||||
|
||||
return (await FunctionsRouter.handleCloudFunction({
|
||||
params: {
|
||||
functionName,
|
||||
},
|
||||
config,
|
||||
auth,
|
||||
info,
|
||||
body: args,
|
||||
})).response.result;
|
||||
return (
|
||||
await FunctionsRouter.handleCloudFunction({
|
||||
params: {
|
||||
functionName,
|
||||
},
|
||||
config,
|
||||
auth,
|
||||
info,
|
||||
body: args,
|
||||
})
|
||||
).response.result;
|
||||
} catch (e) {
|
||||
parseGraphQLSchema.handleError(e);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@ import Parse from 'parse/node';
|
||||
import { GraphQLNonNull } from 'graphql';
|
||||
import { mutationWithClientMutationId } from 'graphql-relay';
|
||||
import * as schemaTypes from './schemaTypes';
|
||||
import {
|
||||
transformToParse,
|
||||
transformToGraphQL,
|
||||
} from '../transformers/schemaFields';
|
||||
import { transformToParse, transformToGraphQL } from '../transformers/schemaFields';
|
||||
import { enforceMasterKeyAccess } from '../parseGraphQLUtils';
|
||||
import { getClass } from './schemaQueries';
|
||||
|
||||
@@ -42,10 +39,7 @@ const load = parseGraphQLSchema => {
|
||||
}
|
||||
|
||||
const schema = await config.database.loadSchema({ clearCache: true });
|
||||
const parseClass = await schema.addClassIfNotExists(
|
||||
name,
|
||||
transformToParse(schemaFields)
|
||||
);
|
||||
const parseClass = await schema.addClassIfNotExists(name, transformToParse(schemaFields));
|
||||
return {
|
||||
class: {
|
||||
name: parseClass.className,
|
||||
@@ -58,18 +52,9 @@ const load = parseGraphQLSchema => {
|
||||
},
|
||||
});
|
||||
|
||||
parseGraphQLSchema.addGraphQLType(
|
||||
createClassMutation.args.input.type.ofType,
|
||||
true,
|
||||
true
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLType(createClassMutation.args.input.type.ofType, true, true);
|
||||
parseGraphQLSchema.addGraphQLType(createClassMutation.type, true, true);
|
||||
parseGraphQLSchema.addGraphQLMutation(
|
||||
'createClass',
|
||||
createClassMutation,
|
||||
true,
|
||||
true
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLMutation('createClass', createClassMutation, true, true);
|
||||
|
||||
const updateClassMutation = mutationWithClientMutationId({
|
||||
name: 'UpdateClass',
|
||||
@@ -123,23 +108,13 @@ const load = parseGraphQLSchema => {
|
||||
},
|
||||
});
|
||||
|
||||
parseGraphQLSchema.addGraphQLType(
|
||||
updateClassMutation.args.input.type.ofType,
|
||||
true,
|
||||
true
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLType(updateClassMutation.args.input.type.ofType, true, true);
|
||||
parseGraphQLSchema.addGraphQLType(updateClassMutation.type, true, true);
|
||||
parseGraphQLSchema.addGraphQLMutation(
|
||||
'updateClass',
|
||||
updateClassMutation,
|
||||
true,
|
||||
true
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLMutation('updateClass', updateClassMutation, true, true);
|
||||
|
||||
const deleteClassMutation = mutationWithClientMutationId({
|
||||
name: 'DeleteClass',
|
||||
description:
|
||||
'The deleteClass mutation can be used to delete an existing object class.',
|
||||
description: 'The deleteClass mutation can be used to delete an existing object class.',
|
||||
inputFields: {
|
||||
name: schemaTypes.CLASS_NAME_ATT,
|
||||
},
|
||||
@@ -178,18 +153,9 @@ const load = parseGraphQLSchema => {
|
||||
},
|
||||
});
|
||||
|
||||
parseGraphQLSchema.addGraphQLType(
|
||||
deleteClassMutation.args.input.type.ofType,
|
||||
true,
|
||||
true
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLType(deleteClassMutation.args.input.type.ofType, true, true);
|
||||
parseGraphQLSchema.addGraphQLType(deleteClassMutation.type, true, true);
|
||||
parseGraphQLSchema.addGraphQLMutation(
|
||||
'deleteClass',
|
||||
deleteClassMutation,
|
||||
true,
|
||||
true
|
||||
);
|
||||
parseGraphQLSchema.addGraphQLMutation('deleteClass', deleteClassMutation, true, true);
|
||||
};
|
||||
|
||||
export { load };
|
||||
|
||||
@@ -9,15 +9,9 @@ const getClass = async (name, schema) => {
|
||||
return await schema.getOneSchema(name, true);
|
||||
} catch (e) {
|
||||
if (e === undefined) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_CLASS_NAME,
|
||||
`Class ${name} does not exist.`
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${name} does not exist.`);
|
||||
} else {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
||||
'Database adapter error.'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error.');
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -26,8 +20,7 @@ const load = parseGraphQLSchema => {
|
||||
parseGraphQLSchema.addGraphQLQuery(
|
||||
'class',
|
||||
{
|
||||
description:
|
||||
'The class query can be used to retrieve an existing object class.',
|
||||
description: 'The class query can be used to retrieve an existing object class.',
|
||||
args: {
|
||||
name: schemaTypes.CLASS_NAME_ATT,
|
||||
},
|
||||
@@ -57,11 +50,8 @@ const load = parseGraphQLSchema => {
|
||||
parseGraphQLSchema.addGraphQLQuery(
|
||||
'classes',
|
||||
{
|
||||
description:
|
||||
'The classes query can be used to retrieve the existing object classes.',
|
||||
type: new GraphQLNonNull(
|
||||
new GraphQLList(new GraphQLNonNull(schemaTypes.CLASS))
|
||||
),
|
||||
description: 'The classes query can be used to retrieve the existing object classes.',
|
||||
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(schemaTypes.CLASS))),
|
||||
resolve: async (_source, _args, context) => {
|
||||
try {
|
||||
const { config, auth } = context;
|
||||
|
||||
@@ -14,8 +14,7 @@ const SCHEMA_FIELD_NAME_ATT = {
|
||||
|
||||
const SCHEMA_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
name: 'SchemaFieldInput',
|
||||
description:
|
||||
'The SchemaFieldInput is used to specify a field of an object class schema.',
|
||||
description: 'The SchemaFieldInput is used to specify a field of an object class schema.',
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
},
|
||||
@@ -57,8 +56,7 @@ const SCHEMA_STRING_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_STRING_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaStringField',
|
||||
description:
|
||||
'The SchemaStringField is used to return information of a String field.',
|
||||
description: 'The SchemaStringField is used to return information of a String field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -76,8 +74,7 @@ const SCHEMA_NUMBER_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_NUMBER_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaNumberField',
|
||||
description:
|
||||
'The SchemaNumberField is used to return information of a Number field.',
|
||||
description: 'The SchemaNumberField is used to return information of a Number field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -95,8 +92,7 @@ const SCHEMA_BOOLEAN_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_BOOLEAN_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaBooleanField',
|
||||
description:
|
||||
'The SchemaBooleanField is used to return information of a Boolean field.',
|
||||
description: 'The SchemaBooleanField is used to return information of a Boolean field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -114,8 +110,7 @@ const SCHEMA_ARRAY_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_ARRAY_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaArrayField',
|
||||
description:
|
||||
'The SchemaArrayField is used to return information of an Array field.',
|
||||
description: 'The SchemaArrayField is used to return information of an Array field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -133,8 +128,7 @@ const SCHEMA_OBJECT_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_OBJECT_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaObjectField',
|
||||
description:
|
||||
'The SchemaObjectField is used to return information of an Object field.',
|
||||
description: 'The SchemaObjectField is used to return information of an Object field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -152,8 +146,7 @@ const SCHEMA_DATE_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_DATE_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaDateField',
|
||||
description:
|
||||
'The SchemaDateField is used to return information of a Date field.',
|
||||
description: 'The SchemaDateField is used to return information of a Date field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -171,8 +164,7 @@ const SCHEMA_FILE_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_FILE_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaFileField',
|
||||
description:
|
||||
'The SchemaFileField is used to return information of a File field.',
|
||||
description: 'The SchemaFileField is used to return information of a File field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -190,8 +182,7 @@ const SCHEMA_GEO_POINT_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_GEO_POINT_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaGeoPointField',
|
||||
description:
|
||||
'The SchemaGeoPointField is used to return information of a Geo Point field.',
|
||||
description: 'The SchemaGeoPointField is used to return information of a Geo Point field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -209,8 +200,7 @@ const SCHEMA_POLYGON_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_POLYGON_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaPolygonField',
|
||||
description:
|
||||
'The SchemaPolygonField is used to return information of a Polygon field.',
|
||||
description: 'The SchemaPolygonField is used to return information of a Polygon field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -228,8 +218,7 @@ const SCHEMA_BYTES_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_BYTES_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaBytesField',
|
||||
description:
|
||||
'The SchemaBytesField is used to return information of a Bytes field.',
|
||||
description: 'The SchemaBytesField is used to return information of a Bytes field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -253,8 +242,7 @@ const SCHEMA_POINTER_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_POINTER_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaPointerField',
|
||||
description:
|
||||
'The SchemaPointerField is used to return information of a Pointer field.',
|
||||
description: 'The SchemaPointerField is used to return information of a Pointer field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -274,8 +262,7 @@ const SCHEMA_RELATION_FIELD_INPUT = new GraphQLInputObjectType({
|
||||
|
||||
const SCHEMA_RELATION_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaRelationField',
|
||||
description:
|
||||
'The SchemaRelationField is used to return information of a Relation field.',
|
||||
description: 'The SchemaRelationField is used to return information of a Relation field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -285,8 +272,7 @@ const SCHEMA_RELATION_FIELD = new GraphQLObjectType({
|
||||
|
||||
const SCHEMA_ACL_FIELD = new GraphQLObjectType({
|
||||
name: 'SchemaACLField',
|
||||
description:
|
||||
'The SchemaACLField is used to return information of an ACL field.',
|
||||
description: 'The SchemaACLField is used to return information of an ACL field.',
|
||||
interfaces: [SCHEMA_FIELD],
|
||||
fields: {
|
||||
name: SCHEMA_FIELD_NAME_ATT,
|
||||
@@ -298,28 +284,23 @@ const SCHEMA_FIELDS_INPUT = new GraphQLInputObjectType({
|
||||
description: `The CreateClassSchemaInput type is used to specify the schema for a new object class to be created.`,
|
||||
fields: {
|
||||
addStrings: {
|
||||
description:
|
||||
'These are the String fields to be added to the class schema.',
|
||||
description: 'These are the String fields to be added to the class schema.',
|
||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_STRING_FIELD_INPUT)),
|
||||
},
|
||||
addNumbers: {
|
||||
description:
|
||||
'These are the Number fields to be added to the class schema.',
|
||||
description: 'These are the Number fields to be added to the class schema.',
|
||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_NUMBER_FIELD_INPUT)),
|
||||
},
|
||||
addBooleans: {
|
||||
description:
|
||||
'These are the Boolean fields to be added to the class schema.',
|
||||
description: 'These are the Boolean fields to be added to the class schema.',
|
||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_BOOLEAN_FIELD_INPUT)),
|
||||
},
|
||||
addArrays: {
|
||||
description:
|
||||
'These are the Array fields to be added to the class schema.',
|
||||
description: 'These are the Array fields to be added to the class schema.',
|
||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_ARRAY_FIELD_INPUT)),
|
||||
},
|
||||
addObjects: {
|
||||
description:
|
||||
'These are the Object fields to be added to the class schema.',
|
||||
description: 'These are the Object fields to be added to the class schema.',
|
||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_OBJECT_FIELD_INPUT)),
|
||||
},
|
||||
addDates: {
|
||||
@@ -336,23 +317,19 @@ const SCHEMA_FIELDS_INPUT = new GraphQLInputObjectType({
|
||||
type: SCHEMA_GEO_POINT_FIELD_INPUT,
|
||||
},
|
||||
addPolygons: {
|
||||
description:
|
||||
'These are the Polygon fields to be added to the class schema.',
|
||||
description: 'These are the Polygon fields to be added to the class schema.',
|
||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_POLYGON_FIELD_INPUT)),
|
||||
},
|
||||
addBytes: {
|
||||
description:
|
||||
'These are the Bytes fields to be added to the class schema.',
|
||||
description: 'These are the Bytes fields to be added to the class schema.',
|
||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_BYTES_FIELD_INPUT)),
|
||||
},
|
||||
addPointers: {
|
||||
description:
|
||||
'These are the Pointer fields to be added to the class schema.',
|
||||
description: 'These are the Pointer fields to be added to the class schema.',
|
||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_POINTER_FIELD_INPUT)),
|
||||
},
|
||||
addRelations: {
|
||||
description:
|
||||
'These are the Relation fields to be added to the class schema.',
|
||||
description: 'These are the Relation fields to be added to the class schema.',
|
||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_RELATION_FIELD_INPUT)),
|
||||
},
|
||||
remove: {
|
||||
@@ -374,9 +351,7 @@ const CLASS = new GraphQLObjectType({
|
||||
name: CLASS_NAME_ATT,
|
||||
schemaFields: {
|
||||
description: "These are the schema's fields of the object class.",
|
||||
type: new GraphQLNonNull(
|
||||
new GraphQLList(new GraphQLNonNull(SCHEMA_FIELD))
|
||||
),
|
||||
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(SCHEMA_FIELD))),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -5,18 +5,10 @@ import rest from '../../rest';
|
||||
import { extractKeysAndInclude } from './parseClassTypes';
|
||||
import { Auth } from '../../Auth';
|
||||
|
||||
const getUserFromSessionToken = async (
|
||||
context,
|
||||
queryInfo,
|
||||
keysPrefix,
|
||||
userId
|
||||
) => {
|
||||
const getUserFromSessionToken = async (context, queryInfo, keysPrefix, userId) => {
|
||||
const { info, config } = context;
|
||||
if (!info || !info.sessionToken) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_SESSION_TOKEN,
|
||||
'Invalid session token'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
|
||||
}
|
||||
const sessionToken = info.sessionToken;
|
||||
const selectedFields = getFieldNames(queryInfo)
|
||||
@@ -70,10 +62,7 @@ const getUserFromSessionToken = async (
|
||||
info.context
|
||||
);
|
||||
if (!response.results || response.results.length == 0) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_SESSION_TOKEN,
|
||||
'Invalid session token'
|
||||
);
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
|
||||
} else {
|
||||
const user = response.results[0];
|
||||
return {
|
||||
@@ -91,17 +80,11 @@ const load = parseGraphQLSchema => {
|
||||
parseGraphQLSchema.addGraphQLQuery(
|
||||
'viewer',
|
||||
{
|
||||
description:
|
||||
'The viewer query can be used to return the current user data.',
|
||||
description: 'The viewer query can be used to return the current user data.',
|
||||
type: new GraphQLNonNull(parseGraphQLSchema.viewerType),
|
||||
async resolve(_source, _args, context, queryInfo) {
|
||||
try {
|
||||
return await getUserFromSessionToken(
|
||||
context,
|
||||
queryInfo,
|
||||
'user.',
|
||||
false
|
||||
);
|
||||
return await getUserFromSessionToken(context, queryInfo, 'user.', false);
|
||||
} catch (e) {
|
||||
parseGraphQLSchema.handleError(e);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
|
||||
|
||||
const transformConstraintTypeToGraphQL = (
|
||||
parseType,
|
||||
targetClass,
|
||||
parseClassTypes,
|
||||
fieldName
|
||||
) => {
|
||||
const transformConstraintTypeToGraphQL = (parseType, targetClass, parseClassTypes, fieldName) => {
|
||||
if (fieldName === 'id' || fieldName === 'objectId') {
|
||||
return defaultGraphQLTypes.ID_WHERE_INPUT;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import {
|
||||
GraphQLString,
|
||||
GraphQLFloat,
|
||||
GraphQLBoolean,
|
||||
GraphQLList,
|
||||
} from 'graphql';
|
||||
import { GraphQLString, GraphQLFloat, GraphQLBoolean, GraphQLList } from 'graphql';
|
||||
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
|
||||
|
||||
const transformInputTypeToGraphQL = (
|
||||
parseType,
|
||||
targetClass,
|
||||
parseClassTypes
|
||||
) => {
|
||||
const transformInputTypeToGraphQL = (parseType, targetClass, parseClassTypes) => {
|
||||
switch (parseType) {
|
||||
case 'String':
|
||||
return GraphQLString;
|
||||
|
||||
@@ -14,19 +14,13 @@ const transformTypes = async (
|
||||
classGraphQLUpdateType,
|
||||
config: { isCreateEnabled, isUpdateEnabled },
|
||||
} = parseGraphQLSchema.parseClassTypes[className];
|
||||
const parseClass = parseGraphQLSchema.parseClasses.find(
|
||||
(clazz) => clazz.className === className
|
||||
);
|
||||
const parseClass = parseGraphQLSchema.parseClasses.find(clazz => clazz.className === className);
|
||||
if (fields) {
|
||||
const classGraphQLCreateTypeFields =
|
||||
isCreateEnabled && classGraphQLCreateType
|
||||
? classGraphQLCreateType.getFields()
|
||||
: null;
|
||||
isCreateEnabled && classGraphQLCreateType ? classGraphQLCreateType.getFields() : null;
|
||||
const classGraphQLUpdateTypeFields =
|
||||
isUpdateEnabled && classGraphQLUpdateType
|
||||
? classGraphQLUpdateType.getFields()
|
||||
: null;
|
||||
const promises = Object.keys(fields).map(async (field) => {
|
||||
isUpdateEnabled && classGraphQLUpdateType ? classGraphQLUpdateType.getFields() : null;
|
||||
const promises = Object.keys(fields).map(async field => {
|
||||
let inputTypeField;
|
||||
if (inputType === 'create' && classGraphQLCreateTypeFields) {
|
||||
inputTypeField = classGraphQLCreateTypeFields[field];
|
||||
@@ -84,18 +78,15 @@ const transformers = {
|
||||
}
|
||||
throw new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.');
|
||||
},
|
||||
polygon: (value) => ({
|
||||
polygon: value => ({
|
||||
__type: 'Polygon',
|
||||
coordinates: value.map((geoPoint) => [
|
||||
geoPoint.latitude,
|
||||
geoPoint.longitude,
|
||||
]),
|
||||
coordinates: value.map(geoPoint => [geoPoint.latitude, geoPoint.longitude]),
|
||||
}),
|
||||
geoPoint: (value) => ({
|
||||
geoPoint: value => ({
|
||||
...value,
|
||||
__type: 'GeoPoint',
|
||||
}),
|
||||
ACL: (value) => {
|
||||
ACL: value => {
|
||||
const parseACL = {};
|
||||
if (value.public) {
|
||||
parseACL['*'] = {
|
||||
@@ -104,7 +95,7 @@ const transformers = {
|
||||
};
|
||||
}
|
||||
if (value.users) {
|
||||
value.users.forEach((rule) => {
|
||||
value.users.forEach(rule => {
|
||||
const globalIdObject = fromGlobalId(rule.userId);
|
||||
if (globalIdObject.type === '_User') {
|
||||
rule.userId = globalIdObject.id;
|
||||
@@ -116,7 +107,7 @@ const transformers = {
|
||||
});
|
||||
}
|
||||
if (value.roles) {
|
||||
value.roles.forEach((rule) => {
|
||||
value.roles.forEach(rule => {
|
||||
parseACL[`role:${rule.roleName}`] = {
|
||||
read: rule.read,
|
||||
write: rule.write,
|
||||
@@ -125,13 +116,7 @@ const transformers = {
|
||||
}
|
||||
return parseACL;
|
||||
},
|
||||
relation: async (
|
||||
targetClass,
|
||||
field,
|
||||
value,
|
||||
parseGraphQLSchema,
|
||||
{ config, auth, info }
|
||||
) => {
|
||||
relation: async (targetClass, field, value, parseGraphQLSchema, { config, auth, info }) => {
|
||||
if (Object.keys(value).length === 0)
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_POINTER,
|
||||
@@ -147,22 +132,16 @@ const transformers = {
|
||||
if (value.createAndAdd) {
|
||||
nestedObjectsToAdd = (
|
||||
await Promise.all(
|
||||
value.createAndAdd.map(async (input) => {
|
||||
value.createAndAdd.map(async input => {
|
||||
const parseFields = await transformTypes('create', input, {
|
||||
className: targetClass,
|
||||
parseGraphQLSchema,
|
||||
req: { config, auth, info },
|
||||
});
|
||||
return objectsMutations.createObject(
|
||||
targetClass,
|
||||
parseFields,
|
||||
config,
|
||||
auth,
|
||||
info
|
||||
);
|
||||
return objectsMutations.createObject(targetClass, parseFields, config, auth, info);
|
||||
})
|
||||
)
|
||||
).map((object) => ({
|
||||
).map(object => ({
|
||||
__type: 'Pointer',
|
||||
className: targetClass,
|
||||
objectId: object.objectId,
|
||||
@@ -171,7 +150,7 @@ const transformers = {
|
||||
|
||||
if (value.add || nestedObjectsToAdd.length > 0) {
|
||||
if (!value.add) value.add = [];
|
||||
value.add = value.add.map((input) => {
|
||||
value.add = value.add.map(input => {
|
||||
const globalIdObject = fromGlobalId(input);
|
||||
if (globalIdObject.type === targetClass) {
|
||||
input = globalIdObject.id;
|
||||
@@ -191,7 +170,7 @@ const transformers = {
|
||||
if (value.remove) {
|
||||
op.ops.push({
|
||||
__op: 'RemoveRelation',
|
||||
objects: value.remove.map((input) => {
|
||||
objects: value.remove.map(input => {
|
||||
const globalIdObject = fromGlobalId(input);
|
||||
if (globalIdObject.type === targetClass) {
|
||||
input = globalIdObject.id;
|
||||
@@ -206,13 +185,7 @@ const transformers = {
|
||||
}
|
||||
return op;
|
||||
},
|
||||
pointer: async (
|
||||
targetClass,
|
||||
field,
|
||||
value,
|
||||
parseGraphQLSchema,
|
||||
{ config, auth, info }
|
||||
) => {
|
||||
pointer: async (targetClass, field, value, parseGraphQLSchema, { config, auth, info }) => {
|
||||
if (Object.keys(value).length > 1 || Object.keys(value).length === 0)
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_POINTER,
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
|
||||
import {
|
||||
GraphQLString,
|
||||
GraphQLFloat,
|
||||
GraphQLBoolean,
|
||||
GraphQLList,
|
||||
GraphQLNonNull,
|
||||
} from 'graphql';
|
||||
import { GraphQLString, GraphQLFloat, GraphQLBoolean, GraphQLList, GraphQLNonNull } from 'graphql';
|
||||
|
||||
const transformOutputTypeToGraphQL = (
|
||||
parseType,
|
||||
targetClass,
|
||||
parseClassTypes
|
||||
) => {
|
||||
const transformOutputTypeToGraphQL = (parseType, targetClass, parseClassTypes) => {
|
||||
switch (parseType) {
|
||||
case 'String':
|
||||
return GraphQLString;
|
||||
@@ -41,9 +31,7 @@ const transformOutputTypeToGraphQL = (
|
||||
parseClassTypes[targetClass] &&
|
||||
parseClassTypes[targetClass].classGraphQLFindResultType
|
||||
) {
|
||||
return new GraphQLNonNull(
|
||||
parseClassTypes[targetClass].classGraphQLFindResultType
|
||||
);
|
||||
return new GraphQLNonNull(parseClassTypes[targetClass].classGraphQLFindResultType);
|
||||
} else {
|
||||
return new GraphQLNonNull(defaultGraphQLTypes.OBJECT);
|
||||
}
|
||||
|
||||
@@ -51,9 +51,7 @@ const transformQueryConstraintInputToParse = (
|
||||
parentConstraints,
|
||||
parseClasses
|
||||
) => {
|
||||
const fields = parseClasses.find(
|
||||
parseClass => parseClass.className === className
|
||||
).fields;
|
||||
const fields = parseClasses.find(parseClass => parseClass.className === className).fields;
|
||||
if (parentFieldName === 'id' && className) {
|
||||
Object.keys(constraints).forEach(constraintName => {
|
||||
const constraintValue = constraints[constraintName];
|
||||
@@ -110,12 +108,7 @@ const transformQueryConstraintInputToParse = (
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
if (
|
||||
fieldValue.key &&
|
||||
fieldValue.value &&
|
||||
parentConstraints &&
|
||||
parentFieldName
|
||||
) {
|
||||
if (fieldValue.key && fieldValue.value && parentConstraints && parentFieldName) {
|
||||
delete parentConstraints[parentFieldName];
|
||||
parentConstraints[`${parentFieldName}.${fieldValue.key}`] = {
|
||||
...parentConstraints[`${parentFieldName}.${fieldValue.key}`],
|
||||
@@ -123,8 +116,7 @@ const transformQueryConstraintInputToParse = (
|
||||
};
|
||||
} else if (
|
||||
fields[parentFieldName] &&
|
||||
(fields[parentFieldName].type === 'Pointer' ||
|
||||
fields[parentFieldName].type === 'Relation')
|
||||
(fields[parentFieldName].type === 'Pointer' || fields[parentFieldName].type === 'Relation')
|
||||
) {
|
||||
const { targetClass } = fields[parentFieldName];
|
||||
if (fieldName === 'exists') {
|
||||
@@ -193,11 +185,7 @@ const transformQueryConstraintInputToParse = (
|
||||
}
|
||||
break;
|
||||
case 'box':
|
||||
if (
|
||||
typeof fieldValue === 'object' &&
|
||||
fieldValue.bottomLeft &&
|
||||
fieldValue.upperRight
|
||||
) {
|
||||
if (typeof fieldValue === 'object' && fieldValue.bottomLeft && fieldValue.upperRight) {
|
||||
fieldValue = [
|
||||
{
|
||||
__type: 'GeoPoint',
|
||||
@@ -221,11 +209,7 @@ const transformQueryConstraintInputToParse = (
|
||||
}
|
||||
break;
|
||||
case 'centerSphere':
|
||||
if (
|
||||
typeof fieldValue === 'object' &&
|
||||
fieldValue.center &&
|
||||
fieldValue.distance
|
||||
) {
|
||||
if (typeof fieldValue === 'object' && fieldValue.center && fieldValue.distance) {
|
||||
fieldValue = [
|
||||
{
|
||||
__type: 'GeoPoint',
|
||||
|
||||
@@ -22,20 +22,12 @@ const transformToParse = (graphQLSchemaFields, existingFields) => {
|
||||
}
|
||||
if (
|
||||
graphQLSchemaFields.remove &&
|
||||
graphQLSchemaFields.remove.find(
|
||||
removeField => removeField.name === field.name
|
||||
)
|
||||
graphQLSchemaFields.remove.find(removeField => removeField.name === field.name)
|
||||
) {
|
||||
return parseSchemaFields;
|
||||
}
|
||||
if (
|
||||
parseSchemaFields[field.name] ||
|
||||
(existingFields && existingFields[field.name])
|
||||
) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_KEY_NAME,
|
||||
`Duplicated field name: ${field.name}`
|
||||
);
|
||||
if (parseSchemaFields[field.name] || (existingFields && existingFields[field.name])) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Duplicated field name: ${field.name}`);
|
||||
}
|
||||
if (type === 'Relation' || type === 'Pointer') {
|
||||
return {
|
||||
|
||||
@@ -127,7 +127,8 @@ module.exports.ParseServerOptions = {
|
||||
},
|
||||
emailVerifyTokenReuseIfValid: {
|
||||
env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_REUSE_IF_VALID',
|
||||
help: 'an existing email verify token should be reused when resend verification email is requested',
|
||||
help:
|
||||
'an existing email verify token should be reused when resend verification email is requested',
|
||||
action: parsers.booleanParser,
|
||||
default: false,
|
||||
},
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
const LiveQueryServerOptions = require('../../Options/Definitions')
|
||||
.LiveQueryServerOptions;
|
||||
const LiveQueryServerOptions = require('../../Options/Definitions').LiveQueryServerOptions;
|
||||
export default LiveQueryServerOptions;
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
const ParseServerDefinitions = require('../../Options/Definitions')
|
||||
.ParseServerOptions;
|
||||
const ParseServerDefinitions = require('../../Options/Definitions').ParseServerOptions;
|
||||
export default ParseServerDefinitions;
|
||||
|
||||
@@ -5,7 +5,7 @@ let _definitions;
|
||||
let _reverseDefinitions;
|
||||
let _defaults;
|
||||
|
||||
Command.prototype.loadDefinitions = function(definitions) {
|
||||
Command.prototype.loadDefinitions = function (definitions) {
|
||||
_definitions = definitions;
|
||||
|
||||
Object.keys(definitions).reduce((program, opt) => {
|
||||
@@ -47,7 +47,7 @@ Command.prototype.loadDefinitions = function(definitions) {
|
||||
}, {});
|
||||
|
||||
/* istanbul ignore next */
|
||||
this.on('--help', function() {
|
||||
this.on('--help', function () {
|
||||
console.log(' Configure From Environment:');
|
||||
console.log('');
|
||||
Object.keys(_reverseDefinitions).forEach(key => {
|
||||
@@ -100,7 +100,7 @@ function parseConfigFile(program) {
|
||||
return options;
|
||||
}
|
||||
|
||||
Command.prototype.setValuesIfNeeded = function(options) {
|
||||
Command.prototype.setValuesIfNeeded = function (options) {
|
||||
Object.keys(options).forEach(key => {
|
||||
if (!Object.prototype.hasOwnProperty.call(this, key)) {
|
||||
this[key] = options[key];
|
||||
@@ -110,7 +110,7 @@ Command.prototype.setValuesIfNeeded = function(options) {
|
||||
|
||||
Command.prototype._parse = Command.prototype.parse;
|
||||
|
||||
Command.prototype.parse = function(args, env) {
|
||||
Command.prototype.parse = function (args, env) {
|
||||
this._parse(args);
|
||||
// Parse the environment first
|
||||
const envOptions = parseEnvironment(env);
|
||||
@@ -123,7 +123,7 @@ Command.prototype.parse = function(args, env) {
|
||||
this.setValuesIfNeeded(_defaults);
|
||||
};
|
||||
|
||||
Command.prototype.getOptions = function() {
|
||||
Command.prototype.getOptions = function () {
|
||||
return Object.keys(_definitions).reduce((options, key) => {
|
||||
if (typeof this[key] !== 'undefined') {
|
||||
options[key] = this[key];
|
||||
|
||||
@@ -24,7 +24,7 @@ function logStartupOptions(options) {
|
||||
}
|
||||
}
|
||||
|
||||
export default function({ definitions, help, usage, start }) {
|
||||
export default function ({ definitions, help, usage, start }) {
|
||||
program.loadDefinitions(definitions);
|
||||
if (usage) {
|
||||
program.usage(usage);
|
||||
@@ -35,7 +35,7 @@ export default function({ definitions, help, usage, start }) {
|
||||
program.parse(process.argv, process.env);
|
||||
|
||||
const options = program.getOptions();
|
||||
start(program, options, function() {
|
||||
start(program, options, function () {
|
||||
logStartupOptions(options);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user