Fix Prettier (#7066)

This commit is contained in:
Diamond Lewis
2020-12-13 11:19:04 -06:00
committed by GitHub
parent d4948572a8
commit 033a0bd443
64 changed files with 697 additions and 1887 deletions

View File

@@ -105,7 +105,7 @@
"posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} mongodb-runner stop", "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} mongodb-runner stop",
"coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} TESTING=1 nyc jasmine", "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} TESTING=1 nyc jasmine",
"start": "node ./bin/parse-server", "start": "node ./bin/parse-server",
"prettier": "prettier --write {src,spec}/{**/*,*}.js", "prettier": "prettier --write '{src,spec}/{**/*,*}.js'",
"prepare": "npm run build", "prepare": "npm run build",
"postinstall": "node -p 'require(\"./postinstall.js\")()'" "postinstall": "node -p 'require(\"./postinstall.js\")()'"
}, },

View File

@@ -216,7 +216,7 @@ it('Should delete the password from authData after validation', done => {
const options = { const options = {
suffix: 'o=example', suffix: 'o=example',
url: `ldap://localhost:${port}`, url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example' dn: 'uid={{id}}, o=example',
}; };
const authData = { id: 'testuser', password: 'secret' }; const authData = { id: 'testuser', password: 'secret' };
@@ -237,22 +237,23 @@ it('Should not save the password in the user record after authentication', done
const options = { const options = {
suffix: 'o=example', suffix: 'o=example',
url: `ldap://localhost:${port}`, url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example' dn: 'uid={{id}}, o=example',
}; };
reconfigureServer({ auth: { ldap: options } }).then(() => { reconfigureServer({ auth: { ldap: options } }).then(() => {
const authData = { authData: { id: 'testuser', password: 'secret' } }; const authData = { authData: { id: 'testuser', password: 'secret' } };
Parse.User.logInWith('ldap', authData).then((returnedUser) => { Parse.User.logInWith('ldap', authData).then(returnedUser => {
const query = new Parse.Query("User"); const query = new Parse.Query('User');
query query
.equalTo('objectId', returnedUser.id).first({ useMasterKey: true }) .equalTo('objectId', returnedUser.id)
.then((user) => { .first({ useMasterKey: true })
expect(user.get('authData')).toEqual({ ldap:{ id: 'testuser' }}); .then(user => {
expect(user.get('authData')).toEqual({ ldap: { id: 'testuser' } });
expect(user.get('authData').ldap.password).toBeUndefined(); expect(user.get('authData').ldap.password).toBeUndefined();
done(); done();
}) })
.catch(done.fail) .catch(done.fail)
.finally(() => server.close()) .finally(() => server.close());
}) });
}); });
}); });
}); });

View File

@@ -2,12 +2,9 @@ var https = require('https'),
crypto = require('crypto'); crypto = require('crypto');
var Parse = require('parse/node').Parse; var Parse = require('parse/node').Parse;
var OAuth = function(options) { var OAuth = function (options) {
if (!options) { if (!options) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'No options passed to OAuth');
Parse.Error.INTERNAL_SERVER_ERROR,
'No options passed to OAuth'
);
} }
this.consumer_key = options.consumer_key; this.consumer_key = options.consumer_key;
this.consumer_secret = options.consumer_secret; this.consumer_secret = options.consumer_secret;
@@ -17,22 +14,22 @@ var OAuth = function(options) {
this.oauth_params = options.oauth_params || {}; 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); var request = this.buildRequest(method, path, params, body);
// Encode the body properly, the current Parse Implementation don't do it properly // 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 var httpRequest = https
.request(request, function(res) { .request(request, function (res) {
var data = ''; var data = '';
res.on('data', function(chunk) { res.on('data', function (chunk) {
data += chunk; data += chunk;
}); });
res.on('end', function() { res.on('end', function () {
data = JSON.parse(data); data = JSON.parse(data);
resolve(data); resolve(data);
}); });
}) })
.on('error', function() { .on('error', function () {
reject('Failed to make an OAuth request'); reject('Failed to make an OAuth request');
}); });
if (request.body) { 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) { if (path.indexOf('/') != 0) {
path = '/' + path; path = '/' + path;
} }
@@ -62,12 +59,7 @@ OAuth.prototype.buildRequest = function(method, path, params, body) {
oauth_params['oauth_token'] = this.auth_token; oauth_params['oauth_token'] = this.auth_token;
} }
request = OAuth.signRequest( request = OAuth.signRequest(request, oauth_params, this.consumer_secret, this.auth_token_secret);
request,
oauth_params,
this.consumer_secret,
this.auth_token_secret
);
if (body && Object.keys(body).length > 0) { if (body && Object.keys(body).length > 0) {
request.body = OAuth.buildParameterString(body); request.body = OAuth.buildParameterString(body);
@@ -75,18 +67,18 @@ OAuth.prototype.buildRequest = function(method, path, params, body) {
return request; return request;
}; };
OAuth.prototype.get = function(path, params) { OAuth.prototype.get = function (path, params) {
return this.send('GET', 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); return this.send('POST', path, params, body);
}; };
/* /*
Proper string %escape encoding Proper string %escape encoding
*/ */
OAuth.encode = function(str) { OAuth.encode = function (str) {
// discuss at: http://phpjs.org/functions/rawurlencode/ // discuss at: http://phpjs.org/functions/rawurlencode/
// original by: Brett Zamir (http://brett-zamir.me) // original by: Brett Zamir (http://brett-zamir.me)
// input by: travc // input by: travc
@@ -126,25 +118,23 @@ OAuth.version = '1.0';
/* /*
Generate a nonce Generate a nonce
*/ */
OAuth.nonce = function() { OAuth.nonce = function () {
var text = ''; var text = '';
var possible = var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (var i = 0; i < 30; i++) for (var i = 0; i < 30; i++) text += possible.charAt(Math.floor(Math.random() * possible.length));
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text; return text;
}; };
OAuth.buildParameterString = function(obj) { OAuth.buildParameterString = function (obj) {
// Sort keys and encode values // Sort keys and encode values
if (obj) { if (obj) {
var keys = Object.keys(obj).sort(); var keys = Object.keys(obj).sort();
// Map key=value, join them by & // Map key=value, join them by &
return keys return keys
.map(function(key) { .map(function (key) {
return key + '=' + OAuth.encode(obj[key]); return key + '=' + OAuth.encode(obj[key]);
}) })
.join('&'); .join('&');
@@ -157,33 +147,19 @@ OAuth.buildParameterString = function(obj) {
Build the signature string from the object Build the signature string from the object
*/ */
OAuth.buildSignatureString = function(method, url, parameters) { OAuth.buildSignatureString = function (method, url, parameters) {
return [ return [method.toUpperCase(), OAuth.encode(url), OAuth.encode(parameters)].join('&');
method.toUpperCase(),
OAuth.encode(url),
OAuth.encode(parameters),
].join('&');
}; };
/* /*
Retuns encoded HMAC-SHA1 from key and text Retuns encoded HMAC-SHA1 from key and text
*/ */
OAuth.signature = function(text, key) { OAuth.signature = function (text, key) {
crypto = require('crypto'); crypto = require('crypto');
return OAuth.encode( return OAuth.encode(crypto.createHmac('sha1', key).update(text).digest('base64'));
crypto
.createHmac('sha1', key)
.update(text)
.digest('base64')
);
}; };
OAuth.signRequest = function( OAuth.signRequest = function (request, oauth_parameters, consumer_secret, auth_token_secret) {
request,
oauth_parameters,
consumer_secret,
auth_token_secret
) {
oauth_parameters = oauth_parameters || {}; oauth_parameters = oauth_parameters || {};
// Set default values // Set default values
@@ -224,16 +200,9 @@ OAuth.signRequest = function(
// Build the signature string // Build the signature string
var url = 'https://' + request.host + '' + request.path; var url = 'https://' + request.host + '' + request.path;
var signatureString = OAuth.buildSignatureString( var signatureString = OAuth.buildSignatureString(request.method, url, parameterString);
request.method,
url,
parameterString
);
// Hash the signature string // Hash the signature string
var signatureKey = [ var signatureKey = [OAuth.encode(consumer_secret), OAuth.encode(auth_token_secret)].join('&');
OAuth.encode(consumer_secret),
OAuth.encode(auth_token_secret),
].join('&');
var signature = OAuth.signature(signatureString, signatureKey); var signature = OAuth.signature(signatureString, signatureKey);
@@ -246,7 +215,7 @@ OAuth.signRequest = function(
// Set the authorization header // Set the authorization header
var authHeader = Object.keys(oauth_parameters) var authHeader = Object.keys(oauth_parameters)
.sort() .sort()
.map(function(key) { .map(function (key) {
var value = oauth_parameters[key]; var value = oauth_parameters[key];
return key + '="' + value + '"'; return key + '="' + value + '"';
}) })

View File

@@ -33,24 +33,15 @@ const getAppleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
const getHeaderFromToken = token => { const getHeaderFromToken = token => {
const decodedToken = jwt.decode(token, { complete: true }); const decodedToken = jwt.decode(token, { complete: true });
if (!decodedToken) { if (!decodedToken) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `provided token does not decode as JWT`);
Parse.Error.OBJECT_NOT_FOUND,
`provided token does not decode as JWT`
);
} }
return decodedToken.header; return decodedToken.header;
}; };
const verifyIdToken = async ( const verifyIdToken = async ({ token, id }, { clientId, cacheMaxEntries, cacheMaxAge }) => {
{ token, id },
{ clientId, cacheMaxEntries, cacheMaxAge }
) => {
if (!token) { if (!token) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
Parse.Error.OBJECT_NOT_FOUND,
`id token is invalid for this user.`
);
} }
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token); const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
@@ -60,11 +51,7 @@ const verifyIdToken = async (
cacheMaxAge = cacheMaxAge || ONE_HOUR_IN_MS; cacheMaxAge = cacheMaxAge || ONE_HOUR_IN_MS;
cacheMaxEntries = cacheMaxEntries || 5; cacheMaxEntries = cacheMaxEntries || 5;
const appleKey = await getAppleKeyByKeyId( const appleKey = await getAppleKeyByKeyId(keyId, cacheMaxEntries, cacheMaxAge);
keyId,
cacheMaxEntries,
cacheMaxAge
);
const signingKey = appleKey.publicKey || appleKey.rsaPublicKey; const signingKey = appleKey.publicKey || appleKey.rsaPublicKey;
try { try {
@@ -87,10 +74,7 @@ const verifyIdToken = async (
} }
if (jwtClaims.sub !== id) { if (jwtClaims.sub !== id) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
Parse.Error.OBJECT_NOT_FOUND,
`auth data is invalid for this user.`
);
} }
return jwtClaims; return jwtClaims;
}; };

View File

@@ -19,20 +19,12 @@ function getAppSecretPath(authData, options = {}) {
// Returns a promise that fulfills iff this user id is valid. // Returns a promise that fulfills iff this user id is valid.
function validateAuthData(authData, options) { function validateAuthData(authData, options) {
return graphRequest( return graphRequest(
'me?fields=id&access_token=' + 'me?fields=id&access_token=' + authData.access_token + getAppSecretPath(authData, options)
authData.access_token +
getAppSecretPath(authData, options)
).then(data => { ).then(data => {
if ( if ((data && data.id == authData.id) || (process.env.TESTING && authData.id === 'test')) {
(data && data.id == authData.id) ||
(process.env.TESTING && authData.id === 'test')
) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Facebook auth is invalid for this user.'
);
}); });
} }
@@ -43,10 +35,7 @@ function validateAppId(appIds, authData, options) {
return Promise.resolve(); return Promise.resolve();
} }
if (!appIds.length) { if (!appIds.length) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is not configured.');
Parse.Error.OBJECT_NOT_FOUND,
'Facebook auth is not configured.'
);
} }
return graphRequest( return graphRequest(
'app?access_token=' + access_token + getAppSecretPath(authData, options) 'app?access_token=' + access_token + getAppSecretPath(authData, options)
@@ -54,10 +43,7 @@ function validateAppId(appIds, authData, options) {
if (data && appIds.indexOf(data.id) != -1) { if (data && appIds.indexOf(data.id) != -1) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Facebook auth is invalid for this user.'
);
}); });
} }

View File

@@ -96,20 +96,14 @@ function verifySignature(publicKey, authData) {
verifier.update(authData.salt, 'base64'); verifier.update(authData.salt, 'base64');
if (!verifier.verify(publicKey, authData.signature, 'base64')) { if (!verifier.verify(publicKey, authData.signature, 'base64')) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple Game Center - invalid signature');
Parse.Error.OBJECT_NOT_FOUND,
'Apple Game Center - invalid signature'
);
} }
} }
// Returns a promise that fulfills if this user id is valid. // Returns a promise that fulfills if this user id is valid.
async function validateAuthData(authData) { async function validateAuthData(authData) {
if (!authData.id) { if (!authData.id) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple Game Center - authData id missing');
Parse.Error.OBJECT_NOT_FOUND,
'Apple Game Center - authData id missing'
);
} }
authData.playerId = authData.id; authData.playerId = authData.id;
const publicKey = await getAppleCertificate(authData.publicKeyUrl); const publicKey = await getAppleCertificate(authData.publicKeyUrl);

View File

@@ -8,10 +8,7 @@ function validateAuthData(authData) {
if (data && data.id == authData.id) { if (data && data.id == authData.id) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Github auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Github auth is invalid for this user.'
);
}); });
} }

View File

@@ -39,9 +39,7 @@ function getGoogleKeyByKeyId(keyId) {
if (expire) { if (expire) {
cache = Object.assign({}, pems, { cache = Object.assign({}, pems, {
expiresAt: new Date( expiresAt: new Date(new Date().getTime() + Number(expire[1]) * 1000),
new Date().getTime() + Number(expire[1]) * 1000
),
}); });
} }
} }
@@ -57,10 +55,7 @@ function getHeaderFromToken(token) {
const decodedToken = jwt.decode(token, { complete: true }); const decodedToken = jwt.decode(token, { complete: true });
if (!decodedToken) { if (!decodedToken) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `provided token does not decode as JWT`);
Parse.Error.OBJECT_NOT_FOUND,
`provided token does not decode as JWT`
);
} }
return decodedToken.header; return decodedToken.header;
@@ -68,10 +63,7 @@ function getHeaderFromToken(token) {
async function verifyIdToken({ id_token: token, id }, { clientId }) { async function verifyIdToken({ id_token: token, id }, { clientId }) {
if (!token) { if (!token) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
Parse.Error.OBJECT_NOT_FOUND,
`id token is invalid for this user.`
);
} }
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token); const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
@@ -96,10 +88,7 @@ async function verifyIdToken({ id_token: token, id }, { clientId }) {
} }
if (jwtClaims.sub !== id) { if (jwtClaims.sub !== id) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
Parse.Error.OBJECT_NOT_FOUND,
`auth data is invalid for this user.`
);
} }
if (clientId && jwtClaims.aud !== clientId) { if (clientId && jwtClaims.aud !== clientId) {
@@ -140,9 +129,7 @@ function rsaPublicKeyToPEM(modulusB64, exponentB64) {
const encodedExplen = encodeLengthHex(explen); const encodedExplen = encodeLengthHex(explen);
const encodedPubkey = const encodedPubkey =
'30' + '30' +
encodeLengthHex( encodeLengthHex(modlen + explen + encodedModlen.length / 2 + encodedExplen.length / 2 + 2) +
modlen + explen + encodedModlen.length / 2 + encodedExplen.length / 2 + 2
) +
'02' + '02' +
encodedModlen + encodedModlen +
modulusHex + modulusHex +

View File

@@ -1,7 +1,7 @@
const https = require('https'); const https = require('https');
function makeCallback(resolve, reject, noJSON) { function makeCallback(resolve, reject, noJSON) {
return function(res) { return function (res) {
let data = ''; let data = '';
res.on('data', chunk => { res.on('data', chunk => {
data += chunk; data += chunk;
@@ -23,9 +23,7 @@ function makeCallback(resolve, reject, noJSON) {
function get(options, noJSON = false) { function get(options, noJSON = false) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
https https.get(options, makeCallback(resolve, reject, noJSON)).on('error', reject);
.get(options, makeCallback(resolve, reject, noJSON))
.on('error', reject);
}); });
} }

View File

@@ -92,11 +92,7 @@ function loadAuthAdapter(provider, authOptions) {
// Try the configuration methods // Try the configuration methods
if (providerOptions) { if (providerOptions) {
const optionalAdapter = loadAdapter( const optionalAdapter = loadAdapter(providerOptions, undefined, providerOptions);
providerOptions,
undefined,
providerOptions
);
if (optionalAdapter) { if (optionalAdapter) {
['validateAuthData', 'validateAppId'].forEach(key => { ['validateAuthData', 'validateAppId'].forEach(key => {
if (optionalAdapter[key]) { if (optionalAdapter[key]) {
@@ -128,10 +124,7 @@ module.exports = function (authOptions = {}, enableAnonymousUsers = true) {
return; return;
} }
const { adapter, appIds, providerOptions } = loadAuthAdapter( const { adapter, appIds, providerOptions } = loadAuthAdapter(provider, authOptions);
provider,
authOptions
);
return authDataValidator(adapter, appIds, providerOptions); return authDataValidator(adapter, appIds, providerOptions);
}; };

View File

@@ -11,10 +11,7 @@ function validateAuthData(authData) {
if (response && response.data && response.data.id == authData.id) { if (response && response.data && response.data.id == authData.id) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Instagram auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Instagram auth is invalid for this user.'
);
}); });
} }

View File

@@ -5,19 +5,17 @@ const httpsRequest = require('./httpsRequest');
// Returns a promise that fulfills iff this user id is valid. // Returns a promise that fulfills iff this user id is valid.
function validateAuthData(authData, options) { function validateAuthData(authData, options) {
return request(options.janrain_capture_host, authData.access_token).then( return request(options.janrain_capture_host, authData.access_token).then(data => {
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
//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
//see: https://docs.janrain.com/api/registration/entity/#entity if (data && data.stat == 'ok' && data.result == authData.id) {
if (data && data.stat == 'ok' && data.result == authData.id) { return;
return;
}
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Janrain capture auth is invalid for this user.'
);
} }
); 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. // Returns a promise that fulfills iff this app id is valid.

View File

@@ -37,12 +37,7 @@ const { Parse } = require('parse/node');
const httpsRequest = require('./httpsRequest'); const httpsRequest = require('./httpsRequest');
const arraysEqual = (_arr1, _arr2) => { const arraysEqual = (_arr1, _arr2) => {
if ( if (!Array.isArray(_arr1) || !Array.isArray(_arr2) || _arr1.length !== _arr2.length) return false;
!Array.isArray(_arr1) ||
!Array.isArray(_arr2) ||
_arr1.length !== _arr2.length
)
return false;
var arr1 = _arr1.concat().sort(); var arr1 = _arr1.concat().sort();
var arr2 = _arr2.concat().sort(); var arr2 = _arr2.concat().sort();
@@ -54,21 +49,12 @@ const arraysEqual = (_arr1, _arr2) => {
return true; return true;
}; };
const handleAuth = async ( const handleAuth = async ({ access_token, id, roles, groups } = {}, { config } = {}) => {
{ access_token, id, roles, groups } = {},
{ config } = {}
) => {
if (!(access_token && id)) { if (!(access_token && id)) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Missing access token and/or User id');
Parse.Error.OBJECT_NOT_FOUND,
'Missing access token and/or User id'
);
} }
if (!config || !(config['auth-server-url'] && config['realm'])) { if (!config || !(config['auth-server-url'] && config['realm'])) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Missing keycloak configuration');
Parse.Error.OBJECT_NOT_FOUND,
'Missing keycloak configuration'
);
} }
try { try {
const response = await httpsRequest.get({ const response = await httpsRequest.get({
@@ -87,10 +73,7 @@ const handleAuth = async (
) { ) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid authentication');
Parse.Error.OBJECT_NOT_FOUND,
'Invalid authentication'
);
} catch (e) { } catch (e) {
if (e instanceof Parse.Error) { if (e instanceof Parse.Error) {
throw e; throw e;

View File

@@ -4,16 +4,12 @@ const Parse = require('parse/node').Parse;
function validateAuthData(authData, options) { function validateAuthData(authData, options) {
if (!optionsAreValid(options)) { if (!optionsAreValid(options)) {
return new Promise((_, reject) => { return new Promise((_, reject) => {
reject( reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP auth configuration missing'));
new Parse.Error(
Parse.Error.INTERNAL_SERVER_ERROR,
'LDAP auth configuration missing'
)
);
}); });
} }
const clientOptions = (options.url.startsWith("ldaps://")) ? const clientOptions = options.url.startsWith('ldaps://')
{ url: options.url, tlsOptions: options.tlsOptions } : { url: options.url }; ? { url: options.url, tlsOptions: options.tlsOptions }
: { url: options.url };
const client = ldapjs.createClient(clientOptions); const client = ldapjs.createClient(clientOptions);
const userCn = const userCn =
@@ -23,28 +19,31 @@ function validateAuthData(authData, options) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
client.bind(userCn, authData.password, ldapError => { client.bind(userCn, authData.password, ldapError => {
delete(authData.password); delete authData.password;
if (ldapError) { if (ldapError) {
let error; let error;
switch (ldapError.code) { switch (ldapError.code) {
case 49: 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; 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'); error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAPS: Certificate mismatch');
break; break;
default: 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); reject(error);
client.destroy(ldapError); client.destroy(ldapError);
return; return;
} }
if ( if (typeof options.groupCn === 'string' && typeof options.groupFilter === 'string') {
typeof options.groupCn === 'string' &&
typeof options.groupFilter === 'string'
) {
searchForGroup(client, options, authData.id, resolve, reject); searchForGroup(client, options, authData.id, resolve, reject);
} else { } else {
client.unbind(); client.unbind();
@@ -61,7 +60,7 @@ function optionsAreValid(options) {
typeof options.suffix === 'string' && typeof options.suffix === 'string' &&
typeof options.url === 'string' && typeof options.url === 'string' &&
(options.url.startsWith('ldap://') || (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) { if (searchError) {
client.unbind(); client.unbind();
client.destroy(); client.destroy();
return reject( return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
new Parse.Error(
Parse.Error.INTERNAL_SERVER_ERROR,
'LDAP group search failed'
)
);
} }
res.on('searchEntry', entry => { res.on('searchEntry', entry => {
if (entry.object.cn === options.groupCn) { if (entry.object.cn === options.groupCn) {
@@ -96,20 +90,12 @@ function searchForGroup(client, options, id, resolve, reject) {
client.unbind(); client.unbind();
client.destroy(); client.destroy();
return reject( return reject(
new Parse.Error( new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP: User not in group')
Parse.Error.INTERNAL_SERVER_ERROR,
'LDAP: User not in group'
)
); );
} }
}); });
res.on('error', () => { res.on('error', () => {
return reject( return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
new Parse.Error(
Parse.Error.INTERNAL_SERVER_ERROR,
'LDAP group search failed'
)
);
}); });
}); });
} }

View File

@@ -8,10 +8,7 @@ function validateAuthData(authData) {
if (response && response.userId && response.userId === authData.id) { if (response && response.userId && response.userId === authData.id) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Line auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Line auth is invalid for this user.'
);
}); });
} }

View File

@@ -4,17 +4,12 @@ const httpsRequest = require('./httpsRequest');
// Returns a promise that fulfills iff this user id is valid. // Returns a promise that fulfills iff this user id is valid.
function validateAuthData(authData) { function validateAuthData(authData) {
return request('me', authData.access_token, authData.is_mobile_sdk).then( return request('me', authData.access_token, authData.is_mobile_sdk).then(data => {
data => { if (data && data.id == authData.id) {
if (data && data.id == authData.id) { return;
return;
}
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Linkedin auth is invalid for this user.'
);
} }
); 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. // Returns a promise that fulfills iff this app id is valid.

View File

@@ -8,10 +8,7 @@ function validateAuthData(authData) {
if (data && data.id == authData.id) { if (data && data.id == authData.id) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Meetup auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Meetup auth is invalid for this user.'
);
}); });
} }

View File

@@ -4,17 +4,15 @@ const httpsRequest = require('./httpsRequest');
// Returns a promise that fulfills if this user mail is valid. // Returns a promise that fulfills if this user mail is valid.
function validateAuthData(authData) { function validateAuthData(authData) {
return request('me', authData.access_token).then( return request('me', authData.access_token).then(response => {
response => { if (response && response.id && response.id == authData.id) {
if (response && response.id && response.id == authData.id) { return;
return;
}
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Microsoft Graph auth is invalid for this user.'
);
} }
); 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. // Returns a promise that fulfills if this app id is valid.

View File

@@ -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."; "OAuth2: the access_token's appID is empty or is not in the list of permitted appIDs in the auth configuration.";
const MISSING_APPIDS = const MISSING_APPIDS =
'OAuth2 configuration is missing the client app IDs ("appIds" config parameter).'; 'OAuth2 configuration is missing the client app IDs ("appIds" config parameter).';
const MISSING_URL = const MISSING_URL = 'OAuth2 token introspection endpoint URL is missing from configuration!';
'OAuth2 token introspection endpoint URL is missing from configuration!';
// Returns a promise that fulfills if this user id is valid. // Returns a promise that fulfills if this user id is valid.
function validateAuthData(authData, options) { function validateAuthData(authData, options) {

View File

@@ -14,10 +14,7 @@ function validateAuthData(authData) {
if (data && data.sub == authData.id) { if (data && data.sub == authData.id) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'PhantAuth auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'PhantAuth auth is invalid for this user.'
);
}); });
} }

View File

@@ -4,16 +4,11 @@ var Parse = require('parse/node').Parse;
// Returns a promise that fulfills iff this user id is valid. // Returns a promise that fulfills iff this user id is valid.
function validateAuthData(authData) { function validateAuthData(authData) {
return graphRequest('me?access_token=' + authData.access_token).then(function( return graphRequest('me?access_token=' + authData.access_token).then(function (data) {
data
) {
if (data && data.openid == authData.id) { if (data && data.openid == authData.id) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'qq auth is invalid for this user.');
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. // A promisey wrapper for qq graph requests.
function graphRequest(path) { function graphRequest(path) {
return httpsRequest return httpsRequest.get('https://graph.qq.com/oauth2.0/' + path, true).then(data => {
.get('https://graph.qq.com/oauth2.0/' + path, true) return parseResponseData(data);
.then(data => { });
return parseResponseData(data);
});
} }
function parseResponseData(data) { function parseResponseData(data) {
const starPos = data.indexOf('('); const starPos = data.indexOf('(');
const endPos = data.indexOf(')'); const endPos = data.indexOf(')');
if (starPos == -1 || endPos == -1) { if (starPos == -1 || endPos == -1) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'qq auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'qq auth is invalid for this user.'
);
} }
data = data.substring(starPos + 1, endPos - 1); data = data.substring(starPos + 1, endPos - 1);
return JSON.parse(data); return JSON.parse(data);

View File

@@ -8,10 +8,7 @@ function validateAuthData(authData) {
if (data && data.id == authData.id) { if (data && data.id == authData.id) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Spotify auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Spotify auth is invalid for this user.'
);
}); });
} }
@@ -19,19 +16,13 @@ function validateAuthData(authData) {
function validateAppId(appIds, authData) { function validateAppId(appIds, authData) {
var access_token = authData.access_token; var access_token = authData.access_token;
if (!appIds.length) { if (!appIds.length) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Spotify auth is not configured.');
Parse.Error.OBJECT_NOT_FOUND,
'Spotify auth is not configured.'
);
} }
return request('me', access_token).then(data => { return request('me', access_token).then(data => {
if (data && appIds.indexOf(data.id) != -1) { if (data && appIds.indexOf(data.id) != -1) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Spotify auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Spotify auth is invalid for this user.'
);
}); });
} }

View File

@@ -5,10 +5,7 @@ var Parse = require('parse/node').Parse;
// Returns a promise that fulfills iff this user id is valid. // Returns a promise that fulfills iff this user id is valid.
function validateAuthData(authData, options) { function validateAuthData(authData, options) {
if (!options) { if (!options) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Twitter auth configuration missing');
Parse.Error.INTERNAL_SERVER_ERROR,
'Twitter auth configuration missing'
);
} }
options = handleMultipleConfigurations(authData, options); options = handleMultipleConfigurations(authData, options);
var client = new OAuth(options); var client = new OAuth(options);
@@ -20,10 +17,7 @@ function validateAuthData(authData, options) {
if (data && data.id_str == '' + authData.id) { if (data && data.id_str == '' + authData.id) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Twitter auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Twitter auth is invalid for this user.'
);
}); });
} }
@@ -36,20 +30,14 @@ function handleMultipleConfigurations(authData, options) {
if (Array.isArray(options)) { if (Array.isArray(options)) {
const consumer_key = authData.consumer_key; const consumer_key = authData.consumer_key;
if (!consumer_key) { if (!consumer_key) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Twitter auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Twitter auth is invalid for this user.'
);
} }
options = options.filter(option => { options = options.filter(option => {
return option.consumer_key == consumer_key; return option.consumer_key == consumer_key;
}); });
if (options.length == 0) { if (options.length == 0) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Twitter auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Twitter auth is invalid for this user.'
);
} }
options = options[0]; options = options[0];
} }

View File

@@ -11,10 +11,7 @@ function validateAuthData(authData, params) {
if (response && response.access_token) { if (response && response.access_token) {
return request( return request(
'api.vk.com', 'api.vk.com',
'method/users.get?access_token=' + 'method/users.get?access_token=' + authData.access_token + '&v=' + params.apiVersion
authData.access_token +
'&v=' +
params.apiVersion
).then(function (response) { ).then(function (response) {
if ( if (
response && response &&
@@ -24,16 +21,10 @@ function validateAuthData(authData, params) {
) { ) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Vk auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'Vk auth is invalid for this user.'
);
}); });
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Vk appIds or appSecret is incorrect.');
Parse.Error.OBJECT_NOT_FOUND,
'Vk appIds or appSecret is incorrect.'
);
}); });
} }

View File

@@ -4,17 +4,14 @@ var Parse = require('parse/node').Parse;
// Returns a promise that fulfills iff this user id is valid. // Returns a promise that fulfills iff this user id is valid.
function validateAuthData(authData) { function validateAuthData(authData) {
return graphRequest( return graphRequest('auth?access_token=' + authData.access_token + '&openid=' + authData.id).then(
'auth?access_token=' + authData.access_token + '&openid=' + authData.id function (data) {
).then(function(data) { if (data.errcode == 0) {
if (data.errcode == 0) { return;
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. // Returns a promise that fulfills if this app id is valid.

View File

@@ -5,14 +5,11 @@ var querystring = require('querystring');
// Returns a promise that fulfills iff this user id is valid. // Returns a promise that fulfills iff this user id is valid.
function validateAuthData(authData) { 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) { if (data && data.uid == authData.id) {
return; return;
} }
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'weibo auth is invalid for this user.');
Parse.Error.OBJECT_NOT_FOUND,
'weibo auth is invalid for this user.'
);
}); });
} }

View File

@@ -9,7 +9,7 @@ function debug() {
logger.debug.apply(logger, ['RedisCacheAdapter', ...arguments]); 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 { export class RedisCacheAdapter {
constructor(redisCtx, ttl = DEFAULT_REDIS_TTL) { constructor(redisCtx, ttl = DEFAULT_REDIS_TTL) {
@@ -22,8 +22,8 @@ export class RedisCacheAdapter {
if (!this.client) { if (!this.client) {
return Promise.resolve(); return Promise.resolve();
} }
return new Promise((resolve) => { return new Promise(resolve => {
this.client.quit((err) => { this.client.quit(err => {
if (err) { if (err) {
logger.error('RedisCacheAdapter error on shutdown', { error: err }); logger.error('RedisCacheAdapter error on shutdown', { error: err });
} }
@@ -37,7 +37,7 @@ export class RedisCacheAdapter {
return this.queue.enqueue( return this.queue.enqueue(
key, key,
() => () =>
new Promise((resolve) => { new Promise(resolve => {
this.client.get(key, function (err, res) { this.client.get(key, function (err, res) {
debug('-> get', key, res); debug('-> get', key, res);
if (!res) { if (!res) {
@@ -62,7 +62,7 @@ export class RedisCacheAdapter {
return this.queue.enqueue( return this.queue.enqueue(
key, key,
() => () =>
new Promise((resolve) => { new Promise(resolve => {
this.client.set(key, value, function () { this.client.set(key, value, function () {
resolve(); resolve();
}); });
@@ -77,7 +77,7 @@ export class RedisCacheAdapter {
return this.queue.enqueue( return this.queue.enqueue(
key, key,
() => () =>
new Promise((resolve) => { new Promise(resolve => {
this.client.psetex(key, ttl, value, function () { this.client.psetex(key, ttl, value, function () {
resolve(); resolve();
}); });
@@ -90,7 +90,7 @@ export class RedisCacheAdapter {
return this.queue.enqueue( return this.queue.enqueue(
key, key,
() => () =>
new Promise((resolve) => { new Promise(resolve => {
this.client.del(key, function () { this.client.del(key, function () {
resolve(); resolve();
}); });
@@ -103,7 +103,7 @@ export class RedisCacheAdapter {
return this.queue.enqueue( return this.queue.enqueue(
FLUSH_DB_KEY, FLUSH_DB_KEY,
() => () =>
new Promise((resolve) => { new Promise(resolve => {
this.client.flushdb(function () { this.client.flushdb(function () {
resolve(); resolve();
}); });

View File

@@ -38,12 +38,7 @@ export class FilesAdapter {
* *
* @return {Promise} a promise that should fail if the storage didn't succeed * @return {Promise} a promise that should fail if the storage didn't succeed
*/ */
createFile( createFile(filename: string, data, contentType: string, options: Object): Promise {}
filename: string,
data,
contentType: string,
options: Object
): Promise {}
/** Responsible for deleting the specified file /** 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@. ~_-]*$/; const regx = /^[_a-zA-Z0-9][a-zA-Z0-9@. ~_-]*$/;
if (!filename.match(regx)) { if (!filename.match(regx)) {
return new Parse.Error( return new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename contains invalid characters.');
Parse.Error.INVALID_FILE_NAME,
'Filename contains invalid characters.'
);
} }
return null; return null;
} }

View File

@@ -28,11 +28,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
this._algorithm = 'aes-256-gcm'; this._algorithm = 'aes-256-gcm';
this._encryptionKey = this._encryptionKey =
encryptionKey !== undefined encryptionKey !== undefined
? crypto ? crypto.createHash('sha256').update(String(encryptionKey)).digest('base64').substr(0, 32)
.createHash('sha256')
.update(String(encryptionKey))
.digest('base64')
.substr(0, 32)
: null; : null;
const defaultMongoOptions = { const defaultMongoOptions = {
useNewUrlParser: true, useNewUrlParser: true,
@@ -43,13 +39,12 @@ export class GridFSBucketAdapter extends FilesAdapter {
_connect() { _connect() {
if (!this._connectionPromise) { if (!this._connectionPromise) {
this._connectionPromise = MongoClient.connect( this._connectionPromise = MongoClient.connect(this._databaseURI, this._mongoOptions).then(
this._databaseURI, client => {
this._mongoOptions this._client = client;
).then(client => { return client.db(client.s.options.dbName);
this._client = client; }
return client.db(client.s.options.dbName); );
});
} }
return this._connectionPromise; return this._connectionPromise;
} }
@@ -68,11 +63,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
if (this._encryptionKey !== null) { if (this._encryptionKey !== null) {
try { try {
const iv = crypto.randomBytes(16); const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv( const cipher = crypto.createCipheriv(this._algorithm, this._encryptionKey, iv);
this._algorithm,
this._encryptionKey,
iv
);
const encryptedResult = Buffer.concat([ const encryptedResult = Buffer.concat([
cipher.update(data), cipher.update(data),
cipher.final(), cipher.final(),
@@ -126,16 +117,9 @@ export class GridFSBucketAdapter extends FilesAdapter {
const authTag = data.slice(authTagLocation); const authTag = data.slice(authTagLocation);
const iv = data.slice(ivLocation, authTagLocation); const iv = data.slice(ivLocation, authTagLocation);
const encrypted = data.slice(0, ivLocation); const encrypted = data.slice(0, ivLocation);
const decipher = crypto.createDecipheriv( const decipher = crypto.createDecipheriv(this._algorithm, this._encryptionKey, iv);
this._algorithm,
this._encryptionKey,
iv
);
decipher.setAuthTag(authTag); decipher.setAuthTag(authTag);
const decrypted = Buffer.concat([ const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
decipher.update(encrypted),
decipher.final(),
]);
return resolve(decrypted); return resolve(decrypted);
} catch (err) { } catch (err) {
return reject(err); return reject(err);
@@ -160,10 +144,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
options.oldKey options.oldKey
); );
} else { } else {
oldKeyFileAdapter = new GridFSBucketAdapter( oldKeyFileAdapter = new GridFSBucketAdapter(this._databaseURI, this._mongoOptions);
this._databaseURI,
this._mongoOptions
);
} }
if (options.fileNames !== undefined) { if (options.fileNames !== undefined) {
fileNames = options.fileNames; fileNames = options.fileNames;
@@ -186,9 +167,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
this.createFile(fileName, plainTextData) this.createFile(fileName, plainTextData)
.then(() => { .then(() => {
fileNamesRotated.push(fileName); fileNamesRotated.push(fileName);
fileNamesNotRotated = fileNamesNotRotated.filter(function ( fileNamesNotRotated = fileNamesNotRotated.filter(function (value) {
value
) {
return value !== fileName; return value !== fileName;
}); });
fileNameIndex += 1; fileNameIndex += 1;
@@ -223,13 +202,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
} }
getFileLocation(config, filename) { getFileLocation(config, filename) {
return ( return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
config.mount +
'/files/' +
config.applicationId +
'/' +
encodeURIComponent(filename)
);
} }
async getMetadata(filename) { async getMetadata(filename) {

View File

@@ -30,13 +30,12 @@ export class GridStoreAdapter extends FilesAdapter {
_connect() { _connect() {
if (!this._connectionPromise) { if (!this._connectionPromise) {
this._connectionPromise = MongoClient.connect( this._connectionPromise = MongoClient.connect(this._databaseURI, this._mongoOptions).then(
this._databaseURI, client => {
this._mongoOptions this._client = client;
).then(client => { return client.db(client.s.options.dbName);
this._client = client; }
return client.db(client.s.options.dbName); );
});
} }
return this._connectionPromise; return this._connectionPromise;
} }
@@ -85,13 +84,7 @@ export class GridStoreAdapter extends FilesAdapter {
} }
getFileLocation(config, filename) { getFileLocation(config, filename) {
return ( return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
config.mount +
'/files/' +
config.applicationId +
'/' +
encodeURIComponent(filename)
);
} }
async handleFileStream(filename: string, req, res, contentType) { async handleFileStream(filename: string, req, res, contentType) {
@@ -152,14 +145,14 @@ function handleRangeRequest(stream, req, res, contentType) {
'Content-Type': contentType, 'Content-Type': contentType,
}); });
stream.seek(start, function() { stream.seek(start, function () {
// Get gridFile stream // Get gridFile stream
const gridFileStream = stream.stream(true); const gridFileStream = stream.stream(true);
let bufferAvail = 0; let bufferAvail = 0;
let remainingBytesToWrite = contentLength; let remainingBytesToWrite = contentLength;
let totalBytesWritten = 0; let totalBytesWritten = 0;
// Write to response // Write to response
gridFileStream.on('data', function(data) { gridFileStream.on('data', function (data) {
bufferAvail += data.length; bufferAvail += data.length;
if (bufferAvail > 0) { if (bufferAvail > 0) {
// slice returns the same buffer if overflowing // slice returns the same buffer if overflowing

View File

@@ -20,11 +20,7 @@ function configureTransports(options) {
{ {
filename: 'parse-server.info', filename: 'parse-server.info',
json: true, json: true,
format: format.combine( format: format.combine(format.timestamp(), format.splat(), format.json()),
format.timestamp(),
format.splat(),
format.json()
),
}, },
options options
) )
@@ -37,11 +33,7 @@ function configureTransports(options) {
{ {
filename: 'parse-server.err', filename: 'parse-server.err',
json: true, json: true,
format: format.combine( format: format.combine(format.timestamp(), format.splat(), format.json()),
format.timestamp(),
format.splat(),
format.json()
),
}, },
options, options,
{ level: 'error' } { level: 'error' }
@@ -120,9 +112,7 @@ export function addTransport(transport) {
export function removeTransport(transport) { export function removeTransport(transport) {
const matchingTransport = logger.transports.find(t1 => { const matchingTransport = logger.transports.find(t1 => {
return typeof transport === 'string' return typeof transport === 'string' ? t1.name === transport : t1 === transport;
? t1.name === transport
: t1 === transport;
}); });
if (matchingTransport) { if (matchingTransport) {

View File

@@ -28,8 +28,7 @@ export class WinstonLoggerAdapter extends LoggerAdapter {
options = {}; options = {};
} }
// defaults to 7 days prior // defaults to 7 days prior
const from = const from = options.from || new Date(Date.now() - 7 * MILLISECONDS_IN_A_DAY);
options.from || new Date(Date.now() - 7 * MILLISECONDS_IN_A_DAY);
const until = options.until || new Date(); const until = options.until || new Date();
const limit = options.size || 10; const limit = options.size || 10;
const order = options.order || 'desc'; const order = options.order || 'desc';

View File

@@ -15,17 +15,7 @@ export default class MongoCollection {
// idea. Or even if this behavior is a good idea. // idea. Or even if this behavior is a good idea.
find( find(
query, 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 // Support for Full Text Search - $text
if (keys && keys.$score) { if (keys && keys.$score) {
@@ -44,10 +34,7 @@ export default class MongoCollection {
explain, explain,
}).catch(error => { }).catch(error => {
// Check for "no geoindex" error // Check for "no geoindex" error
if ( if (error.code != 17007 && !error.message.match(/unable to find index for .geoNear/)) {
error.code != 17007 &&
!error.message.match(/unable to find index for .geoNear/)
) {
throw error; throw error;
} }
// Figure out what key needs an index // Figure out what key needs an index
@@ -88,17 +75,7 @@ export default class MongoCollection {
_rawFind( _rawFind(
query, 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, { let findOperation = this._mongoCollection.find(query, {
skip, skip,
@@ -113,9 +90,7 @@ export default class MongoCollection {
} }
if (caseInsensitive) { if (caseInsensitive) {
findOperation = findOperation.collation( findOperation = findOperation.collation(MongoCollection.caseInsensitiveCollation());
MongoCollection.caseInsensitiveCollation()
);
} }
if (maxTimeMS) { if (maxTimeMS) {

View File

@@ -41,9 +41,7 @@ function mongoFieldToParseSchemaField(type) {
const nonFieldSchemaKeys = ['_id', '_metadata', '_client_permissions']; const nonFieldSchemaKeys = ['_id', '_metadata', '_client_permissions'];
function mongoSchemaFieldsToParseSchemaFields(schema) { function mongoSchemaFieldsToParseSchemaFields(schema) {
var fieldNames = Object.keys(schema).filter( var fieldNames = Object.keys(schema).filter(key => nonFieldSchemaKeys.indexOf(key) === -1);
(key) => nonFieldSchemaKeys.indexOf(key) === -1
);
var response = fieldNames.reduce((obj, fieldName) => { var response = fieldNames.reduce((obj, fieldName) => {
obj[fieldName] = mongoFieldToParseSchemaField(schema[fieldName]); obj[fieldName] = mongoFieldToParseSchemaField(schema[fieldName]);
if ( if (
@@ -110,7 +108,7 @@ function mongoSchemaToParseSchema(mongoSchema) {
function _mongoSchemaQueryFromNameQuery(name: string, query) { function _mongoSchemaQueryFromNameQuery(name: string, query) {
const object = { _id: name }; const object = { _id: name };
if (query) { if (query) {
Object.keys(query).forEach((key) => { Object.keys(query).forEach(key => {
object[key] = query[key]; object[key] = query[key];
}); });
} }
@@ -156,15 +154,13 @@ class MongoSchemaCollection {
} }
_fetchAllSchemasFrom_SCHEMA() { _fetchAllSchemasFrom_SCHEMA() {
return this._collection return this._collection._rawFind({}).then(schemas => schemas.map(mongoSchemaToParseSchema));
._rawFind({})
.then((schemas) => schemas.map(mongoSchemaToParseSchema));
} }
_fetchOneSchemaFrom_SCHEMA(name: string) { _fetchOneSchemaFrom_SCHEMA(name: string) {
return this._collection return this._collection
._rawFind(_mongoSchemaQueryFromNameQuery(name), { limit: 1 }) ._rawFind(_mongoSchemaQueryFromNameQuery(name), { limit: 1 })
.then((results) => { .then(results => {
if (results.length === 1) { if (results.length === 1) {
return mongoSchemaToParseSchema(results[0]); return mongoSchemaToParseSchema(results[0]);
} else { } else {
@@ -175,22 +171,17 @@ class MongoSchemaCollection {
// Atomically find and delete an object based on query. // Atomically find and delete an object based on query.
findAndDeleteSchema(name: string) { findAndDeleteSchema(name: string) {
return this._collection._mongoCollection.findOneAndDelete( return this._collection._mongoCollection.findOneAndDelete(_mongoSchemaQueryFromNameQuery(name));
_mongoSchemaQueryFromNameQuery(name)
);
} }
insertSchema(schema: any) { insertSchema(schema: any) {
return this._collection return this._collection
.insertOne(schema) .insertOne(schema)
.then((result) => mongoSchemaToParseSchema(result.ops[0])) .then(result => mongoSchemaToParseSchema(result.ops[0]))
.catch((error) => { .catch(error => {
if (error.code === 11000) { if (error.code === 11000) {
//Mongo's duplicate key error //Mongo's duplicate key error
throw new Parse.Error( throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Class already exists.');
Parse.Error.DUPLICATE_VALUE,
'Class already exists.'
);
} else { } else {
throw error; throw error;
} }
@@ -198,17 +189,11 @@ class MongoSchemaCollection {
} }
updateSchema(name: string, update) { updateSchema(name: string, update) {
return this._collection.updateOne( return this._collection.updateOne(_mongoSchemaQueryFromNameQuery(name), update);
_mongoSchemaQueryFromNameQuery(name),
update
);
} }
upsertSchema(name: string, query: string, update) { upsertSchema(name: string, query: string, update) {
return this._collection.upsertOne( return this._collection.upsertOne(_mongoSchemaQueryFromNameQuery(name, query), update);
_mongoSchemaQueryFromNameQuery(name, query),
update
);
} }
// Add a field to the schema. If database does not support the field // 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) { addFieldIfNotExists(className: string, fieldName: string, fieldType: string) {
return this._fetchOneSchemaFrom_SCHEMA(className) return this._fetchOneSchemaFrom_SCHEMA(className)
.then( .then(
(schema) => { schema => {
// If a field with this name already exists, it will be handled elsewhere. // If a field with this name already exists, it will be handled elsewhere.
if (schema.fields[fieldName] != undefined) { if (schema.fields[fieldName] != undefined) {
return; return;
@@ -235,8 +220,7 @@ class MongoSchemaCollection {
// Make sure there are not other geopoint fields // Make sure there are not other geopoint fields
if ( if (
Object.keys(schema.fields).some( Object.keys(schema.fields).some(
(existingField) => existingField => schema.fields[existingField].type === 'GeoPoint'
schema.fields[existingField].type === 'GeoPoint'
) )
) { ) {
throw new Parse.Error( throw new Parse.Error(
@@ -247,7 +231,7 @@ class MongoSchemaCollection {
} }
return; return;
}, },
(error) => { error => {
// If error is undefined, the schema doesn't exist, and we can create the schema with the field. // 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 some other error, reject with it.
if (error === undefined) { if (error === undefined) {

View File

@@ -2,16 +2,8 @@
import MongoCollection from './MongoCollection'; import MongoCollection from './MongoCollection';
import MongoSchemaCollection from './MongoSchemaCollection'; import MongoSchemaCollection from './MongoSchemaCollection';
import { StorageAdapter } from '../StorageAdapter'; import { StorageAdapter } from '../StorageAdapter';
import type { import type { SchemaType, QueryType, StorageClass, QueryOptions } from '../StorageAdapter';
SchemaType, import { parse as parseUrl, format as formatUrl } from '../../../vendor/mongodbUrl';
QueryType,
StorageClass,
QueryOptions,
} from '../StorageAdapter';
import {
parse as parseUrl,
format as formatUrl,
} from '../../../vendor/mongodbUrl';
import { import {
parseObjectToMongoObjectForCreate, parseObjectToMongoObjectForCreate,
mongoObjectToParseObject, 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 // 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. // apps prefix, this will go very very badly. We should fix that somehow.
return ( return collection.collectionName.indexOf(mongoAdapter._collectionPrefix) == 0;
collection.collectionName.indexOf(mongoAdapter._collectionPrefix) == 0
);
}); });
}); });
}; };
@@ -85,16 +75,13 @@ const mongoSchemaFromFieldsAndClassNameAndCLP = (
for (const fieldName in fields) { for (const fieldName in fields) {
const { type, targetClass, ...fieldOptions } = fields[fieldName]; const { type, targetClass, ...fieldOptions } = fields[fieldName];
mongoObject[ mongoObject[fieldName] = MongoSchemaCollection.parseFieldTypeToMongoFieldType({
fieldName
] = MongoSchemaCollection.parseFieldTypeToMongoFieldType({
type, type,
targetClass, targetClass,
}); });
if (fieldOptions && Object.keys(fieldOptions).length > 0) { if (fieldOptions && Object.keys(fieldOptions).length > 0) {
mongoObject._metadata = mongoObject._metadata || {}; 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; mongoObject._metadata.fields_options[fieldName] = fieldOptions;
} }
} }
@@ -108,11 +95,7 @@ const mongoSchemaFromFieldsAndClassNameAndCLP = (
} }
} }
if ( if (indexes && typeof indexes === 'object' && Object.keys(indexes).length > 0) {
indexes &&
typeof indexes === 'object' &&
Object.keys(indexes).length > 0
) {
mongoObject._metadata = mongoObject._metadata || {}; mongoObject._metadata = mongoObject._metadata || {};
mongoObject._metadata.indexes = indexes; mongoObject._metadata.indexes = indexes;
} }
@@ -137,11 +120,7 @@ export class MongoStorageAdapter implements StorageAdapter {
_maxTimeMS: ?number; _maxTimeMS: ?number;
canSortOnJoinTables: boolean; canSortOnJoinTables: boolean;
constructor({ constructor({ uri = defaults.DefaultMongoURI, collectionPrefix = '', mongoOptions = {} }: any) {
uri = defaults.DefaultMongoURI,
collectionPrefix = '',
mongoOptions = {},
}: any) {
this._uri = uri; this._uri = uri;
this._collectionPrefix = collectionPrefix; this._collectionPrefix = collectionPrefix;
this._mongoOptions = mongoOptions; this._mongoOptions = mongoOptions;
@@ -225,9 +204,7 @@ export class MongoStorageAdapter implements StorageAdapter {
classExists(name: string) { classExists(name: string) {
return this.connect() return this.connect()
.then(() => { .then(() => {
return this.database return this.database.listCollections({ name: this._collectionPrefix + name }).toArray();
.listCollections({ name: this._collectionPrefix + name })
.toArray();
}) })
.then(collections => { .then(collections => {
return collections.length > 0; return collections.length > 0;
@@ -262,10 +239,7 @@ export class MongoStorageAdapter implements StorageAdapter {
Object.keys(submittedIndexes).forEach(name => { Object.keys(submittedIndexes).forEach(name => {
const field = submittedIndexes[name]; const field = submittedIndexes[name];
if (existingIndexes[name] && field.__op !== 'Delete') { if (existingIndexes[name] && field.__op !== 'Delete') {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_QUERY, `Index ${name} exists, cannot update.`);
Parse.Error.INVALID_QUERY,
`Index ${name} exists, cannot update.`
);
} }
if (!existingIndexes[name] && field.__op === 'Delete') { if (!existingIndexes[name] && field.__op === 'Delete') {
throw new Parse.Error( throw new Parse.Error(
@@ -349,26 +323,15 @@ export class MongoStorageAdapter implements StorageAdapter {
schema.indexes schema.indexes
); );
mongoObject._id = className; mongoObject._id = className;
return this.setIndexesWithSchemaFormat( return this.setIndexesWithSchemaFormat(className, schema.indexes, {}, schema.fields)
className,
schema.indexes,
{},
schema.fields
)
.then(() => this._schemaCollection()) .then(() => this._schemaCollection())
.then(schemaCollection => schemaCollection.insertSchema(mongoObject)) .then(schemaCollection => schemaCollection.insertSchema(mongoObject))
.catch(err => this.handleError(err)); .catch(err => this.handleError(err));
} }
addFieldIfNotExists( addFieldIfNotExists(className: string, fieldName: string, type: any): Promise<void> {
className: string,
fieldName: string,
type: any
): Promise<void> {
return this._schemaCollection() return this._schemaCollection()
.then(schemaCollection => .then(schemaCollection => schemaCollection.addFieldIfNotExists(className, fieldName, type))
schemaCollection.addFieldIfNotExists(className, fieldName, type)
)
.then(() => this.createIndexesIfNeeded(className, fieldName, type)) .then(() => this.createIndexesIfNeeded(className, fieldName, type))
.catch(err => this.handleError(err)); .catch(err => this.handleError(err));
} }
@@ -388,9 +351,7 @@ export class MongoStorageAdapter implements StorageAdapter {
}) })
// We've dropped the collection, now remove the _SCHEMA document // We've dropped the collection, now remove the _SCHEMA document
.then(() => this._schemaCollection()) .then(() => this._schemaCollection())
.then(schemaCollection => .then(schemaCollection => schemaCollection.findAndDeleteSchema(className))
schemaCollection.findAndDeleteSchema(className)
)
.catch(err => this.handleError(err)) .catch(err => this.handleError(err))
); );
} }
@@ -398,9 +359,7 @@ export class MongoStorageAdapter implements StorageAdapter {
deleteAllClasses(fast: boolean) { deleteAllClasses(fast: boolean) {
return storageAdapterAllCollections(this).then(collections => return storageAdapterAllCollections(this).then(collections =>
Promise.all( Promise.all(
collections.map(collection => collections.map(collection => (fast ? collection.deleteMany({}) : collection.drop()))
fast ? collection.deleteMany({}) : collection.drop()
)
) )
); );
} }
@@ -450,13 +409,9 @@ export class MongoStorageAdapter implements StorageAdapter {
}); });
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => .then(collection => collection.updateMany(collectionFilter, collectionUpdate))
collection.updateMany(collectionFilter, collectionUpdate)
)
.then(() => this._schemaCollection()) .then(() => this._schemaCollection())
.then(schemaCollection => .then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate))
schemaCollection.updateSchema(className, schemaUpdate)
)
.catch(err => this.handleError(err)); .catch(err => this.handleError(err));
} }
@@ -465,9 +420,7 @@ export class MongoStorageAdapter implements StorageAdapter {
// rejection reason are TBD. // rejection reason are TBD.
getAllClasses(): Promise<StorageClass[]> { getAllClasses(): Promise<StorageClass[]> {
return this._schemaCollection() return this._schemaCollection()
.then(schemasCollection => .then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA())
schemasCollection._fetchAllSchemasFrom_SCHEMA()
)
.catch(err => this.handleError(err)); .catch(err => this.handleError(err));
} }
@@ -476,31 +429,18 @@ export class MongoStorageAdapter implements StorageAdapter {
// undefined as the reason. // undefined as the reason.
getClass(className: string): Promise<StorageClass> { getClass(className: string): Promise<StorageClass> {
return this._schemaCollection() return this._schemaCollection()
.then(schemasCollection => .then(schemasCollection => schemasCollection._fetchOneSchemaFrom_SCHEMA(className))
schemasCollection._fetchOneSchemaFrom_SCHEMA(className)
)
.catch(err => this.handleError(err)); .catch(err => this.handleError(err));
} }
// TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema, // 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 // 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. // the schema only for the legacy mongo format. We'll figure that out later.
createObject( createObject(className: string, schema: SchemaType, object: any, transactionalSession: ?any) {
className: string,
schema: SchemaType,
object: any,
transactionalSession: ?any
) {
schema = convertParseSchemaToMongoSchema(schema); schema = convertParseSchemaToMongoSchema(schema);
const mongoObject = parseObjectToMongoObjectForCreate( const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema);
className,
object,
schema
);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => .then(collection => collection.insertOne(mongoObject, transactionalSession))
collection.insertOne(mongoObject, transactionalSession)
)
.catch(error => { .catch(error => {
if (error.code === 11000) { if (error.code === 11000) {
// Duplicate value // Duplicate value
@@ -510,9 +450,7 @@ export class MongoStorageAdapter implements StorageAdapter {
); );
err.underlyingError = error; err.underlyingError = error;
if (error.message) { if (error.message) {
const matches = error.message.match( const matches = error.message.match(/index:[\sa-zA-Z0-9_\-\.]+\$?([a-zA-Z_-]+)_1/);
/index:[\sa-zA-Z0-9_\-\.]+\$?([a-zA-Z_-]+)_1/
);
if (matches && Array.isArray(matches)) { if (matches && Array.isArray(matches)) {
err.userInfo = { duplicated_field: matches[1] }; err.userInfo = { duplicated_field: matches[1] };
} }
@@ -543,18 +481,12 @@ export class MongoStorageAdapter implements StorageAdapter {
.then( .then(
({ result }) => { ({ result }) => {
if (result.n === 0) { if (result.n === 0) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
Parse.Error.OBJECT_NOT_FOUND,
'Object not found.'
);
} }
return Promise.resolve(); return Promise.resolve();
}, },
() => { () => {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter 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 mongoUpdate = transformUpdate(className, update, schema);
const mongoWhere = transformWhere(className, query, schema); const mongoWhere = transformWhere(className, query, schema);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => .then(collection => collection.updateMany(mongoWhere, mongoUpdate, transactionalSession))
collection.updateMany(mongoWhere, mongoUpdate, transactionalSession)
)
.catch(err => this.handleError(err)); .catch(err => this.handleError(err));
} }
@@ -621,9 +551,7 @@ export class MongoStorageAdapter implements StorageAdapter {
const mongoUpdate = transformUpdate(className, update, schema); const mongoUpdate = transformUpdate(className, update, schema);
const mongoWhere = transformWhere(className, query, schema); const mongoWhere = transformWhere(className, query, schema);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => .then(collection => collection.upsertOne(mongoWhere, mongoUpdate, transactionalSession))
collection.upsertOne(mongoWhere, mongoUpdate, transactionalSession)
)
.catch(err => this.handleError(err)); .catch(err => this.handleError(err));
} }
@@ -632,16 +560,7 @@ export class MongoStorageAdapter implements StorageAdapter {
className: string, className: string,
schema: SchemaType, schema: SchemaType,
query: QueryType, query: QueryType,
{ { skip, limit, sort, keys, readPreference, hint, caseInsensitive, explain }: QueryOptions
skip,
limit,
sort,
keys,
readPreference,
hint,
caseInsensitive,
explain,
}: QueryOptions
): Promise<any> { ): Promise<any> {
schema = convertParseSchemaToMongoSchema(schema); schema = convertParseSchemaToMongoSchema(schema);
const mongoWhere = transformWhere(className, query, schema); const mongoWhere = transformWhere(className, query, schema);
@@ -689,9 +608,7 @@ export class MongoStorageAdapter implements StorageAdapter {
if (explain) { if (explain) {
return objects; return objects;
} }
return objects.map(object => return objects.map(object => mongoObjectToParseObject(className, object, schema));
mongoObjectToParseObject(className, object, schema)
);
}) })
.catch(err => this.handleError(err)); .catch(err => this.handleError(err));
} }
@@ -706,18 +623,14 @@ export class MongoStorageAdapter implements StorageAdapter {
): Promise<any> { ): Promise<any> {
schema = convertParseSchemaToMongoSchema(schema); schema = convertParseSchemaToMongoSchema(schema);
const indexCreationRequest = {}; const indexCreationRequest = {};
const mongoFieldNames = fieldNames.map(fieldName => const mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema));
transformKey(className, fieldName, schema)
);
mongoFieldNames.forEach(fieldName => { mongoFieldNames.forEach(fieldName => {
indexCreationRequest[fieldName] = indexCreationRequest[fieldName] = options.indexType !== undefined ? options.indexType : 1;
options.indexType !== undefined ? options.indexType : 1;
}); });
const defaultOptions: Object = { background: true, sparse: true }; const defaultOptions: Object = { background: true, sparse: true };
const indexNameOptions: Object = indexName ? { name: indexName } : {}; const indexNameOptions: Object = indexName ? { name: indexName } : {};
const ttlOptions: Object = const ttlOptions: Object = options.ttl !== undefined ? { expireAfterSeconds: options.ttl } : {};
options.ttl !== undefined ? { expireAfterSeconds: options.ttl } : {};
const caseInsensitiveOptions: Object = caseInsensitive const caseInsensitiveOptions: Object = caseInsensitive
? { collation: MongoCollection.caseInsensitiveCollation() } ? { collation: MongoCollection.caseInsensitiveCollation() }
: {}; : {};
@@ -732,10 +645,8 @@ export class MongoStorageAdapter implements StorageAdapter {
.then( .then(
collection => collection =>
new Promise((resolve, reject) => new Promise((resolve, reject) =>
collection._mongoCollection.createIndex( collection._mongoCollection.createIndex(indexCreationRequest, indexOptions, error =>
indexCreationRequest, error ? reject(error) : resolve()
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 // 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, // Way of determining if a field is nullable. Undefined doesn't count against uniqueness,
// which is why we use sparse indexes. // which is why we use sparse indexes.
ensureUniqueness( ensureUniqueness(className: string, schema: SchemaType, fieldNames: string[]) {
className: string,
schema: SchemaType,
fieldNames: string[]
) {
schema = convertParseSchemaToMongoSchema(schema); schema = convertParseSchemaToMongoSchema(schema);
const indexCreationRequest = {}; const indexCreationRequest = {};
const mongoFieldNames = fieldNames.map(fieldName => const mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema));
transformKey(className, fieldName, schema)
);
mongoFieldNames.forEach(fieldName => { mongoFieldNames.forEach(fieldName => {
indexCreationRequest[fieldName] = 1; indexCreationRequest[fieldName] = 1;
}); });
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => .then(collection => collection._ensureSparseUniqueIndexInBackground(indexCreationRequest))
collection._ensureSparseUniqueIndexInBackground(indexCreationRequest)
)
.catch(error => { .catch(error => {
if (error.code === 11000) { if (error.code === 11000) {
throw new Parse.Error( throw new Parse.Error(
@@ -808,23 +711,14 @@ export class MongoStorageAdapter implements StorageAdapter {
.catch(err => this.handleError(err)); .catch(err => this.handleError(err));
} }
distinct( distinct(className: string, schema: SchemaType, query: QueryType, fieldName: string) {
className: string,
schema: SchemaType,
query: QueryType,
fieldName: string
) {
schema = convertParseSchemaToMongoSchema(schema); schema = convertParseSchemaToMongoSchema(schema);
const isPointerField = const isPointerField = schema.fields[fieldName] && schema.fields[fieldName].type === 'Pointer';
schema.fields[fieldName] && schema.fields[fieldName].type === 'Pointer';
const transformField = transformKey(className, fieldName, schema); const transformField = transformKey(className, fieldName, schema);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => .then(collection =>
collection.distinct( collection.distinct(transformField, transformWhere(className, query, schema))
transformField,
transformWhere(className, query, schema)
)
) )
.then(objects => { .then(objects => {
objects = objects.filter(obj => obj != null); objects = objects.filter(obj => obj != null);
@@ -862,16 +756,10 @@ export class MongoStorageAdapter implements StorageAdapter {
stage.$match = this._parseAggregateArgs(schema, stage.$match); stage.$match = this._parseAggregateArgs(schema, stage.$match);
} }
if (stage.$project) { if (stage.$project) {
stage.$project = this._parseAggregateProjectArgs( stage.$project = this._parseAggregateProjectArgs(schema, stage.$project);
schema,
stage.$project
);
} }
if (stage.$geoNear && stage.$geoNear.query) { if (stage.$geoNear && stage.$geoNear.query) {
stage.$geoNear.query = this._parseAggregateArgs( stage.$geoNear.query = this._parseAggregateArgs(schema, stage.$geoNear.query);
schema,
stage.$geoNear.query
);
} }
return stage; return stage;
}); });
@@ -894,8 +782,7 @@ export class MongoStorageAdapter implements StorageAdapter {
if ( if (
result._id == null || result._id == null ||
result._id == undefined || result._id == undefined ||
(['object', 'string'].includes(typeof result._id) && (['object', 'string'].includes(typeof result._id) && _.isEmpty(result._id))
_.isEmpty(result._id))
) { ) {
result._id = null; result._id = null;
} }
@@ -905,11 +792,7 @@ export class MongoStorageAdapter implements StorageAdapter {
}); });
return results; return results;
}) })
.then(objects => .then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)))
objects.map(object =>
mongoObjectToParseObject(className, object, schema)
)
)
.catch(err => this.handleError(err)); .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. // Pass objects down to MongoDB...this is more than likely an $exists operator.
returnValue[`_p_${field}`] = pipeline[field]; returnValue[`_p_${field}`] = pipeline[field];
} else { } else {
returnValue[ returnValue[`_p_${field}`] = `${schema.fields[field].targetClass}$${pipeline[field]}`;
`_p_${field}`
] = `${schema.fields[field].targetClass}$${pipeline[field]}`;
} }
} else if ( } else if (schema.fields[field] && schema.fields[field].type === 'Date') {
schema.fields[field] &&
schema.fields[field].type === 'Date'
) {
returnValue[field] = this._convertToDate(pipeline[field]); returnValue[field] = this._convertToDate(pipeline[field]);
} else { } else {
returnValue[field] = this._parseAggregateArgs( returnValue[field] = this._parseAggregateArgs(schema, pipeline[field]);
schema,
pipeline[field]
);
} }
if (field === 'objectId') { if (field === 'objectId') {
@@ -1011,16 +886,11 @@ export class MongoStorageAdapter implements StorageAdapter {
// updatedAt or objectId and change it accordingly. // updatedAt or objectId and change it accordingly.
_parseAggregateGroupArgs(schema: any, pipeline: any): any { _parseAggregateGroupArgs(schema: any, pipeline: any): any {
if (Array.isArray(pipeline)) { if (Array.isArray(pipeline)) {
return pipeline.map(value => return pipeline.map(value => this._parseAggregateGroupArgs(schema, value));
this._parseAggregateGroupArgs(schema, value)
);
} else if (typeof pipeline === 'object') { } else if (typeof pipeline === 'object') {
const returnValue = {}; const returnValue = {};
for (const field in pipeline) { for (const field in pipeline) {
returnValue[field] = this._parseAggregateGroupArgs( returnValue[field] = this._parseAggregateGroupArgs(schema, pipeline[field]);
schema,
pipeline[field]
);
} }
return returnValue; return returnValue;
} else if (typeof pipeline === 'string') { } else if (typeof pipeline === 'string') {
@@ -1077,10 +947,7 @@ export class MongoStorageAdapter implements StorageAdapter {
case '': case '':
break; break;
default: default:
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Not supported read preference.');
Parse.Error.INVALID_QUERY,
'Not supported read preference.'
);
} }
return readPreference; return readPreference;
} }
@@ -1111,11 +978,7 @@ export class MongoStorageAdapter implements StorageAdapter {
return Promise.resolve(); return Promise.resolve();
} }
createTextIndexesIfNeeded( createTextIndexesIfNeeded(className: string, query: QueryType, schema: any): Promise<void> {
className: string,
query: QueryType,
schema: any
): Promise<void> {
for (const fieldName in query) { for (const fieldName in query) {
if (!query[fieldName] || !query[fieldName].$text) { if (!query[fieldName] || !query[fieldName].$text) {
continue; continue;

View File

@@ -20,27 +20,16 @@ const transformKey = (className, fieldName, schema) => {
return 'times_used'; return 'times_used';
} }
if ( if (schema.fields[fieldName] && schema.fields[fieldName].__type == 'Pointer') {
schema.fields[fieldName] &&
schema.fields[fieldName].__type == 'Pointer'
) {
fieldName = '_p_' + fieldName; fieldName = '_p_' + fieldName;
} else if ( } else if (schema.fields[fieldName] && schema.fields[fieldName].type == 'Pointer') {
schema.fields[fieldName] &&
schema.fields[fieldName].type == 'Pointer'
) {
fieldName = '_p_' + fieldName; fieldName = '_p_' + fieldName;
} }
return fieldName; return fieldName;
}; };
const transformKeyValueForUpdate = ( const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSchema) => {
className,
restKey,
restValue,
parseFormatSchema
) => {
// Check if the schema is known since it's a built-in field. // Check if the schema is known since it's a built-in field.
var key = restKey; var key = restKey;
var timeField = false; var timeField = false;
@@ -109,11 +98,8 @@ const transformKeyValueForUpdate = (
} }
if ( if (
(parseFormatSchema.fields[key] && (parseFormatSchema.fields[key] && parseFormatSchema.fields[key].type === 'Pointer') ||
parseFormatSchema.fields[key].type === 'Pointer') || (!parseFormatSchema.fields[key] && restValue && restValue.__type == 'Pointer')
(!parseFormatSchema.fields[key] &&
restValue &&
restValue.__type == 'Pointer')
) { ) {
key = '_p_' + key; key = '_p_' + key;
} }
@@ -179,7 +165,7 @@ const isAllValuesRegexOrNone = values => {
}; };
const isAnyValueRegex = values => { const isAnyValueRegex = values => {
return values.some(function(value) { return values.some(function (value) {
return isRegex(value); return isRegex(value);
}); });
}; };
@@ -292,9 +278,7 @@ function transformQueryKeyValue(className, key, value, schema, count = false) {
case '$nor': case '$nor':
return { return {
key: key, key: key,
value: value.map(subQuery => value: value.map(subQuery => transformWhere(className, subQuery, schema, count)),
transformWhere(className, subQuery, schema, count)
),
}; };
case 'lastUsed': case 'lastUsed':
if (valueAsDate(value)) { if (valueAsDate(value)) {
@@ -315,17 +299,13 @@ function transformQueryKeyValue(className, key, value, schema, count = false) {
} }
} }
const expectedTypeIsArray = const expectedTypeIsArray = schema && schema.fields[key] && schema.fields[key].type === 'Array';
schema && schema.fields[key] && schema.fields[key].type === 'Array';
const expectedTypeIsPointer = const expectedTypeIsPointer =
schema && schema.fields[key] && schema.fields[key].type === 'Pointer'; schema && schema.fields[key] && schema.fields[key].type === 'Pointer';
const field = schema && schema.fields[key]; const field = schema && schema.fields[key];
if ( if (expectedTypeIsPointer || (!schema && value && value.__type === 'Pointer')) {
expectedTypeIsPointer ||
(!schema && value && value.__type === 'Pointer')
) {
key = '_p_' + key; key = '_p_' + key;
} }
@@ -362,23 +342,13 @@ function transformQueryKeyValue(className, key, value, schema, count = false) {
function transformWhere(className, restWhere, schema, count = false) { function transformWhere(className, restWhere, schema, count = false) {
const mongoWhere = {}; const mongoWhere = {};
for (const restKey in restWhere) { for (const restKey in restWhere) {
const out = transformQueryKeyValue( const out = transformQueryKeyValue(className, restKey, restWhere[restKey], schema, count);
className,
restKey,
restWhere[restKey],
schema,
count
);
mongoWhere[out.key] = out.value; mongoWhere[out.key] = out.value;
} }
return mongoWhere; return mongoWhere;
} }
const parseObjectKeyValueToMongoObjectKeyValue = ( const parseObjectKeyValueToMongoObjectKeyValue = (restKey, restValue, schema) => {
restKey,
restValue,
schema
) => {
// Check if the schema is known since it's a built-in field. // Check if the schema is known since it's a built-in field.
let transformedValue; let transformedValue;
let coercedToDate; let coercedToDate;
@@ -388,37 +358,27 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
case 'expiresAt': case 'expiresAt':
transformedValue = transformTopLevelAtom(restValue); transformedValue = transformTopLevelAtom(restValue);
coercedToDate = coercedToDate =
typeof transformedValue === 'string' typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
? new Date(transformedValue)
: transformedValue;
return { key: 'expiresAt', value: coercedToDate }; return { key: 'expiresAt', value: coercedToDate };
case '_email_verify_token_expires_at': case '_email_verify_token_expires_at':
transformedValue = transformTopLevelAtom(restValue); transformedValue = transformTopLevelAtom(restValue);
coercedToDate = coercedToDate =
typeof transformedValue === 'string' typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
? new Date(transformedValue)
: transformedValue;
return { key: '_email_verify_token_expires_at', value: coercedToDate }; return { key: '_email_verify_token_expires_at', value: coercedToDate };
case '_account_lockout_expires_at': case '_account_lockout_expires_at':
transformedValue = transformTopLevelAtom(restValue); transformedValue = transformTopLevelAtom(restValue);
coercedToDate = coercedToDate =
typeof transformedValue === 'string' typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
? new Date(transformedValue)
: transformedValue;
return { key: '_account_lockout_expires_at', value: coercedToDate }; return { key: '_account_lockout_expires_at', value: coercedToDate };
case '_perishable_token_expires_at': case '_perishable_token_expires_at':
transformedValue = transformTopLevelAtom(restValue); transformedValue = transformTopLevelAtom(restValue);
coercedToDate = coercedToDate =
typeof transformedValue === 'string' typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
? new Date(transformedValue)
: transformedValue;
return { key: '_perishable_token_expires_at', value: coercedToDate }; return { key: '_perishable_token_expires_at', value: coercedToDate };
case '_password_changed_at': case '_password_changed_at':
transformedValue = transformTopLevelAtom(restValue); transformedValue = transformTopLevelAtom(restValue);
coercedToDate = coercedToDate =
typeof transformedValue === 'string' typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
? new Date(transformedValue)
: transformedValue;
return { key: '_password_changed_at', value: coercedToDate }; return { key: '_password_changed_at', value: coercedToDate };
case '_failed_login_count': case '_failed_login_count':
case '_rperm': case '_rperm':
@@ -432,10 +392,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
default: default:
// Auth data should have been transformed already // Auth data should have been transformed already
if (restKey.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { if (restKey.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'can only query on ' + restKey);
Parse.Error.INVALID_KEY_NAME,
'can only query on ' + restKey
);
} }
// Trust that the auth data has been transformed and save it directly // Trust that the auth data has been transformed and save it directly
if (restKey.match(/^_auth_data_[a-zA-Z0-9_]+$/)) { if (restKey.match(/^_auth_data_[a-zA-Z0-9_]+$/)) {
@@ -473,9 +430,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
} }
// Handle normal objects by recursing // Handle normal objects by recursing
if ( if (Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))) {
Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))
) {
throw new Parse.Error( throw new Parse.Error(
Parse.Error.INVALID_NESTED_KEY, Parse.Error.INVALID_NESTED_KEY,
"Nested keys should not contain the '$' or '.' characters" "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 // Use the legacy mongo format for createdAt and updatedAt
if (mongoCreate.createdAt) { if (mongoCreate.createdAt) {
mongoCreate._created_at = new Date( mongoCreate._created_at = new Date(mongoCreate.createdAt.iso || mongoCreate.createdAt);
mongoCreate.createdAt.iso || mongoCreate.createdAt
);
delete mongoCreate.createdAt; delete mongoCreate.createdAt;
} }
if (mongoCreate.updatedAt) { if (mongoCreate.updatedAt) {
mongoCreate._updated_at = new Date( mongoCreate._updated_at = new Date(mongoCreate.updatedAt.iso || mongoCreate.updatedAt);
mongoCreate.updatedAt.iso || mongoCreate.updatedAt
);
delete mongoCreate.updatedAt; delete mongoCreate.updatedAt;
} }
@@ -593,22 +544,14 @@ function CannotTransform() {}
const transformInteriorAtom = atom => { const transformInteriorAtom = atom => {
// TODO: check validity harder for the __type-defined types // TODO: check validity harder for the __type-defined types
if ( if (typeof atom === 'object' && atom && !(atom instanceof Date) && atom.__type === 'Pointer') {
typeof atom === 'object' &&
atom &&
!(atom instanceof Date) &&
atom.__type === 'Pointer'
) {
return { return {
__type: 'Pointer', __type: 'Pointer',
className: atom.className, className: atom.className,
objectId: atom.objectId, objectId: atom.objectId,
}; };
} else if (typeof atom === 'function' || typeof atom === 'symbol') { } else if (typeof atom === 'function' || typeof atom === 'symbol') {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `cannot transform value: ${atom}`);
Parse.Error.INVALID_JSON,
`cannot transform value: ${atom}`
);
} else if (DateCoder.isValidJSON(atom)) { } else if (DateCoder.isValidJSON(atom)) {
return DateCoder.JSONToDatabase(atom); return DateCoder.JSONToDatabase(atom);
} else if (BytesCoder.isValidJSON(atom)) { } else if (BytesCoder.isValidJSON(atom)) {
@@ -640,10 +583,7 @@ function transformTopLevelAtom(atom, field) {
return atom; return atom;
case 'symbol': case 'symbol':
case 'function': case 'function':
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `cannot transform value: ${atom}`);
Parse.Error.INVALID_JSON,
`cannot transform value: ${atom}`
);
case 'object': case 'object':
if (atom instanceof Date) { if (atom instanceof Date) {
// Technically dates are not rest format, but, it seems pretty // 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) { if (typeof constraint !== 'object' || !constraint) {
return CannotTransform; return CannotTransform;
} }
const transformFunction = inArray const transformFunction = inArray ? transformInteriorAtom : transformTopLevelAtom;
? transformInteriorAtom
: transformTopLevelAtom;
const transformer = atom => { const transformer = atom => {
const result = transformFunction(atom, field); const result = transformFunction(atom, field);
if (result === CannotTransform) { if (result === CannotTransform) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `bad atom: ${JSON.stringify(atom)}`);
Parse.Error.INVALID_JSON,
`bad atom: ${JSON.stringify(atom)}`
);
} }
return result; return result;
}; };
@@ -839,9 +774,7 @@ function transformConstraint(constraint, field, count = false) {
// This is a hack so that: // This is a hack so that:
// $regex is handled before $options // $regex is handled before $options
// $nearSphere is handled before $maxDistance // $nearSphere is handled before $maxDistance
var keys = Object.keys(constraint) var keys = Object.keys(constraint).sort().reverse();
.sort()
.reverse();
var answer = {}; var answer = {};
for (var key of keys) { for (var key of keys) {
switch (key) { switch (key) {
@@ -892,10 +825,7 @@ function transformConstraint(constraint, field, count = false) {
case '$nin': { case '$nin': {
const arr = constraint[key]; const arr = constraint[key];
if (!(arr instanceof Array)) { if (!(arr instanceof Array)) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value');
Parse.Error.INVALID_JSON,
'bad ' + key + ' value'
);
} }
answer[key] = _.flatMap(arr, value => { answer[key] = _.flatMap(arr, value => {
return (atom => { return (atom => {
@@ -911,10 +841,7 @@ function transformConstraint(constraint, field, count = false) {
case '$all': { case '$all': {
const arr = constraint[key]; const arr = constraint[key];
if (!(arr instanceof Array)) { if (!(arr instanceof Array)) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value');
Parse.Error.INVALID_JSON,
'bad ' + key + ' value'
);
} }
answer[key] = arr.map(transformInteriorAtom); answer[key] = arr.map(transformInteriorAtom);
@@ -939,10 +866,7 @@ function transformConstraint(constraint, field, count = false) {
case '$containedBy': { case '$containedBy': {
const arr = constraint[key]; const arr = constraint[key];
if (!(arr instanceof Array)) { if (!(arr instanceof Array)) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $containedBy: should be an array`);
Parse.Error.INVALID_JSON,
`bad $containedBy: should be an array`
);
} }
answer.$elemMatch = { answer.$elemMatch = {
$nin: arr.map(transformer), $nin: arr.map(transformer),
@@ -956,33 +880,21 @@ function transformConstraint(constraint, field, count = false) {
case '$text': { case '$text': {
const search = constraint[key].$search; const search = constraint[key].$search;
if (typeof search !== 'object') { if (typeof search !== 'object') {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $search, should be object`);
Parse.Error.INVALID_JSON,
`bad $text: $search, should be object`
);
} }
if (!search.$term || typeof search.$term !== 'string') { if (!search.$term || typeof search.$term !== 'string') {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $term, should be string`);
Parse.Error.INVALID_JSON,
`bad $text: $term, should be string`
);
} else { } else {
answer[key] = { answer[key] = {
$search: search.$term, $search: search.$term,
}; };
} }
if (search.$language && typeof search.$language !== 'string') { if (search.$language && typeof search.$language !== 'string') {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $language, should be string`);
Parse.Error.INVALID_JSON,
`bad $text: $language, should be string`
);
} else if (search.$language) { } else if (search.$language) {
answer[key].$language = search.$language; answer[key].$language = search.$language;
} }
if ( if (search.$caseSensitive && typeof search.$caseSensitive !== 'boolean') {
search.$caseSensitive &&
typeof search.$caseSensitive !== 'boolean'
) {
throw new Parse.Error( throw new Parse.Error(
Parse.Error.INVALID_JSON, Parse.Error.INVALID_JSON,
`bad $text: $caseSensitive, should be boolean` `bad $text: $caseSensitive, should be boolean`
@@ -990,10 +902,7 @@ function transformConstraint(constraint, field, count = false) {
} else if (search.$caseSensitive) { } else if (search.$caseSensitive) {
answer[key].$caseSensitive = search.$caseSensitive; answer[key].$caseSensitive = search.$caseSensitive;
} }
if ( if (search.$diacriticSensitive && typeof search.$diacriticSensitive !== 'boolean') {
search.$diacriticSensitive &&
typeof search.$diacriticSensitive !== 'boolean'
) {
throw new Parse.Error( throw new Parse.Error(
Parse.Error.INVALID_JSON, Parse.Error.INVALID_JSON,
`bad $text: $diacriticSensitive, should be boolean` `bad $text: $diacriticSensitive, should be boolean`
@@ -1007,10 +916,7 @@ function transformConstraint(constraint, field, count = false) {
const point = constraint[key]; const point = constraint[key];
if (count) { if (count) {
answer.$geoWithin = { answer.$geoWithin = {
$centerSphere: [ $centerSphere: [[point.longitude, point.latitude], constraint.$maxDistance],
[point.longitude, point.latitude],
constraint.$maxDistance,
],
}; };
} else { } else {
answer[key] = [point.longitude, point.latitude]; answer[key] = [point.longitude, point.latitude];
@@ -1046,10 +952,7 @@ function transformConstraint(constraint, field, count = false) {
case '$within': case '$within':
var box = constraint[key]['$box']; var box = constraint[key]['$box'];
if (!box || box.length != 2) { if (!box || box.length != 2) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, 'malformatted $within arg');
Parse.Error.INVALID_JSON,
'malformatted $within arg'
);
} }
answer[key] = { answer[key] = {
$box: [ $box: [
@@ -1092,10 +995,7 @@ function transformConstraint(constraint, field, count = false) {
return point; return point;
} }
if (!GeoPointCoder.isValidJSON(point)) { if (!GeoPointCoder.isValidJSON(point)) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value');
Parse.Error.INVALID_JSON,
'bad $geoWithin value'
);
} else { } else {
Parse.GeoPoint._validate(point.latitude, point.longitude); Parse.GeoPoint._validate(point.latitude, point.longitude);
} }
@@ -1156,10 +1056,7 @@ function transformConstraint(constraint, field, count = false) {
} }
default: default:
if (key.match(/^\$+/)) { if (key.match(/^\$+/)) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad constraint: ' + key);
Parse.Error.INVALID_JSON,
'bad constraint: ' + key
);
} }
return CannotTransform; return CannotTransform;
} }
@@ -1188,10 +1085,7 @@ function transformUpdateOperator({ __op, amount, objects }, flatten) {
case 'Increment': case 'Increment':
if (typeof amount !== 'number') { if (typeof amount !== 'number') {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, 'incrementing must provide a number');
Parse.Error.INVALID_JSON,
'incrementing must provide a number'
);
} }
if (flatten) { if (flatten) {
return amount; return amount;
@@ -1202,10 +1096,7 @@ function transformUpdateOperator({ __op, amount, objects }, flatten) {
case 'Add': case 'Add':
case 'AddUnique': case 'AddUnique':
if (!(objects instanceof Array)) { if (!(objects instanceof Array)) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
Parse.Error.INVALID_JSON,
'objects to add must be an array'
);
} }
var toAdd = objects.map(transformInteriorAtom); var toAdd = objects.map(transformInteriorAtom);
if (flatten) { if (flatten) {
@@ -1220,10 +1111,7 @@ function transformUpdateOperator({ __op, amount, objects }, flatten) {
case 'Remove': case 'Remove':
if (!(objects instanceof Array)) { if (!(objects instanceof Array)) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to remove must be an array');
Parse.Error.INVALID_JSON,
'objects to remove must be an array'
);
} }
var toRemove = objects.map(transformInteriorAtom); var toRemove = objects.map(transformInteriorAtom);
if (flatten) { if (flatten) {
@@ -1379,15 +1267,11 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
break; break;
case 'updatedAt': case 'updatedAt':
case '_updated_at': case '_updated_at':
restObject['updatedAt'] = Parse._encode( restObject['updatedAt'] = Parse._encode(new Date(mongoObject[key])).iso;
new Date(mongoObject[key])
).iso;
break; break;
case 'createdAt': case 'createdAt':
case '_created_at': case '_created_at':
restObject['createdAt'] = Parse._encode( restObject['createdAt'] = Parse._encode(new Date(mongoObject[key])).iso;
new Date(mongoObject[key])
).iso;
break; break;
case 'expiresAt': case 'expiresAt':
case '_expiresAt': case '_expiresAt':
@@ -1395,9 +1279,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
break; break;
case 'lastUsed': case 'lastUsed':
case '_last_used': case '_last_used':
restObject['lastUsed'] = Parse._encode( restObject['lastUsed'] = Parse._encode(new Date(mongoObject[key])).iso;
new Date(mongoObject[key])
).iso;
break; break;
case 'timesUsed': case 'timesUsed':
case 'times_used': case 'times_used':
@@ -1445,11 +1327,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
if (mongoObject[key] === null) { if (mongoObject[key] === null) {
break; break;
} }
restObject[newKey] = transformPointerString( restObject[newKey] = transformPointerString(schema, newKey, mongoObject[key]);
schema,
newKey,
mongoObject[key]
);
break; break;
} else if (key[0] == '_' && key != '__type') { } else if (key[0] == '_' && key != '__type') {
throw 'bad key in untransform: ' + key; throw 'bad key in untransform: ' + key;
@@ -1488,9 +1366,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
break; break;
} }
} }
restObject[key] = nestedMongoObjectToNestedParseObject( restObject[key] = nestedMongoObjectToNestedParseObject(mongoObject[key]);
mongoObject[key]
);
} }
} }
@@ -1518,16 +1394,12 @@ var DateCoder = {
}, },
isValidJSON(value) { isValidJSON(value) {
return ( return typeof value === 'object' && value !== null && value.__type === 'Date';
typeof value === 'object' && value !== null && value.__type === 'Date'
);
}, },
}; };
var BytesCoder = { var BytesCoder = {
base64Pattern: new RegExp( base64Pattern: new RegExp('^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'),
'^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'
),
isBase64Value(object) { isBase64Value(object) {
if (typeof object !== 'string') { if (typeof object !== 'string') {
return false; return false;
@@ -1557,9 +1429,7 @@ var BytesCoder = {
}, },
isValidJSON(value) { isValidJSON(value) {
return ( return typeof value === 'object' && value !== null && value.__type === 'Bytes';
typeof value === 'object' && value !== null && value.__type === 'Bytes'
);
}, },
}; };
@@ -1581,9 +1451,7 @@ var GeoPointCoder = {
}, },
isValidJSON(value) { isValidJSON(value) {
return ( return typeof value === 'object' && value !== null && value.__type === 'GeoPoint';
typeof value === 'object' && value !== null && value.__type === 'GeoPoint'
);
}, },
}; };
@@ -1648,9 +1516,7 @@ var PolygonCoder = {
}, },
isValidJSON(value) { isValidJSON(value) {
return ( return typeof value === 'object' && value !== null && value.__type === 'Polygon';
typeof value === 'object' && value !== null && value.__type === 'Polygon'
);
}, },
}; };
@@ -1671,9 +1537,7 @@ var FileCoder = {
}, },
isValidJSON(value) { isValidJSON(value) {
return ( return typeof value === 'object' && value !== null && value.__type === 'File';
typeof value === 'object' && value !== null && value.__type === 'File'
);
}, },
}; };

View File

@@ -9,9 +9,7 @@ function getDatabaseOptionsFromURI(uri) {
databaseOptions.host = parsedURI.hostname || 'localhost'; databaseOptions.host = parsedURI.hostname || 'localhost';
databaseOptions.port = parsedURI.port ? parseInt(parsedURI.port) : 5432; databaseOptions.port = parsedURI.port ? parseInt(parsedURI.port) : 5432;
databaseOptions.database = parsedURI.pathname databaseOptions.database = parsedURI.pathname ? parsedURI.pathname.substr(1) : undefined;
? parsedURI.pathname.substr(1)
: undefined;
databaseOptions.user = authParts.length > 0 ? authParts[0] : ''; databaseOptions.user = authParts.length > 0 ? authParts[0] : '';
databaseOptions.password = authParts.length > 1 ? authParts[1] : ''; databaseOptions.password = authParts.length > 1 ? authParts[1] : '';
@@ -55,14 +53,11 @@ function getDatabaseOptionsFromURI(uri) {
} }
databaseOptions.binary = databaseOptions.binary =
queryParams.binary && queryParams.binary.toLowerCase() === 'true' queryParams.binary && queryParams.binary.toLowerCase() === 'true' ? true : false;
? true
: false;
databaseOptions.client_encoding = queryParams.client_encoding; databaseOptions.client_encoding = queryParams.client_encoding;
databaseOptions.application_name = queryParams.application_name; databaseOptions.application_name = queryParams.application_name;
databaseOptions.fallback_application_name = databaseOptions.fallback_application_name = queryParams.fallback_application_name;
queryParams.fallback_application_name;
if (queryParams.poolSize) { if (queryParams.poolSize) {
databaseOptions.poolSize = parseInt(queryParams.poolSize) || 10; databaseOptions.poolSize = parseInt(queryParams.poolSize) || 10;
@@ -77,8 +72,7 @@ function getDatabaseOptionsFromURI(uri) {
databaseOptions.idleTimeoutMillis = parseInt(queryParams.idleTimeoutMillis); databaseOptions.idleTimeoutMillis = parseInt(queryParams.idleTimeoutMillis);
} }
if (queryParams.keepAlive) { if (queryParams.keepAlive) {
databaseOptions.keepAlive = databaseOptions.keepAlive = queryParams.keepAlive.toLowerCase() === 'true' ? true : false;
queryParams.keepAlive.toLowerCase() === 'true' ? true : false;
} }
return databaseOptions; return databaseOptions;

View File

@@ -253,12 +253,7 @@ interface WhereClause {
sorts: Array<any>; sorts: Array<any>;
} }
const buildWhereClause = ({ const buildWhereClause = ({ schema, query, index, caseInsensitive }): WhereClause => {
schema,
query,
index,
caseInsensitive,
}): WhereClause => {
const patterns = []; const patterns = [];
let values = []; let values = [];
const sorts = []; const sorts = [];
@@ -266,9 +261,7 @@ const buildWhereClause = ({
schema = toPostgresSchema(schema); schema = toPostgresSchema(schema);
for (const fieldName in query) { for (const fieldName in query) {
const isArrayField = const isArrayField =
schema.fields && schema.fields && schema.fields[fieldName] && schema.fields[fieldName].type === 'Array';
schema.fields[fieldName] &&
schema.fields[fieldName].type === 'Array';
const initialPatternsLength = patterns.length; const initialPatternsLength = patterns.length;
const fieldValue = query[fieldName]; const fieldValue = query[fieldName];
@@ -284,10 +277,7 @@ const buildWhereClause = ({
if (authDataMatch) { if (authDataMatch) {
// TODO: Handle querying by _auth_data_provider, authData is stored in authData field // TODO: Handle querying by _auth_data_provider, authData is stored in authData field
continue; continue;
} else if ( } else if (caseInsensitive && (fieldName === 'username' || fieldName === 'email')) {
caseInsensitive &&
(fieldName === 'username' || fieldName === 'email')
) {
patterns.push(`LOWER($${index}:name) = LOWER($${index + 1})`); patterns.push(`LOWER($${index}:name) = LOWER($${index + 1})`);
values.push(fieldName, fieldValue); values.push(fieldName, fieldValue);
index += 2; index += 2;
@@ -324,10 +314,7 @@ const buildWhereClause = ({
} else if (typeof fieldValue === 'boolean') { } else if (typeof fieldValue === 'boolean') {
patterns.push(`$${index}:name = $${index + 1}`); patterns.push(`$${index}:name = $${index + 1}`);
// Can't cast boolean to double precision // Can't cast boolean to double precision
if ( if (schema.fields[fieldName] && schema.fields[fieldName].type === 'Number') {
schema.fields[fieldName] &&
schema.fields[fieldName].type === 'Number'
) {
// Should always return zero results // Should always return zero results
const MAX_INT_PLUS_ONE = 9223372036854775808; const MAX_INT_PLUS_ONE = 9223372036854775808;
values.push(fieldName, MAX_INT_PLUS_ONE); values.push(fieldName, MAX_INT_PLUS_ONE);
@@ -377,9 +364,7 @@ const buildWhereClause = ({
// if not null, we need to manually exclude null // if not null, we need to manually exclude null
if (fieldValue.$ne.__type === 'GeoPoint') { if (fieldValue.$ne.__type === 'GeoPoint') {
patterns.push( patterns.push(
`($${index}:name <> POINT($${index + 1}, $${ `($${index}:name <> POINT($${index + 1}, $${index + 2}) OR $${index}:name IS NULL)`
index + 2
}) OR $${index}:name IS NULL)`
); );
} else { } else {
if (fieldName.indexOf('.') >= 0) { if (fieldName.indexOf('.') >= 0) {
@@ -388,9 +373,7 @@ const buildWhereClause = ({
`(${constraintFieldName} <> $${index} OR ${constraintFieldName} IS NULL)` `(${constraintFieldName} <> $${index} OR ${constraintFieldName} IS NULL)`
); );
} else { } else {
patterns.push( patterns.push(`($${index}:name <> $${index + 1} OR $${index}:name IS NULL)`);
`($${index}:name <> $${index + 1} OR $${index}:name IS NULL)`
);
} }
} }
} }
@@ -421,8 +404,7 @@ const buildWhereClause = ({
} }
} }
} }
const isInOrNin = const isInOrNin = Array.isArray(fieldValue.$in) || Array.isArray(fieldValue.$nin);
Array.isArray(fieldValue.$in) || Array.isArray(fieldValue.$nin);
if ( if (
Array.isArray(fieldValue.$in) && Array.isArray(fieldValue.$in) &&
isArrayField && isArrayField &&
@@ -441,9 +423,7 @@ const buildWhereClause = ({
} }
}); });
if (allowNull) { if (allowNull) {
patterns.push( patterns.push(`($${index}:name IS NULL OR $${index}:name && ARRAY[${inPatterns.join()}])`);
`($${index}:name IS NULL OR $${index}:name && ARRAY[${inPatterns.join()}])`
);
} else { } else {
patterns.push(`$${index}:name && ARRAY[${inPatterns.join()}]`); patterns.push(`$${index}:name && ARRAY[${inPatterns.join()}]`);
} }
@@ -453,9 +433,7 @@ const buildWhereClause = ({
const not = notIn ? ' NOT ' : ''; const not = notIn ? ' NOT ' : '';
if (baseArray.length > 0) { if (baseArray.length > 0) {
if (isArrayField) { if (isArrayField) {
patterns.push( patterns.push(`${not} array_contains($${index}:name, $${index + 1})`);
`${not} array_contains($${index}:name, $${index + 1})`
);
values.push(fieldName, JSON.stringify(baseArray)); values.push(fieldName, JSON.stringify(baseArray));
index += 2; index += 2;
} else { } else {
@@ -518,13 +496,9 @@ const buildWhereClause = ({
const value = processRegexPattern(fieldValue.$all[i].$regex); const value = processRegexPattern(fieldValue.$all[i].$regex);
fieldValue.$all[i] = value.substring(1) + '%'; fieldValue.$all[i] = value.substring(1) + '%';
} }
patterns.push( patterns.push(`array_contains_all_regex($${index}:name, $${index + 1}::jsonb)`);
`array_contains_all_regex($${index}:name, $${index + 1}::jsonb)`
);
} else { } else {
patterns.push( patterns.push(`array_contains_all($${index}:name, $${index + 1}::jsonb)`);
`array_contains_all($${index}:name, $${index + 1}::jsonb)`
);
} }
values.push(fieldName, JSON.stringify(fieldValue.$all)); values.push(fieldName, JSON.stringify(fieldValue.$all));
index += 2; index += 2;
@@ -549,10 +523,7 @@ const buildWhereClause = ({
if (fieldValue.$containedBy) { if (fieldValue.$containedBy) {
const arr = fieldValue.$containedBy; const arr = fieldValue.$containedBy;
if (!(arr instanceof Array)) { if (!(arr instanceof Array)) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $containedBy: should be an array`);
Parse.Error.INVALID_JSON,
`bad $containedBy: should be an array`
);
} }
patterns.push(`$${index}:name <@ $${index + 1}::jsonb`); patterns.push(`$${index}:name <@ $${index + 1}::jsonb`);
@@ -564,22 +535,13 @@ const buildWhereClause = ({
const search = fieldValue.$text.$search; const search = fieldValue.$text.$search;
let language = 'english'; let language = 'english';
if (typeof search !== 'object') { if (typeof search !== 'object') {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $search, should be object`);
Parse.Error.INVALID_JSON,
`bad $text: $search, should be object`
);
} }
if (!search.$term || typeof search.$term !== 'string') { if (!search.$term || typeof search.$term !== 'string') {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $term, should be string`);
Parse.Error.INVALID_JSON,
`bad $text: $term, should be string`
);
} }
if (search.$language && typeof search.$language !== 'string') { if (search.$language && typeof search.$language !== 'string') {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $language, should be string`);
Parse.Error.INVALID_JSON,
`bad $text: $language, should be string`
);
} else if (search.$language) { } else if (search.$language) {
language = 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.` `bad $text: $caseSensitive not supported, please use $regex or create a separate lower case column.`
); );
} }
if ( if (search.$diacriticSensitive && typeof search.$diacriticSensitive !== 'boolean') {
search.$diacriticSensitive &&
typeof search.$diacriticSensitive !== 'boolean'
) {
throw new Parse.Error( throw new Parse.Error(
Parse.Error.INVALID_JSON, Parse.Error.INVALID_JSON,
`bad $text: $diacriticSensitive, should be boolean` `bad $text: $diacriticSensitive, should be boolean`
@@ -609,9 +568,7 @@ const buildWhereClause = ({
); );
} }
patterns.push( patterns.push(
`to_tsvector($${index}, $${index + 1}:name) @@ to_tsquery($${ `to_tsvector($${index}, $${index + 1}:name) @@ to_tsquery($${index + 2}, $${index + 3})`
index + 2
}, $${index + 3})`
); );
values.push(language, fieldName, language, search.$term); values.push(language, fieldName, language, search.$term);
index += 4; index += 4;
@@ -716,10 +673,7 @@ const buildWhereClause = ({
return `(${point[0]}, ${point[1]})`; return `(${point[0]}, ${point[1]})`;
} }
if (typeof point !== 'object' || point.__type !== 'GeoPoint') { if (typeof point !== 'object' || point.__type !== 'GeoPoint') {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value');
Parse.Error.INVALID_JSON,
'bad $geoWithin value'
);
} else { } else {
Parse.GeoPoint._validate(point.latitude, point.longitude); Parse.GeoPoint._validate(point.latitude, point.longitude);
} }
@@ -830,9 +784,7 @@ const buildWhereClause = ({
if (initialPatternsLength === patterns.length) { if (initialPatternsLength === patterns.length) {
throw new Parse.Error( throw new Parse.Error(
Parse.Error.OPERATION_FORBIDDEN, Parse.Error.OPERATION_FORBIDDEN,
`Postgres doesn't support this query type yet ${JSON.stringify( `Postgres doesn't support this query type yet ${JSON.stringify(fieldValue)}`
fieldValue
)}`
); );
} }
} }
@@ -903,12 +855,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
const self = this; const self = this;
await this._client.task('set-class-level-permissions', async t => { await this._client.task('set-class-level-permissions', async t => {
await self._ensureSchemaCollectionExists(t); await self._ensureSchemaCollectionExists(t);
const values = [ const values = [className, 'schema', 'classLevelPermissions', JSON.stringify(CLPs)];
className,
'schema',
'classLevelPermissions',
JSON.stringify(CLPs),
];
await t.none( await t.none(
`UPDATE "_SCHEMA" SET $2:name = json_object_set_key($2:name, $3::text, $4::jsonb) WHERE "className" = $1`, `UPDATE "_SCHEMA" SET $2:name = json_object_set_key($2:name, $3::text, $4::jsonb) WHERE "className" = $1`,
values values
@@ -936,10 +883,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
Object.keys(submittedIndexes).forEach(name => { Object.keys(submittedIndexes).forEach(name => {
const field = submittedIndexes[name]; const field = submittedIndexes[name];
if (existingIndexes[name] && field.__op !== 'Delete') { if (existingIndexes[name] && field.__op !== 'Delete') {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_QUERY, `Index ${name} exists, cannot update.`);
Parse.Error.INVALID_QUERY,
`Index ${name} exists, cannot update.`
);
} }
if (!existingIndexes[name] && field.__op === 'Delete') { if (!existingIndexes[name] && field.__op === 'Delete') {
throw new Parse.Error( throw new Parse.Error(
@@ -990,24 +934,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
'INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)', 'INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)',
{ className, schema } { className, schema }
); );
await this.setIndexesWithSchemaFormat( await this.setIndexesWithSchemaFormat(className, schema.indexes, {}, schema.fields, t);
className,
schema.indexes,
{},
schema.fields,
t
);
return toParseSchema(schema); return toParseSchema(schema);
}) })
.catch(err => { .catch(err => {
if ( if (err.code === PostgresUniqueIndexViolationError && err.detail.includes(className)) {
err.code === PostgresUniqueIndexViolationError && throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, `Class ${className} already exists.`);
err.detail.includes(className)
) {
throw new Parse.Error(
Parse.Error.DUPLICATE_VALUE,
`Class ${className} already exists.`
);
} }
throw err; throw err;
}); });
@@ -1093,24 +1025,14 @@ export class PostgresStorageAdapter implements StorageAdapter {
const newColumns = Object.keys(schema.fields) const newColumns = Object.keys(schema.fields)
.filter(item => columns.indexOf(item) === -1) .filter(item => columns.indexOf(item) === -1)
.map(fieldName => .map(fieldName =>
self.addFieldIfNotExists( self.addFieldIfNotExists(className, fieldName, schema.fields[fieldName], t)
className,
fieldName,
schema.fields[fieldName],
t
)
); );
await t.batch(newColumns); await t.batch(newColumns);
}); });
} }
async addFieldIfNotExists( async addFieldIfNotExists(className: string, fieldName: string, type: any, conn: any) {
className: string,
fieldName: string,
type: any,
conn: any
) {
// TODO: Must be revised for invalid logic... // TODO: Must be revised for invalid logic...
debug('addFieldIfNotExists', { className, fieldName, type }); debug('addFieldIfNotExists', { className, fieldName, type });
conn = conn || this._client; conn = conn || this._client;
@@ -1128,11 +1050,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
); );
} catch (error) { } catch (error) {
if (error.code === PostgresRelationDoesNotExistError) { if (error.code === PostgresRelationDoesNotExistError) {
return self.createClass( return self.createClass(className, { fields: { [fieldName]: type } }, t);
className,
{ fields: { [fieldName]: type } },
t
);
} }
if (error.code !== PostgresDuplicateColumnError) { if (error.code !== PostgresDuplicateColumnError) {
throw error; throw error;
@@ -1234,11 +1152,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
// may do so. // may do so.
// Returns a Promise. // Returns a Promise.
async deleteFields( async deleteFields(className: string, schema: SchemaType, fieldNames: string[]): Promise<void> {
className: string,
schema: SchemaType,
fieldNames: string[]
): Promise<void> {
debug('deleteFields', className, fieldNames); debug('deleteFields', className, fieldNames);
fieldNames = fieldNames.reduce((list: Array<string>, fieldName: string) => { fieldNames = fieldNames.reduce((list: Array<string>, fieldName: string) => {
const field = schema.fields[fieldName]; const field = schema.fields[fieldName];
@@ -1257,15 +1171,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
.join(', DROP COLUMN'); .join(', DROP COLUMN');
await this._client.tx('delete-fields', async t => { await this._client.tx('delete-fields', async t => {
await t.none( await t.none('UPDATE "_SCHEMA" SET "schema" = $<schema> WHERE "className" = $<className>', {
'UPDATE "_SCHEMA" SET "schema" = $<schema> WHERE "className" = $<className>', schema,
{ schema, className } className,
); });
if (values.length > 1) { if (values.length > 1) {
await t.none( await t.none(`ALTER TABLE $1:name DROP COLUMN IF EXISTS ${columns}`, values);
`ALTER TABLE $1:name DROP COLUMN IF EXISTS ${columns}`,
values
);
} }
}); });
} }
@@ -1412,10 +1323,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
const fieldName = columnsArray[index]; const fieldName = columnsArray[index];
if (['_rperm', '_wperm'].indexOf(fieldName) >= 0) { if (['_rperm', '_wperm'].indexOf(fieldName) >= 0) {
termination = '::text[]'; termination = '::text[]';
} else if ( } else if (schema.fields[fieldName] && schema.fields[fieldName].type === 'Array') {
schema.fields[fieldName] &&
schema.fields[fieldName].type === 'Array'
) {
termination = '::jsonb'; termination = '::jsonb';
} }
return `$${index + 2 + columnsArray.length}${termination}`; return `$${index + 2 + columnsArray.length}${termination}`;
@@ -1427,18 +1335,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
return `POINT($${l}, $${l + 1})`; return `POINT($${l}, $${l + 1})`;
}); });
const columnsPattern = columnsArray const columnsPattern = columnsArray.map((col, index) => `$${index + 2}:name`).join();
.map((col, index) => `$${index + 2}:name`)
.join();
const valuesPattern = initialValues.concat(geoPointsInjects).join(); const valuesPattern = initialValues.concat(geoPointsInjects).join();
const qs = `INSERT INTO $1:name (${columnsPattern}) VALUES (${valuesPattern})`; const qs = `INSERT INTO $1:name (${columnsPattern}) VALUES (${valuesPattern})`;
const values = [className, ...columnsArray, ...valuesArray]; const values = [className, ...columnsArray, ...valuesArray];
debug(qs, values); debug(qs, values);
const promise = (transactionalSession const promise = (transactionalSession ? transactionalSession.t : this._client)
? transactionalSession.t
: this._client
)
.none(qs, values) .none(qs, values)
.then(() => ({ ops: [object] })) .then(() => ({ ops: [object] }))
.catch(error => { .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`; const qs = `WITH deleted AS (DELETE FROM $1:name WHERE ${where.pattern} RETURNING *) SELECT count(*) FROM deleted`;
debug(qs, values); debug(qs, values);
const promise = (transactionalSession const promise = (transactionalSession ? transactionalSession.t : this._client)
? transactionalSession.t
: this._client
)
.one(qs, values, a => +a.count) .one(qs, values, a => +a.count)
.then(count => { .then(count => {
if (count === 0) { if (count === 0) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
Parse.Error.OBJECT_NOT_FOUND,
'Object not found.'
);
} else { } else {
return count; return count;
} }
@@ -1523,13 +1420,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
transactionalSession: ?any transactionalSession: ?any
): Promise<any> { ): Promise<any> {
debug('findOneAndUpdate', className, query, update); debug('findOneAndUpdate', className, query, update);
return this.updateObjectsByQuery( return this.updateObjectsByQuery(className, schema, query, update, transactionalSession).then(
className, val => val[0]
schema, );
query,
update,
transactionalSession
).then(val => val[0]);
} }
// Apply the update to all objects that match the given Parse Query. // Apply the update to all objects that match the given Parse Query.
@@ -1592,39 +1485,28 @@ export class PostgresStorageAdapter implements StorageAdapter {
const fieldNameIndex = index; const fieldNameIndex = index;
index += 1; index += 1;
values.push(fieldName); values.push(fieldName);
const update = Object.keys(fieldValue).reduce( const update = Object.keys(fieldValue).reduce((lastKey: string, key: string) => {
(lastKey: string, key: string) => { const str = generate(lastKey, `$${index}::text`, `$${index + 1}::jsonb`);
const str = generate( index += 2;
lastKey, let value = fieldValue[key];
`$${index}::text`, if (value) {
`$${index + 1}::jsonb` if (value.__op === 'Delete') {
); value = null;
index += 2; } else {
let value = fieldValue[key]; value = JSON.stringify(value);
if (value) {
if (value.__op === 'Delete') {
value = null;
} else {
value = JSON.stringify(value);
}
} }
values.push(key, value); }
return str; values.push(key, value);
}, return str;
lastKey }, lastKey);
);
updatePatterns.push(`$${fieldNameIndex}:name = ${update}`); updatePatterns.push(`$${fieldNameIndex}:name = ${update}`);
} else if (fieldValue.__op === 'Increment') { } else if (fieldValue.__op === 'Increment') {
updatePatterns.push( updatePatterns.push(`$${index}:name = COALESCE($${index}:name, 0) + $${index + 1}`);
`$${index}:name = COALESCE($${index}:name, 0) + $${index + 1}`
);
values.push(fieldName, fieldValue.amount); values.push(fieldName, fieldValue.amount);
index += 2; index += 2;
} else if (fieldValue.__op === 'Add') { } else if (fieldValue.__op === 'Add') {
updatePatterns.push( updatePatterns.push(
`$${index}:name = array_add(COALESCE($${index}:name, '[]'::jsonb), $${ `$${index}:name = array_add(COALESCE($${index}:name, '[]'::jsonb), $${index + 1}::jsonb)`
index + 1
}::jsonb)`
); );
values.push(fieldName, JSON.stringify(fieldValue.objects)); values.push(fieldName, JSON.stringify(fieldValue.objects));
index += 2; index += 2;
@@ -1678,9 +1560,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
values.push(fieldName, toPostgresValue(fieldValue)); values.push(fieldName, toPostgresValue(fieldValue));
index += 2; index += 2;
} else if (fieldValue.__type === 'GeoPoint') { } else if (fieldValue.__type === 'GeoPoint') {
updatePatterns.push( updatePatterns.push(`$${index}:name = POINT($${index + 1}, $${index + 2})`);
`$${index}:name = POINT($${index + 1}, $${index + 2})`
);
values.push(fieldName, fieldValue.longitude, fieldValue.latitude); values.push(fieldName, fieldValue.longitude, fieldValue.latitude);
index += 3; index += 3;
} else if (fieldValue.__type === 'Polygon') { } else if (fieldValue.__type === 'Polygon') {
@@ -1745,12 +1625,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
}) })
.map(k => k.split('.')[1]); .map(k => k.split('.')[1]);
const deletePatterns = keysToDelete.reduce( const deletePatterns = keysToDelete.reduce((p: string, c: string, i: number) => {
(p: string, c: string, i: number) => { return p + ` - '$${index + 1 + i}:value'`;
return p + ` - '$${index + 1 + i}:value'`; }, '');
},
''
);
// Override Object // Override Object
let updateObject = "'{}'::jsonb"; let updateObject = "'{}'::jsonb";
@@ -1799,14 +1676,10 @@ export class PostgresStorageAdapter implements StorageAdapter {
}); });
values.push(...where.values); values.push(...where.values);
const whereClause = const whereClause = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
const qs = `UPDATE $1:name SET ${updatePatterns.join()} ${whereClause} RETURNING *`; const qs = `UPDATE $1:name SET ${updatePatterns.join()} ${whereClause} RETURNING *`;
debug('update: ', qs, values); debug('update: ', qs, values);
const promise = (transactionalSession const promise = (transactionalSession ? transactionalSession.t : this._client).any(qs, values);
? transactionalSession.t
: this._client
).any(qs, values);
if (transactionalSession) { if (transactionalSession) {
transactionalSession.batch.push(promise); transactionalSession.batch.push(promise);
} }
@@ -1823,23 +1696,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
) { ) {
debug('upsertOneObject', { className, query, update }); debug('upsertOneObject', { className, query, update });
const createValue = Object.assign({}, query, update); const createValue = Object.assign({}, query, update);
return this.createObject( return this.createObject(className, schema, createValue, transactionalSession).catch(error => {
className,
schema,
createValue,
transactionalSession
).catch(error => {
// ignore duplicate value errors as it's upsert // ignore duplicate value errors as it's upsert
if (error.code !== Parse.Error.DUPLICATE_VALUE) { if (error.code !== Parse.Error.DUPLICATE_VALUE) {
throw error; throw error;
} }
return this.findOneAndUpdate( return this.findOneAndUpdate(className, schema, query, update, transactionalSession);
className,
schema,
query,
update,
transactionalSession
);
}); });
} }
@@ -1868,8 +1730,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
}); });
values.push(...where.values); values.push(...where.values);
const wherePattern = const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
const limitPattern = hasLimit ? `LIMIT $${values.length + 1}` : ''; const limitPattern = hasLimit ? `LIMIT $${values.length + 1}` : '';
if (hasLimit) { if (hasLimit) {
values.push(limit); values.push(limit);
@@ -1892,10 +1753,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
return `${transformKey} DESC`; return `${transformKey} DESC`;
}) })
.join(); .join();
sortPattern = sortPattern = sort !== undefined && Object.keys(sort).length > 0 ? `ORDER BY ${sorting}` : '';
sort !== undefined && Object.keys(sort).length > 0
? `ORDER BY ${sorting}`
: '';
} }
if (where.sorts && Object.keys((where.sorts: any)).length > 0) { if (where.sorts && Object.keys((where.sorts: any)).length > 0) {
sortPattern = `ORDER BY ${where.sorts.join()}`; 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 originalQuery = `SELECT ${columns} FROM $1:name ${wherePattern} ${sortPattern} ${limitPattern} ${skipPattern}`;
const qs = explain const qs = explain ? this.createExplainableQuery(originalQuery) : originalQuery;
? this.createExplainableQuery(originalQuery)
: originalQuery;
debug(qs, values); debug(qs, values);
return this._client return this._client
.any(qs, values) .any(qs, values)
@@ -1943,9 +1799,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
if (explain) { if (explain) {
return results; return results;
} }
return results.map(object => return results.map(object => this.postgresObjectToParseObject(className, object, schema));
this.postgresObjectToParseObject(className, object, schema)
);
}); });
} }
@@ -1977,10 +1831,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
let coords = object[fieldName]; let coords = object[fieldName];
coords = coords.substr(2, coords.length - 4).split('),('); coords = coords.substr(2, coords.length - 4).split('),(');
coords = coords.map(point => { coords = coords.map(point => {
return [ return [parseFloat(point.split(',')[1]), parseFloat(point.split(',')[0])];
parseFloat(point.split(',')[1]),
parseFloat(point.split(',')[0]),
];
}); });
object[fieldName] = { object[fieldName] = {
__type: 'Polygon', __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 // 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, // Way of determining if a field is nullable. Undefined doesn't count against uniqueness,
// which is why we use sparse indexes. // which is why we use sparse indexes.
async ensureUniqueness( async ensureUniqueness(className: string, schema: SchemaType, fieldNames: string[]) {
className: string,
schema: SchemaType,
fieldNames: string[]
) {
const constraintName = `${className}_unique_${fieldNames.sort().join('_')}`; const constraintName = `${className}_unique_${fieldNames.sort().join('_')}`;
const constraintPatterns = fieldNames.map( const constraintPatterns = fieldNames.map((fieldName, index) => `$${index + 3}:name`);
(fieldName, index) => `$${index + 3}:name`
);
const qs = `CREATE UNIQUE INDEX IF NOT EXISTS $2:name ON $1:name(${constraintPatterns.join()})`; const qs = `CREATE UNIQUE INDEX IF NOT EXISTS $2:name ON $1:name(${constraintPatterns.join()})`;
return this._client return this._client.none(qs, [className, constraintName, ...fieldNames]).catch(error => {
.none(qs, [className, constraintName, ...fieldNames]) if (error.code === PostgresDuplicateRelationError && error.message.includes(constraintName)) {
.catch(error => { // Index already exists. Ignore error.
if ( } else if (
error.code === PostgresDuplicateRelationError && error.code === PostgresUniqueIndexViolationError &&
error.message.includes(constraintName) error.message.includes(constraintName)
) { ) {
// Index already exists. Ignore error. // Cast the error into the proper parse error
} else if ( throw new Parse.Error(
error.code === PostgresUniqueIndexViolationError && Parse.Error.DUPLICATE_VALUE,
error.message.includes(constraintName) 'A duplicate value for a field with unique values was provided'
) { );
// Cast the error into the proper parse error } else {
throw new Parse.Error( throw error;
Parse.Error.DUPLICATE_VALUE, }
'A duplicate value for a field with unique values was provided' });
);
} else {
throw error;
}
});
} }
// Executes a count. // Executes a count.
@@ -2103,15 +1943,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
}); });
values.push(...where.values); values.push(...where.values);
const wherePattern = const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
let qs = ''; let qs = '';
if (where.pattern.length > 0 || !estimate) { if (where.pattern.length > 0 || !estimate) {
qs = `SELECT count(*) FROM $1:name ${wherePattern}`; qs = `SELECT count(*) FROM $1:name ${wherePattern}`;
} else { } else {
qs = qs = 'SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = $1';
'SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = $1';
} }
return this._client return this._client
@@ -2130,12 +1968,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
}); });
} }
async distinct( async distinct(className: string, schema: SchemaType, query: QueryType, fieldName: string) {
className: string,
schema: SchemaType,
query: QueryType,
fieldName: string
) {
debug('distinct', className, query); debug('distinct', className, query);
let field = fieldName; let field = fieldName;
let column = fieldName; let column = fieldName;
@@ -2145,13 +1978,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
column = fieldName.split('.')[0]; column = fieldName.split('.')[0];
} }
const isArrayField = const isArrayField =
schema.fields && schema.fields && schema.fields[fieldName] && schema.fields[fieldName].type === 'Array';
schema.fields[fieldName] &&
schema.fields[fieldName].type === 'Array';
const isPointerField = const isPointerField =
schema.fields && schema.fields && schema.fields[fieldName] && schema.fields[fieldName].type === 'Pointer';
schema.fields[fieldName] &&
schema.fields[fieldName].type === 'Pointer';
const values = [field, column, className]; const values = [field, column, className];
const where = buildWhereClause({ const where = buildWhereClause({
schema, schema,
@@ -2161,8 +1990,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
}); });
values.push(...where.values); values.push(...where.values);
const wherePattern = const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
const transformer = isArrayField ? 'jsonb_array_elements' : 'ON'; const transformer = isArrayField ? 'jsonb_array_elements' : 'ON';
let qs = `SELECT DISTINCT ${transformer}($1:name) $2:name FROM $3:name ${wherePattern}`; let qs = `SELECT DISTINCT ${transformer}($1:name) $2:name FROM $3:name ${wherePattern}`;
if (isNested) { if (isNested) {
@@ -2195,9 +2023,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
return results.map(object => object[column][child]); return results.map(object => object[column][child]);
}) })
.then(results => .then(results =>
results.map(object => results.map(object => this.postgresObjectToParseObject(className, object, schema))
this.postgresObjectToParseObject(className, object, schema)
)
); );
} }
@@ -2235,11 +2061,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
index += 1; index += 1;
continue; continue;
} }
if ( if (field === '_id' && typeof value === 'object' && Object.keys(value).length !== 0) {
field === '_id' &&
typeof value === 'object' &&
Object.keys(value).length !== 0
) {
groupValues = value; groupValues = value;
const groupByFields = []; const groupByFields = [];
for (const alias in value) { for (const alias in value) {
@@ -2261,9 +2083,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
columns.push( columns.push(
`EXTRACT(${ `EXTRACT(${
mongoAggregateToPostgres[operation] mongoAggregateToPostgres[operation]
} FROM $${index}:name AT TIME ZONE 'UTC') AS $${ } FROM $${index}:name AT TIME ZONE 'UTC') AS $${index + 1}:name`
index + 1
}:name`
); );
values.push(source, alias); values.push(source, alias);
index += 2; index += 2;
@@ -2323,10 +2143,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
} }
if (stage.$match) { if (stage.$match) {
const patterns = []; const patterns = [];
const orOrAnd = Object.prototype.hasOwnProperty.call( const orOrAnd = Object.prototype.hasOwnProperty.call(stage.$match, '$or')
stage.$match,
'$or'
)
? ' OR ' ? ' OR '
: ' AND '; : ' AND ';
@@ -2345,9 +2162,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
Object.keys(ParseToPosgresComparator).forEach(cmp => { Object.keys(ParseToPosgresComparator).forEach(cmp => {
if (value[cmp]) { if (value[cmp]) {
const pgComparator = ParseToPosgresComparator[cmp]; const pgComparator = ParseToPosgresComparator[cmp];
matchPatterns.push( matchPatterns.push(`$${index}:name ${pgComparator} $${index + 1}`);
`$${index}:name ${pgComparator} $${index + 1}`
);
values.push(field, toPostgresValue(value[cmp])); values.push(field, toPostgresValue(value[cmp]));
index += 2; index += 2;
} }
@@ -2355,18 +2170,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
if (matchPatterns.length > 0) { if (matchPatterns.length > 0) {
patterns.push(`(${matchPatterns.join(' AND ')})`); patterns.push(`(${matchPatterns.join(' AND ')})`);
} }
if ( if (schema.fields[field] && schema.fields[field].type && matchPatterns.length === 0) {
schema.fields[field] &&
schema.fields[field].type &&
matchPatterns.length === 0
) {
patterns.push(`$${index}:name = $${index + 1}`); patterns.push(`$${index}:name = $${index + 1}`);
values.push(field, value); values.push(field, value);
index += 2; index += 2;
} }
} }
wherePattern = wherePattern = patterns.length > 0 ? `WHERE ${patterns.join(` ${orOrAnd} `)}` : '';
patterns.length > 0 ? `WHERE ${patterns.join(` ${orOrAnd} `)}` : '';
} }
if (stage.$limit) { if (stage.$limit) {
limitPattern = `LIMIT $${index}`; limitPattern = `LIMIT $${index}`;
@@ -2390,8 +2200,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
}) })
.join(); .join();
values.push(...keys); values.push(...keys);
sortPattern = sortPattern = sort !== undefined && sorting.length > 0 ? `ORDER BY ${sorting}` : '';
sort !== undefined && sorting.length > 0 ? `ORDER BY ${sorting}` : '';
} }
} }
@@ -2406,17 +2215,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
const originalQuery = `SELECT ${columns const originalQuery = `SELECT ${columns
.filter(Boolean) .filter(Boolean)
.join()} FROM $1:name ${wherePattern} ${skipPattern} ${groupPattern} ${sortPattern} ${limitPattern}`; .join()} FROM $1:name ${wherePattern} ${skipPattern} ${groupPattern} ${sortPattern} ${limitPattern}`;
const qs = explain const qs = explain ? this.createExplainableQuery(originalQuery) : originalQuery;
? this.createExplainableQuery(originalQuery)
: originalQuery;
debug(qs, values); debug(qs, values);
return this._client.any(qs, values).then(a => { return this._client.any(qs, values).then(a => {
if (explain) { if (explain) {
return a; return a;
} }
const results = a.map(object => const results = a.map(object => this.postgresObjectToParseObject(className, object, schema));
this.postgresObjectToParseObject(className, object, schema)
);
results.forEach(result => { results.forEach(result => {
if (!Object.prototype.hasOwnProperty.call(result, 'objectId')) { if (!Object.prototype.hasOwnProperty.call(result, 'objectId')) {
result.objectId = null; result.objectId = null;
@@ -2474,11 +2279,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
}); });
} }
async createIndexes( async createIndexes(className: string, indexes: any, conn: ?any): Promise<void> {
className: string,
indexes: any,
conn: ?any
): Promise<void> {
return (conn || this._client).tx(t => return (conn || this._client).tx(t =>
t.batch( t.batch(
indexes.map(i => { indexes.map(i => {
@@ -2498,9 +2299,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
type: any, type: any,
conn: ?any conn: ?any
): Promise<void> { ): Promise<void> {
await ( await (conn || this._client).none('CREATE INDEX IF NOT EXISTS $1:name ON $2:name ($3:name)', [
conn || this._client
).none('CREATE INDEX IF NOT EXISTS $1:name ON $2:name ($3:name)', [
fieldName, fieldName,
className, className,
type, type,
@@ -2512,9 +2311,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
query: 'DROP INDEX $1:name', query: 'DROP INDEX $1:name',
values: i, values: i,
})); }));
await (conn || this._client).tx(t => await (conn || this._client).tx(t => t.none(this._pgp.helpers.concat(queries)));
t.none(this._pgp.helpers.concat(queries))
);
} }
async getIndexes(className: string) { async getIndexes(className: string) {
@@ -2547,18 +2344,14 @@ export class PostgresStorageAdapter implements StorageAdapter {
} }
commitTransactionalSession(transactionalSession: any): Promise<void> { commitTransactionalSession(transactionalSession: any): Promise<void> {
transactionalSession.resolve( transactionalSession.resolve(transactionalSession.t.batch(transactionalSession.batch));
transactionalSession.t.batch(transactionalSession.batch)
);
return transactionalSession.result; return transactionalSession.result;
} }
abortTransactionalSession(transactionalSession: any): Promise<void> { abortTransactionalSession(transactionalSession: any): Promise<void> {
const result = transactionalSession.result.catch(); const result = transactionalSession.result.catch();
transactionalSession.batch.push(Promise.reject()); transactionalSession.batch.push(Promise.reject());
transactionalSession.resolve( transactionalSession.resolve(transactionalSession.t.batch(transactionalSession.batch));
transactionalSession.t.batch(transactionalSession.batch)
);
return result; return result;
} }
@@ -2575,41 +2368,34 @@ export class PostgresStorageAdapter implements StorageAdapter {
const indexNameOptions: Object = const indexNameOptions: Object =
indexName != null ? { name: indexName } : { name: defaultIndexName }; indexName != null ? { name: indexName } : { name: defaultIndexName };
const constraintPatterns = caseInsensitive const constraintPatterns = caseInsensitive
? fieldNames.map( ? fieldNames.map((fieldName, index) => `lower($${index + 3}:name) varchar_pattern_ops`)
(fieldName, index) => `lower($${index + 3}:name) varchar_pattern_ops`
)
: fieldNames.map((fieldName, index) => `$${index + 3}:name`); : fieldNames.map((fieldName, index) => `$${index + 3}:name`);
const qs = `CREATE INDEX IF NOT EXISTS $1:name ON $2:name (${constraintPatterns.join()})`; const qs = `CREATE INDEX IF NOT EXISTS $1:name ON $2:name (${constraintPatterns.join()})`;
await conn await conn.none(qs, [indexNameOptions.name, className, ...fieldNames]).catch(error => {
.none(qs, [indexNameOptions.name, className, ...fieldNames]) if (
.catch(error => { error.code === PostgresDuplicateRelationError &&
if ( error.message.includes(indexNameOptions.name)
error.code === PostgresDuplicateRelationError && ) {
error.message.includes(indexNameOptions.name) // Index already exists. Ignore error.
) { } else if (
// Index already exists. Ignore error. error.code === PostgresUniqueIndexViolationError &&
} else if ( error.message.includes(indexNameOptions.name)
error.code === PostgresUniqueIndexViolationError && ) {
error.message.includes(indexNameOptions.name) // Cast the error into the proper parse error
) { throw new Parse.Error(
// Cast the error into the proper parse error Parse.Error.DUPLICATE_VALUE,
throw new Parse.Error( 'A duplicate value for a field with unique values was provided'
Parse.Error.DUPLICATE_VALUE, );
'A duplicate value for a field with unique values was provided' } else {
); throw error;
} else { }
throw error; });
}
});
} }
} }
function convertPolygonToSQL(polygon) { function convertPolygonToSQL(polygon) {
if (polygon.length < 3) { if (polygon.length < 3) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_JSON, `Polygon must have at least 3 values`);
Parse.Error.INVALID_JSON,
`Polygon must have at least 3 values`
);
} }
if ( if (
polygon[0][0] !== polygon[polygon.length - 1][0] || polygon[0][0] !== polygon[polygon.length - 1][0] ||
@@ -2757,9 +2543,7 @@ function literalizeRegexPart(s: string) {
var GeoPointCoder = { var GeoPointCoder = {
isValidJSON(value) { isValidJSON(value) {
return ( return typeof value === 'object' && value !== null && value.__type === 'GeoPoint';
typeof value === 'object' && value !== null && value.__type === 'GeoPoint'
);
}, },
}; };

View File

@@ -34,18 +34,10 @@ export interface StorageAdapter {
classExists(className: string): Promise<boolean>; classExists(className: string): Promise<boolean>;
setClassLevelPermissions(className: string, clps: any): Promise<void>; setClassLevelPermissions(className: string, clps: any): Promise<void>;
createClass(className: string, schema: SchemaType): Promise<void>; createClass(className: string, schema: SchemaType): Promise<void>;
addFieldIfNotExists( addFieldIfNotExists(className: string, fieldName: string, type: any): Promise<void>;
className: string,
fieldName: string,
type: any
): Promise<void>;
deleteClass(className: string): Promise<void>; deleteClass(className: string): Promise<void>;
deleteAllClasses(fast: boolean): Promise<void>; deleteAllClasses(fast: boolean): Promise<void>;
deleteFields( deleteFields(className: string, schema: SchemaType, fieldNames: Array<string>): Promise<void>;
className: string,
schema: SchemaType,
fieldNames: Array<string>
): Promise<void>;
getAllClasses(): Promise<StorageClass[]>; getAllClasses(): Promise<StorageClass[]>;
getClass(className: string): Promise<StorageClass>; getClass(className: string): Promise<StorageClass>;
createObject( createObject(
@@ -95,11 +87,7 @@ export interface StorageAdapter {
caseSensitive?: boolean, caseSensitive?: boolean,
options?: Object options?: Object
): Promise<any>; ): Promise<any>;
ensureUniqueness( ensureUniqueness(className: string, schema: SchemaType, fieldNames: Array<string>): Promise<void>;
className: string,
schema: SchemaType,
fieldNames: Array<string>
): Promise<void>;
count( count(
className: string, className: string,
schema: SchemaType, schema: SchemaType,

View File

@@ -5,40 +5,17 @@ const createObject = async (className, fields, config, auth, info) => {
fields = {}; fields = {};
} }
return ( return (await rest.create(config, auth, className, fields, info.clientSDK, info.context))
await rest.create( .response;
config,
auth,
className,
fields,
info.clientSDK,
info.context
)
).response;
}; };
const updateObject = async ( const updateObject = async (className, objectId, fields, config, auth, info) => {
className,
objectId,
fields,
config,
auth,
info
) => {
if (!fields) { if (!fields) {
fields = {}; fields = {};
} }
return ( return (
await rest.update( await rest.update(config, auth, className, { objectId }, fields, info.clientSDK, info.context)
config,
auth,
className,
{ objectId },
fields,
info.clientSDK,
info.context
)
).response; ).response;
}; };

View File

@@ -6,8 +6,7 @@ const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLQuery( parseGraphQLSchema.addGraphQLQuery(
'health', 'health',
{ {
description: description: 'The health query can be used to check if the server is up and running.',
'The health query can be used to check if the server is up and running.',
type: new GraphQLNonNull(GraphQLBoolean), type: new GraphQLNonNull(GraphQLBoolean),
resolve: () => true, resolve: () => true,
}, },

View File

@@ -23,7 +23,7 @@ class TypeValidationError extends Error {
} }
} }
const parseStringValue = (value) => { const parseStringValue = value => {
if (typeof value === 'string') { if (typeof value === 'string') {
return value; return value;
} }
@@ -31,7 +31,7 @@ const parseStringValue = (value) => {
throw new TypeValidationError(value, 'String'); throw new TypeValidationError(value, 'String');
}; };
const parseIntValue = (value) => { const parseIntValue = value => {
if (typeof value === 'string') { if (typeof value === 'string') {
const int = Number(value); const int = Number(value);
if (Number.isInteger(int)) { if (Number.isInteger(int)) {
@@ -42,7 +42,7 @@ const parseIntValue = (value) => {
throw new TypeValidationError(value, 'Int'); throw new TypeValidationError(value, 'Int');
}; };
const parseFloatValue = (value) => { const parseFloatValue = value => {
if (typeof value === 'string') { if (typeof value === 'string') {
const float = Number(value); const float = Number(value);
if (!isNaN(float)) { if (!isNaN(float)) {
@@ -53,7 +53,7 @@ const parseFloatValue = (value) => {
throw new TypeValidationError(value, 'Float'); throw new TypeValidationError(value, 'Float');
}; };
const parseBooleanValue = (value) => { const parseBooleanValue = value => {
if (typeof value === 'boolean') { if (typeof value === 'boolean') {
return value; return value;
} }
@@ -61,7 +61,7 @@ const parseBooleanValue = (value) => {
throw new TypeValidationError(value, 'Boolean'); throw new TypeValidationError(value, 'Boolean');
}; };
const parseValue = (value) => { const parseValue = value => {
switch (value.kind) { switch (value.kind) {
case Kind.STRING: case Kind.STRING:
return parseStringValue(value.value); return parseStringValue(value.value);
@@ -86,15 +86,15 @@ const parseValue = (value) => {
} }
}; };
const parseListValues = (values) => { const parseListValues = values => {
if (Array.isArray(values)) { if (Array.isArray(values)) {
return values.map((value) => parseValue(value)); return values.map(value => parseValue(value));
} }
throw new TypeValidationError(values, 'List'); throw new TypeValidationError(values, 'List');
}; };
const parseObjectFields = (fields) => { const parseObjectFields = fields => {
if (Array.isArray(fields)) { if (Array.isArray(fields)) {
return fields.reduce( return fields.reduce(
(object, field) => ({ (object, field) => ({
@@ -112,15 +112,14 @@ const ANY = new GraphQLScalarType({
name: 'Any', name: 'Any',
description: description:
'The Any scalar type is used in operations and types that involve any type of value.', 'The Any scalar type is used in operations and types that involve any type of value.',
parseValue: (value) => value, parseValue: value => value,
serialize: (value) => value, serialize: value => value,
parseLiteral: (ast) => parseValue(ast), parseLiteral: ast => parseValue(ast),
}); });
const OBJECT = new GraphQLScalarType({ const OBJECT = new GraphQLScalarType({
name: 'Object', name: 'Object',
description: description: 'The Object scalar type is used in operations and types that involve objects.',
'The Object scalar type is used in operations and types that involve objects.',
parseValue(value) { parseValue(value) {
if (typeof value === 'object') { if (typeof value === 'object') {
return value; return value;
@@ -144,7 +143,7 @@ const OBJECT = new GraphQLScalarType({
}, },
}); });
const parseDateIsoValue = (value) => { const parseDateIsoValue = value => {
if (typeof value === 'string') { if (typeof value === 'string') {
const date = new Date(value); const date = new Date(value);
if (!isNaN(date)) { if (!isNaN(date)) {
@@ -157,7 +156,7 @@ const parseDateIsoValue = (value) => {
throw new TypeValidationError(value, 'Date'); throw new TypeValidationError(value, 'Date');
}; };
const serializeDateIso = (value) => { const serializeDateIso = value => {
if (typeof value === 'string') { if (typeof value === 'string') {
return value; return value;
} }
@@ -168,7 +167,7 @@ const serializeDateIso = (value) => {
throw new TypeValidationError(value, 'Date'); throw new TypeValidationError(value, 'Date');
}; };
const parseDateIsoLiteral = (ast) => { const parseDateIsoLiteral = ast => {
if (ast.kind === Kind.STRING) { if (ast.kind === Kind.STRING) {
return parseDateIsoValue(ast.value); return parseDateIsoValue(ast.value);
} }
@@ -178,19 +177,14 @@ const parseDateIsoLiteral = (ast) => {
const DATE = new GraphQLScalarType({ const DATE = new GraphQLScalarType({
name: 'Date', name: 'Date',
description: description: 'The Date scalar type is used in operations and types that involve dates.',
'The Date scalar type is used in operations and types that involve dates.',
parseValue(value) { parseValue(value) {
if (typeof value === 'string' || value instanceof Date) { if (typeof value === 'string' || value instanceof Date) {
return { return {
__type: 'Date', __type: 'Date',
iso: parseDateIsoValue(value), iso: parseDateIsoValue(value),
}; };
} else if ( } else if (typeof value === 'object' && value.__type === 'Date' && value.iso) {
typeof value === 'object' &&
value.__type === 'Date' &&
value.iso
) {
return { return {
__type: value.__type, __type: value.__type,
iso: parseDateIsoValue(value.iso), iso: parseDateIsoValue(value.iso),
@@ -202,11 +196,7 @@ const DATE = new GraphQLScalarType({
serialize(value) { serialize(value) {
if (typeof value === 'string' || value instanceof Date) { if (typeof value === 'string' || value instanceof Date) {
return serializeDateIso(value); return serializeDateIso(value);
} else if ( } else if (typeof value === 'object' && value.__type === 'Date' && value.iso) {
typeof value === 'object' &&
value.__type === 'Date' &&
value.iso
) {
return serializeDateIso(value.iso); return serializeDateIso(value.iso);
} }
@@ -219,8 +209,8 @@ const DATE = new GraphQLScalarType({
iso: parseDateIsoLiteral(ast), iso: parseDateIsoLiteral(ast),
}; };
} else if (ast.kind === Kind.OBJECT) { } else if (ast.kind === Kind.OBJECT) {
const __type = ast.fields.find((field) => field.name.value === '__type'); const __type = ast.fields.find(field => field.name.value === '__type');
const iso = ast.fields.find((field) => field.name.value === 'iso'); const iso = ast.fields.find(field => field.name.value === 'iso');
if (__type && __type.value && __type.value.value === 'Date' && iso) { if (__type && __type.value && __type.value.value === 'Date' && iso) {
return { return {
__type: __type.value.value, __type: __type.value.value,
@@ -273,8 +263,8 @@ const BYTES = new GraphQLScalarType({
base64: ast.value, base64: ast.value,
}; };
} else if (ast.kind === Kind.OBJECT) { } else if (ast.kind === Kind.OBJECT) {
const __type = ast.fields.find((field) => field.name.value === '__type'); const __type = ast.fields.find(field => field.name.value === '__type');
const base64 = ast.fields.find((field) => field.name.value === 'base64'); const base64 = ast.fields.find(field => field.name.value === 'base64');
if ( if (
__type && __type &&
__type.value && __type.value &&
@@ -294,7 +284,7 @@ const BYTES = new GraphQLScalarType({
}, },
}); });
const parseFileValue = (value) => { const parseFileValue = value => {
if (typeof value === 'string') { if (typeof value === 'string') {
return { return {
__type: 'File', __type: 'File',
@@ -314,10 +304,9 @@ const parseFileValue = (value) => {
const FILE = new GraphQLScalarType({ const FILE = new GraphQLScalarType({
name: 'File', name: 'File',
description: description: 'The File scalar type is used in operations and types that involve files.',
'The File scalar type is used in operations and types that involve files.',
parseValue: parseFileValue, parseValue: parseFileValue,
serialize: (value) => { serialize: value => {
if (typeof value === 'string') { if (typeof value === 'string') {
return value; return value;
} else if ( } else if (
@@ -335,9 +324,9 @@ const FILE = new GraphQLScalarType({
if (ast.kind === Kind.STRING) { if (ast.kind === Kind.STRING) {
return parseFileValue(ast.value); return parseFileValue(ast.value);
} else if (ast.kind === Kind.OBJECT) { } else if (ast.kind === Kind.OBJECT) {
const __type = ast.fields.find((field) => field.name.value === '__type'); const __type = ast.fields.find(field => field.name.value === '__type');
const name = ast.fields.find((field) => field.name.value === 'name'); const name = ast.fields.find(field => field.name.value === 'name');
const url = ast.fields.find((field) => field.name.value === 'url'); const url = ast.fields.find(field => field.name.value === 'url');
if (__type && __type.value && name && name.value) { if (__type && __type.value && name && name.value) {
return parseFileValue({ return parseFileValue({
__type: __type.value.value, __type: __type.value.value,
@@ -353,8 +342,7 @@ const FILE = new GraphQLScalarType({
const FILE_INFO = new GraphQLObjectType({ const FILE_INFO = new GraphQLObjectType({
name: 'FileInfo', name: 'FileInfo',
description: description: 'The FileInfo object type is used to return the information about files.',
'The FileInfo object type is used to return the information about files.',
fields: { fields: {
name: { name: {
description: 'This is the file name.', description: 'This is the file name.',
@@ -407,8 +395,7 @@ const GEO_POINT_INPUT = new GraphQLInputObjectType({
const GEO_POINT = new GraphQLObjectType({ const GEO_POINT = new GraphQLObjectType({
name: 'GeoPoint', name: 'GeoPoint',
description: description: 'The GeoPoint object type is used to return the information about geo point fields.',
'The GeoPoint object type is used to return the information about geo point fields.',
fields: GEO_POINT_FIELDS, fields: GEO_POINT_FIELDS,
}); });
@@ -444,13 +431,11 @@ const ROLE_ACL_INPUT = new GraphQLInputObjectType({
type: new GraphQLNonNull(GraphQLString), type: new GraphQLNonNull(GraphQLString),
}, },
read: { read: {
description: description: 'Allow users who are members of the role to read the current object.',
'Allow users who are members of the role to read the current object.',
type: new GraphQLNonNull(GraphQLBoolean), type: new GraphQLNonNull(GraphQLBoolean),
}, },
write: { write: {
description: description: 'Allow users who are members of the role to write on the current object.',
'Allow users who are members of the role to write on the current object.',
type: new GraphQLNonNull(GraphQLBoolean), type: new GraphQLNonNull(GraphQLBoolean),
}, },
}, },
@@ -521,13 +506,11 @@ const ROLE_ACL = new GraphQLObjectType({
type: new GraphQLNonNull(GraphQLID), type: new GraphQLNonNull(GraphQLID),
}, },
read: { read: {
description: description: 'Allow users who are members of the role to read the current object.',
'Allow users who are members of the role to read the current object.',
type: new GraphQLNonNull(GraphQLBoolean), type: new GraphQLNonNull(GraphQLBoolean),
}, },
write: { write: {
description: description: 'Allow users who are members of the role to write on the current object.',
'Allow users who are members of the role to write on the current object.',
type: new GraphQLNonNull(GraphQLBoolean), type: new GraphQLNonNull(GraphQLBoolean),
}, },
}, },
@@ -557,7 +540,7 @@ const ACL = new GraphQLObjectType({
type: new GraphQLList(new GraphQLNonNull(USER_ACL)), type: new GraphQLList(new GraphQLNonNull(USER_ACL)),
resolve(p) { resolve(p) {
const users = []; const users = [];
Object.keys(p).forEach((rule) => { Object.keys(p).forEach(rule => {
if (rule !== '*' && rule.indexOf('role:') !== 0) { if (rule !== '*' && rule.indexOf('role:') !== 0) {
users.push({ users.push({
userId: toGlobalId('_User', rule), userId: toGlobalId('_User', rule),
@@ -574,7 +557,7 @@ const ACL = new GraphQLObjectType({
type: new GraphQLList(new GraphQLNonNull(ROLE_ACL)), type: new GraphQLList(new GraphQLNonNull(ROLE_ACL)),
resolve(p) { resolve(p) {
const roles = []; const roles = [];
Object.keys(p).forEach((rule) => { Object.keys(p).forEach(rule => {
if (rule.indexOf('role:') === 0) { if (rule.indexOf('role:') === 0) {
roles.push({ roles.push({
roleName: rule.replace('role:', ''), roleName: rule.replace('role:', ''),
@@ -610,8 +593,7 @@ const CLASS_NAME_ATT = {
}; };
const GLOBAL_OR_OBJECT_ID_ATT = { const GLOBAL_OR_OBJECT_ID_ATT = {
description: description: 'This is the object id. You can use either the global or the object id.',
'This is the object id. You can use either the global or the object id.',
type: OBJECT_ID, type: OBJECT_ID,
}; };
@@ -686,8 +668,7 @@ const READ_PREFERENCE_ATT = {
}; };
const INCLUDE_READ_PREFERENCE_ATT = { const INCLUDE_READ_PREFERENCE_ATT = {
description: description: 'The read preference for the queries to be executed to include fields.',
'The read preference for the queries to be executed to include fields.',
type: READ_PREFERENCE, type: READ_PREFERENCE,
}; };
@@ -713,8 +694,7 @@ const READ_OPTIONS_ATT = {
}; };
const WHERE_ATT = { const WHERE_ATT = {
description: description: 'These are the conditions that the objects need to match in order to be found',
'These are the conditions that the objects need to match in order to be found',
type: OBJECT, type: OBJECT,
}; };
@@ -736,8 +716,7 @@ const COUNT_ATT = {
const SEARCH_INPUT = new GraphQLInputObjectType({ const SEARCH_INPUT = new GraphQLInputObjectType({
name: 'SearchInput', name: 'SearchInput',
description: description: 'The SearchInput type is used to specifiy a search operation on a full text search.',
'The SearchInput type is used to specifiy a search operation on a full text search.',
fields: { fields: {
term: { term: {
description: 'This is the term to be searched.', description: 'This is the term to be searched.',
@@ -749,13 +728,11 @@ const SEARCH_INPUT = new GraphQLInputObjectType({
type: GraphQLString, type: GraphQLString,
}, },
caseSensitive: { caseSensitive: {
description: description: 'This is the flag to enable or disable case sensitive search.',
'This is the flag to enable or disable case sensitive search.',
type: GraphQLBoolean, type: GraphQLBoolean,
}, },
diacriticSensitive: { diacriticSensitive: {
description: description: 'This is the flag to enable or disable diacritic sensitive search.',
'This is the flag to enable or disable diacritic sensitive search.',
type: GraphQLBoolean, type: GraphQLBoolean,
}, },
}, },
@@ -763,8 +740,7 @@ const SEARCH_INPUT = new GraphQLInputObjectType({
const TEXT_INPUT = new GraphQLInputObjectType({ const TEXT_INPUT = new GraphQLInputObjectType({
name: 'TextInput', name: 'TextInput',
description: description: 'The TextInput type is used to specify a text operation on a constraint.',
'The TextInput type is used to specify a text operation on a constraint.',
fields: { fields: {
search: { search: {
description: 'This is the search to be executed.', description: 'This is the search to be executed.',
@@ -775,8 +751,7 @@ const TEXT_INPUT = new GraphQLInputObjectType({
const BOX_INPUT = new GraphQLInputObjectType({ const BOX_INPUT = new GraphQLInputObjectType({
name: 'BoxInput', name: 'BoxInput',
description: description: 'The BoxInput type is used to specifiy a box operation on a within geo query.',
'The BoxInput type is used to specifiy a box operation on a within geo query.',
fields: { fields: {
bottomLeft: { bottomLeft: {
description: 'This is the bottom left coordinates of the box.', description: 'This is the bottom left coordinates of the box.',
@@ -791,8 +766,7 @@ const BOX_INPUT = new GraphQLInputObjectType({
const WITHIN_INPUT = new GraphQLInputObjectType({ const WITHIN_INPUT = new GraphQLInputObjectType({
name: 'WithinInput', name: 'WithinInput',
description: description: 'The WithinInput type is used to specify a within operation on a constraint.',
'The WithinInput type is used to specify a within operation on a constraint.',
fields: { fields: {
box: { box: {
description: 'This is the box to be specified.', description: 'This is the box to be specified.',
@@ -819,8 +793,7 @@ const CENTER_SPHERE_INPUT = new GraphQLInputObjectType({
const GEO_WITHIN_INPUT = new GraphQLInputObjectType({ const GEO_WITHIN_INPUT = new GraphQLInputObjectType({
name: 'GeoWithinInput', name: 'GeoWithinInput',
description: description: 'The GeoWithinInput type is used to specify a geoWithin operation on a constraint.',
'The GeoWithinInput type is used to specify a geoWithin operation on a constraint.',
fields: { fields: {
polygon: { polygon: {
description: 'This is the polygon to be specified.', 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: 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.', '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, type,
}); });
const notEqualTo = (type) => ({ const notEqualTo = type => ({
description: 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.', '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, type,
}); });
const lessThan = (type) => ({ const lessThan = type => ({
description: 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.', '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, type,
}); });
const lessThanOrEqualTo = (type) => ({ const lessThanOrEqualTo = type => ({
description: 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.', '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, type,
}); });
const greaterThan = (type) => ({ const greaterThan = type => ({
description: 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.', '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, type,
}); });
const greaterThanOrEqualTo = (type) => ({ const greaterThanOrEqualTo = type => ({
description: 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.', '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, type,
}); });
const inOp = (type) => ({ const inOp = type => ({
description: 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.', '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), type: new GraphQLList(type),
}); });
const notIn = (type) => ({ const notIn = type => ({
description: 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.', '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), type: new GraphQLList(type),
@@ -913,8 +886,7 @@ const options = {
const SUBQUERY_INPUT = new GraphQLInputObjectType({ const SUBQUERY_INPUT = new GraphQLInputObjectType({
name: 'SubqueryInput', name: 'SubqueryInput',
description: description: 'The SubqueryInput type is used to specify a sub query to another class.',
'The SubqueryInput type is used to specify a sub query to another class.',
fields: { fields: {
className: CLASS_NAME_ATT, className: CLASS_NAME_ATT,
where: Object.assign({}, WHERE_ATT, { where: Object.assign({}, WHERE_ATT, {
@@ -988,8 +960,7 @@ const STRING_WHERE_INPUT = new GraphQLInputObjectType({
matchesRegex, matchesRegex,
options, options,
text: { text: {
description: description: 'This is the $text operator to specify a full text search constraint.',
'This is the $text operator to specify a full text search constraint.',
type: TEXT_INPUT, type: TEXT_INPUT,
}, },
inQueryKey, inQueryKey,
@@ -1225,27 +1196,21 @@ let ARRAY_RESULT;
const loadArrayResult = (parseGraphQLSchema, parseClasses) => { const loadArrayResult = (parseGraphQLSchema, parseClasses) => {
const classTypes = parseClasses const classTypes = parseClasses
.filter((parseClass) => .filter(parseClass =>
parseGraphQLSchema.parseClassTypes[parseClass.className] parseGraphQLSchema.parseClassTypes[parseClass.className].classGraphQLOutputType ? true : false
.classGraphQLOutputType
? true
: false
) )
.map( .map(
(parseClass) => parseClass => parseGraphQLSchema.parseClassTypes[parseClass.className].classGraphQLOutputType
parseGraphQLSchema.parseClassTypes[parseClass.className]
.classGraphQLOutputType
); );
ARRAY_RESULT = new GraphQLUnionType({ ARRAY_RESULT = new GraphQLUnionType({
name: 'ArrayResult', name: 'ArrayResult',
description: description:
'Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments', 'Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments',
types: () => [ELEMENT, ...classTypes], types: () => [ELEMENT, ...classTypes],
resolveType: (value) => { resolveType: value => {
if (value.__type === 'Object' && value.className && value.objectId) { if (value.__type === 'Object' && value.className && value.objectId) {
if (parseGraphQLSchema.parseClassTypes[value.className]) { if (parseGraphQLSchema.parseClassTypes[value.className]) {
return parseGraphQLSchema.parseClassTypes[value.className] return parseGraphQLSchema.parseClassTypes[value.className].classGraphQLOutputType;
.classGraphQLOutputType;
} else { } else {
return ELEMENT; return ELEMENT;
} }
@@ -1257,7 +1222,7 @@ const loadArrayResult = (parseGraphQLSchema, parseClasses) => {
parseGraphQLSchema.graphQLTypes.push(ARRAY_RESULT); parseGraphQLSchema.graphQLTypes.push(ARRAY_RESULT);
}; };
const load = (parseGraphQLSchema) => { const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLType(GraphQLUpload, true); parseGraphQLSchema.addGraphQLType(GraphQLUpload, true);
parseGraphQLSchema.addGraphQLType(ANY, true); parseGraphQLSchema.addGraphQLType(ANY, true);
parseGraphQLSchema.addGraphQLType(OBJECT, true); parseGraphQLSchema.addGraphQLType(OBJECT, true);

View File

@@ -39,8 +39,7 @@ const load = parseGraphQLSchema => {
} }
}, },
obj => { obj => {
return parseGraphQLSchema.parseClassTypes[obj.className] return parseGraphQLSchema.parseClassTypes[obj.className].classGraphQLOutputType;
.classGraphQLOutputType;
} }
); );

View File

@@ -14,7 +14,7 @@ const handleUpload = async (upload, config) => {
const chunks = []; const chunks = [];
stream stream
.on('error', reject) .on('error', reject)
.on('data', (chunk) => chunks.push(chunk)) .on('data', chunk => chunks.push(chunk))
.on('end', () => resolve(Buffer.concat(chunks))); .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@\.\ ~_-]*$/)) { if (!filename.match(/^[_a-zA-Z0-9][a-zA-Z0-9@\.\ ~_-]*$/)) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename contains invalid characters.');
Parse.Error.INVALID_FILE_NAME,
'Filename contains invalid characters.'
);
} }
try { try {
return { return {
fileInfo: await config.filesController.createFile( fileInfo: await config.filesController.createFile(config, filename, data, mimetype),
config,
filename,
data,
mimetype
),
}; };
} catch (e) { } catch (e) {
logger.error('Error creating a file: ', e); logger.error('Error creating a file: ', e);
throw new Parse.Error( throw new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `Could not store file: ${filename}.`);
Parse.Error.FILE_SAVE_ERROR,
`Could not store file: ${filename}.`
);
} }
}; };
const load = (parseGraphQLSchema) => { const load = parseGraphQLSchema => {
const createMutation = mutationWithClientMutationId({ const createMutation = mutationWithClientMutationId({
name: 'CreateFile', name: 'CreateFile',
description: description: 'The createFile mutation can be used to create and upload a new file.',
'The createFile mutation can be used to create and upload a new file.',
inputFields: { inputFields: {
upload: { upload: {
description: 'This is the new file to be created and uploaded.', description: 'This is the new file to be created and uploaded.',
@@ -80,18 +68,9 @@ const load = (parseGraphQLSchema) => {
}, },
}); });
parseGraphQLSchema.addGraphQLType( parseGraphQLSchema.addGraphQLType(createMutation.args.input.type.ofType, true, true);
createMutation.args.input.type.ofType,
true,
true
);
parseGraphQLSchema.addGraphQLType(createMutation.type, true, true); parseGraphQLSchema.addGraphQLType(createMutation.type, true, true);
parseGraphQLSchema.addGraphQLMutation( parseGraphQLSchema.addGraphQLMutation('createFile', createMutation, true, true);
'createFile',
createMutation,
true,
true
);
}; };
export { load, handleUpload }; export { load, handleUpload };

View File

@@ -24,8 +24,7 @@ const load = parseGraphQLSchema => {
const callCloudCodeMutation = mutationWithClientMutationId({ const callCloudCodeMutation = mutationWithClientMutationId({
name: 'CallCloudCode', name: 'CallCloudCode',
description: description: 'The callCloudCode mutation can be used to invoke a cloud code function.',
'The callCloudCode mutation can be used to invoke a cloud code function.',
inputFields: { inputFields: {
functionName: { functionName: {
description: 'This is the function to be called.', description: 'This is the function to be called.',
@@ -38,8 +37,7 @@ const load = parseGraphQLSchema => {
}, },
outputFields: { outputFields: {
result: { result: {
description: description: 'This is the result value of the cloud code function execution.',
'This is the result value of the cloud code function execution.',
type: defaultGraphQLTypes.ANY, type: defaultGraphQLTypes.ANY,
}, },
}, },
@@ -49,15 +47,17 @@ const load = parseGraphQLSchema => {
const { config, auth, info } = context; const { config, auth, info } = context;
return { return {
result: (await FunctionsRouter.handleCloudFunction({ result: (
params: { await FunctionsRouter.handleCloudFunction({
functionName, params: {
}, functionName,
config, },
auth, config,
info, auth,
body: params, info,
})).response.result, body: params,
})
).response.result,
}; };
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
@@ -65,18 +65,9 @@ const load = parseGraphQLSchema => {
}, },
}); });
parseGraphQLSchema.addGraphQLType( parseGraphQLSchema.addGraphQLType(callCloudCodeMutation.args.input.type.ofType, true, true);
callCloudCodeMutation.args.input.type.ofType,
true,
true
);
parseGraphQLSchema.addGraphQLType(callCloudCodeMutation.type, true, true); parseGraphQLSchema.addGraphQLType(callCloudCodeMutation.type, true, true);
parseGraphQLSchema.addGraphQLMutation( parseGraphQLSchema.addGraphQLMutation('callCloudCode', callCloudCodeMutation, true, true);
'callCloudCode',
callCloudCodeMutation,
true,
true
);
} }
}; };

View File

@@ -2,10 +2,7 @@ import { GraphQLNonNull } from 'graphql';
import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay'; import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay';
import getFieldNames from 'graphql-list-fields'; import getFieldNames from 'graphql-list-fields';
import * as defaultGraphQLTypes from './defaultGraphQLTypes'; import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import { import { extractKeysAndInclude, getParseClassMutationConfig } from '../parseGraphQLUtils';
extractKeysAndInclude,
getParseClassMutationConfig,
} from '../parseGraphQLUtils';
import * as objectsMutations from '../helpers/objectsMutations'; import * as objectsMutations from '../helpers/objectsMutations';
import * as objectsQueries from '../helpers/objectsQueries'; import * as objectsQueries from '../helpers/objectsQueries';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController'; import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
@@ -18,17 +15,10 @@ const getOnlyRequiredFields = (
includedFieldsString, includedFieldsString,
nativeObjectFields nativeObjectFields
) => { ) => {
const includedFields = includedFieldsString const includedFields = includedFieldsString ? includedFieldsString.split(',') : [];
? includedFieldsString.split(',') const selectedFields = selectedFieldsString ? selectedFieldsString.split(',') : [];
: [];
const selectedFields = selectedFieldsString
? selectedFieldsString.split(',')
: [];
const missingFields = selectedFields const missingFields = selectedFields
.filter( .filter(field => !nativeObjectFields.includes(field) || includedFields.includes(field))
field =>
!nativeObjectFields.includes(field) || includedFields.includes(field)
)
.join(','); .join(',');
if (!missingFields.length) { if (!missingFields.length) {
return { needGet: false, keys: '' }; return { needGet: false, keys: '' };
@@ -37,15 +27,10 @@ const getOnlyRequiredFields = (
} }
}; };
const load = function( const load = function (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseGraphQLClassConfig) {
parseGraphQLSchema,
parseClass,
parseClassConfig: ?ParseGraphQLClassConfig
) {
const className = parseClass.className; const className = parseClass.className;
const graphQLClassName = transformClassNameToGraphQL(className); const graphQLClassName = transformClassNameToGraphQL(className);
const getGraphQLQueryName = const getGraphQLQueryName = graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
const { const {
create: isCreateEnabled = true, create: isCreateEnabled = true,
@@ -63,24 +48,20 @@ const load = function(
} = parseGraphQLSchema.parseClassTypes[className]; } = parseGraphQLSchema.parseClassTypes[className];
if (isCreateEnabled) { if (isCreateEnabled) {
const createGraphQLMutationName = const createGraphQLMutationName = createAlias || `create${graphQLClassName}`;
createAlias || `create${graphQLClassName}`;
const createGraphQLMutation = mutationWithClientMutationId({ const createGraphQLMutation = mutationWithClientMutationId({
name: `Create${graphQLClassName}`, name: `Create${graphQLClassName}`,
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${graphQLClassName} class.`, description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${graphQLClassName} class.`,
inputFields: { inputFields: {
fields: { fields: {
description: description: 'These are the fields that will be used to create the new object.',
'These are the fields that will be used to create the new object.',
type: classGraphQLCreateType || defaultGraphQLTypes.OBJECT, type: classGraphQLCreateType || defaultGraphQLTypes.OBJECT,
}, },
}, },
outputFields: { outputFields: {
[getGraphQLQueryName]: { [getGraphQLQueryName]: {
description: 'This is the created object.', description: 'This is the created object.',
type: new GraphQLNonNull( type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
}, },
}, },
mutateAndGetPayload: async (args, context, mutationInfo) => { mutateAndGetPayload: async (args, context, mutationInfo) => {
@@ -106,12 +87,12 @@ const load = function(
.filter(field => field.startsWith(`${getGraphQLQueryName}.`)) .filter(field => field.startsWith(`${getGraphQLQueryName}.`))
.map(field => field.replace(`${getGraphQLQueryName}.`, '')); .map(field => field.replace(`${getGraphQLQueryName}.`, ''));
const { keys, include } = extractKeysAndInclude(selectedFields); const { keys, include } = extractKeysAndInclude(selectedFields);
const { keys: requiredKeys, needGet } = getOnlyRequiredFields( const { keys: requiredKeys, needGet } = getOnlyRequiredFields(fields, keys, include, [
fields, 'id',
keys, 'objectId',
include, 'createdAt',
['id', 'objectId', 'createdAt', 'updatedAt'] 'updatedAt',
); ]);
const needToGetAllKeys = objectsQueries.needToGetAllKeys( const needToGetAllKeys = objectsQueries.needToGetAllKeys(
parseClass.fields, parseClass.fields,
keys, keys,
@@ -160,38 +141,29 @@ const load = function(
}); });
if ( if (
parseGraphQLSchema.addGraphQLType( parseGraphQLSchema.addGraphQLType(createGraphQLMutation.args.input.type.ofType) &&
createGraphQLMutation.args.input.type.ofType
) &&
parseGraphQLSchema.addGraphQLType(createGraphQLMutation.type) parseGraphQLSchema.addGraphQLType(createGraphQLMutation.type)
) { ) {
parseGraphQLSchema.addGraphQLMutation( parseGraphQLSchema.addGraphQLMutation(createGraphQLMutationName, createGraphQLMutation);
createGraphQLMutationName,
createGraphQLMutation
);
} }
} }
if (isUpdateEnabled) { if (isUpdateEnabled) {
const updateGraphQLMutationName = const updateGraphQLMutationName = updateAlias || `update${graphQLClassName}`;
updateAlias || `update${graphQLClassName}`;
const updateGraphQLMutation = mutationWithClientMutationId({ const updateGraphQLMutation = mutationWithClientMutationId({
name: `Update${graphQLClassName}`, name: `Update${graphQLClassName}`,
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`, description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`,
inputFields: { inputFields: {
id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT, id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT,
fields: { fields: {
description: description: 'These are the fields that will be used to update the object.',
'These are the fields that will be used to update the object.',
type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT, type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT,
}, },
}, },
outputFields: { outputFields: {
[getGraphQLQueryName]: { [getGraphQLQueryName]: {
description: 'This is the updated object.', description: 'This is the updated object.',
type: new GraphQLNonNull( type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
}, },
}, },
mutateAndGetPayload: async (args, context, mutationInfo) => { mutateAndGetPayload: async (args, context, mutationInfo) => {
@@ -225,12 +197,11 @@ const load = function(
.filter(field => field.startsWith(`${getGraphQLQueryName}.`)) .filter(field => field.startsWith(`${getGraphQLQueryName}.`))
.map(field => field.replace(`${getGraphQLQueryName}.`, '')); .map(field => field.replace(`${getGraphQLQueryName}.`, ''));
const { keys, include } = extractKeysAndInclude(selectedFields); const { keys, include } = extractKeysAndInclude(selectedFields);
const { keys: requiredKeys, needGet } = getOnlyRequiredFields( const { keys: requiredKeys, needGet } = getOnlyRequiredFields(fields, keys, include, [
fields, 'id',
keys, 'objectId',
include, 'updatedAt',
['id', 'objectId', 'updatedAt'] ]);
);
const needToGetAllKeys = objectsQueries.needToGetAllKeys( const needToGetAllKeys = objectsQueries.needToGetAllKeys(
parseClass.fields, parseClass.fields,
keys, keys,
@@ -279,21 +250,15 @@ const load = function(
}); });
if ( if (
parseGraphQLSchema.addGraphQLType( parseGraphQLSchema.addGraphQLType(updateGraphQLMutation.args.input.type.ofType) &&
updateGraphQLMutation.args.input.type.ofType
) &&
parseGraphQLSchema.addGraphQLType(updateGraphQLMutation.type) parseGraphQLSchema.addGraphQLType(updateGraphQLMutation.type)
) { ) {
parseGraphQLSchema.addGraphQLMutation( parseGraphQLSchema.addGraphQLMutation(updateGraphQLMutationName, updateGraphQLMutation);
updateGraphQLMutationName,
updateGraphQLMutation
);
} }
} }
if (isDestroyEnabled) { if (isDestroyEnabled) {
const deleteGraphQLMutationName = const deleteGraphQLMutationName = destroyAlias || `delete${graphQLClassName}`;
destroyAlias || `delete${graphQLClassName}`;
const deleteGraphQLMutation = mutationWithClientMutationId({ const deleteGraphQLMutation = mutationWithClientMutationId({
name: `Delete${graphQLClassName}`, name: `Delete${graphQLClassName}`,
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`, description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`,
@@ -303,9 +268,7 @@ const load = function(
outputFields: { outputFields: {
[getGraphQLQueryName]: { [getGraphQLQueryName]: {
description: 'This is the deleted object.', description: 'This is the deleted object.',
type: new GraphQLNonNull( type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
}, },
}, },
mutateAndGetPayload: async (args, context, mutationInfo) => { mutateAndGetPayload: async (args, context, mutationInfo) => {
@@ -324,11 +287,7 @@ const load = function(
.map(field => field.replace(`${getGraphQLQueryName}.`, '')); .map(field => field.replace(`${getGraphQLQueryName}.`, ''));
const { keys, include } = extractKeysAndInclude(selectedFields); const { keys, include } = extractKeysAndInclude(selectedFields);
let optimizedObject = {}; let optimizedObject = {};
if ( if (keys && keys.split(',').filter(key => !['id', 'objectId'].includes(key)).length > 0) {
keys &&
keys.split(',').filter(key => !['id', 'objectId'].includes(key))
.length > 0
) {
optimizedObject = await objectsQueries.getObject( optimizedObject = await objectsQueries.getObject(
className, className,
id, id,
@@ -342,13 +301,7 @@ const load = function(
parseGraphQLSchema.parseClasses parseGraphQLSchema.parseClasses
); );
} }
await objectsMutations.deleteObject( await objectsMutations.deleteObject(className, id, config, auth, info);
className,
id,
config,
auth,
info
);
return { return {
[getGraphQLQueryName]: { [getGraphQLQueryName]: {
objectId: id, objectId: id,
@@ -362,15 +315,10 @@ const load = function(
}); });
if ( if (
parseGraphQLSchema.addGraphQLType( parseGraphQLSchema.addGraphQLType(deleteGraphQLMutation.args.input.type.ofType) &&
deleteGraphQLMutation.args.input.type.ofType
) &&
parseGraphQLSchema.addGraphQLType(deleteGraphQLMutation.type) parseGraphQLSchema.addGraphQLType(deleteGraphQLMutation.type)
) { ) {
parseGraphQLSchema.addGraphQLMutation( parseGraphQLSchema.addGraphQLMutation(deleteGraphQLMutationName, deleteGraphQLMutation);
deleteGraphQLMutationName,
deleteGraphQLMutation
);
} }
} }
}; };

View File

@@ -8,20 +8,11 @@ import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLControlle
import { transformClassNameToGraphQL } from '../transformers/className'; import { transformClassNameToGraphQL } from '../transformers/className';
import { extractKeysAndInclude } from '../parseGraphQLUtils'; import { extractKeysAndInclude } from '../parseGraphQLUtils';
const getParseClassQueryConfig = function ( const getParseClassQueryConfig = function (parseClassConfig: ?ParseGraphQLClassConfig) {
parseClassConfig: ?ParseGraphQLClassConfig
) {
return (parseClassConfig && parseClassConfig.query) || {}; return (parseClassConfig && parseClassConfig.query) || {};
}; };
const getQuery = async ( const getQuery = async (parseClass, _source, args, context, queryInfo, parseClasses) => {
parseClass,
_source,
args,
context,
queryInfo,
parseClasses
) => {
let { id } = args; let { id } = args;
const { options } = args; const { options } = args;
const { readPreference, includeReadPreference } = options || {}; const { readPreference, includeReadPreference } = options || {};
@@ -50,11 +41,7 @@ const getQuery = async (
); );
}; };
const load = function ( const load = function (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseGraphQLClassConfig) {
parseGraphQLSchema,
parseClass,
parseClassConfig: ?ParseGraphQLClassConfig
) {
const className = parseClass.className; const className = parseClass.className;
const graphQLClassName = transformClassNameToGraphQL(className); const graphQLClassName = transformClassNameToGraphQL(className);
const { const {
@@ -71,8 +58,7 @@ const load = function (
} = parseGraphQLSchema.parseClassTypes[className]; } = parseGraphQLSchema.parseClassTypes[className];
if (isGetEnabled) { if (isGetEnabled) {
const lowerCaseClassName = const lowerCaseClassName = graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
const getGraphQLQueryName = getAlias || lowerCaseClassName; const getGraphQLQueryName = getAlias || lowerCaseClassName;
@@ -82,9 +68,7 @@ const load = function (
id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT, id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT,
options: defaultGraphQLTypes.READ_OPTIONS_ATT, options: defaultGraphQLTypes.READ_OPTIONS_ATT,
}, },
type: new GraphQLNonNull( type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, queryInfo) { async resolve(_source, args, context, queryInfo) {
try { try {
return await getQuery( return await getQuery(
@@ -103,41 +87,25 @@ const load = function (
} }
if (isFindEnabled) { if (isFindEnabled) {
const lowerCaseClassName = const lowerCaseClassName = graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
const findGraphQLQueryName = findAlias || pluralize(lowerCaseClassName); const findGraphQLQueryName = findAlias || pluralize(lowerCaseClassName);
parseGraphQLSchema.addGraphQLQuery(findGraphQLQueryName, { parseGraphQLSchema.addGraphQLQuery(findGraphQLQueryName, {
description: `The ${findGraphQLQueryName} query can be used to find objects of the ${graphQLClassName} class.`, description: `The ${findGraphQLQueryName} query can be used to find objects of the ${graphQLClassName} class.`,
args: classGraphQLFindArgs, args: classGraphQLFindArgs,
type: new GraphQLNonNull( type: new GraphQLNonNull(classGraphQLFindResultType || defaultGraphQLTypes.OBJECT),
classGraphQLFindResultType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, queryInfo) { async resolve(_source, args, context, queryInfo) {
try { try {
const { const { where, order, skip, first, after, last, before, options } = args;
where, const { readPreference, includeReadPreference, subqueryReadPreference } = options || {};
order,
skip,
first,
after,
last,
before,
options,
} = args;
const {
readPreference,
includeReadPreference,
subqueryReadPreference,
} = options || {};
const { config, auth, info } = context; const { config, auth, info } = context;
const selectedFields = getFieldNames(queryInfo); const selectedFields = getFieldNames(queryInfo);
const { keys, include } = extractKeysAndInclude( const { keys, include } = extractKeysAndInclude(
selectedFields selectedFields
.filter((field) => field.startsWith('edges.node.')) .filter(field => field.startsWith('edges.node.'))
.map((field) => field.replace('edges.node.', '')) .map(field => field.replace('edges.node.', ''))
); );
const parseOrder = order && order.join(','); const parseOrder = order && order.join(',');

View File

@@ -8,11 +8,7 @@ import {
GraphQLBoolean, GraphQLBoolean,
GraphQLEnumType, GraphQLEnumType,
} from 'graphql'; } from 'graphql';
import { import { globalIdField, connectionArgs, connectionDefinitions } from 'graphql-relay';
globalIdField,
connectionArgs,
connectionDefinitions,
} from 'graphql-relay';
import getFieldNames from 'graphql-list-fields'; import getFieldNames from 'graphql-list-fields';
import * as defaultGraphQLTypes from './defaultGraphQLTypes'; import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import * as objectsQueries from '../helpers/objectsQueries'; import * as objectsQueries from '../helpers/objectsQueries';
@@ -21,14 +17,9 @@ import { transformClassNameToGraphQL } from '../transformers/className';
import { transformInputTypeToGraphQL } from '../transformers/inputType'; import { transformInputTypeToGraphQL } from '../transformers/inputType';
import { transformOutputTypeToGraphQL } from '../transformers/outputType'; import { transformOutputTypeToGraphQL } from '../transformers/outputType';
import { transformConstraintTypeToGraphQL } from '../transformers/constraintType'; import { transformConstraintTypeToGraphQL } from '../transformers/constraintType';
import { import { extractKeysAndInclude, getParseClassMutationConfig } from '../parseGraphQLUtils';
extractKeysAndInclude,
getParseClassMutationConfig,
} from '../parseGraphQLUtils';
const getParseClassTypeConfig = function ( const getParseClassTypeConfig = function (parseClassConfig: ?ParseGraphQLClassConfig) {
parseClassConfig: ?ParseGraphQLClassConfig
) {
return (parseClassConfig && parseClassConfig.type) || {}; return (parseClassConfig && parseClassConfig.type) || {};
}; };
@@ -51,22 +42,19 @@ const getInputFieldsAndConstraints = function (
let classSortFields; let classSortFields;
// All allowed customs fields // All allowed customs fields
const classCustomFields = classFields.filter((field) => { const classCustomFields = classFields.filter(field => {
return ( return !Object.keys(defaultGraphQLTypes.PARSE_OBJECT_FIELDS).includes(field) && field !== 'id';
!Object.keys(defaultGraphQLTypes.PARSE_OBJECT_FIELDS).includes(field) &&
field !== 'id'
);
}); });
if (allowedInputFields && allowedInputFields.create) { if (allowedInputFields && allowedInputFields.create) {
classCreateFields = classCustomFields.filter((field) => { classCreateFields = classCustomFields.filter(field => {
return allowedInputFields.create.includes(field); return allowedInputFields.create.includes(field);
}); });
} else { } else {
classCreateFields = classCustomFields; classCreateFields = classCustomFields;
} }
if (allowedInputFields && allowedInputFields.update) { if (allowedInputFields && allowedInputFields.update) {
classUpdateFields = classCustomFields.filter((field) => { classUpdateFields = classCustomFields.filter(field => {
return allowedInputFields.update.includes(field); return allowedInputFields.update.includes(field);
}); });
} else { } else {
@@ -74,7 +62,7 @@ const getInputFieldsAndConstraints = function (
} }
if (allowedOutputFields) { if (allowedOutputFields) {
classOutputFields = classCustomFields.filter((field) => { classOutputFields = classCustomFields.filter(field => {
return allowedOutputFields.includes(field); return allowedOutputFields.includes(field);
}); });
} else { } else {
@@ -82,13 +70,11 @@ const getInputFieldsAndConstraints = function (
} }
// Filters the "password" field from class _User // Filters the "password" field from class _User
if (parseClass.className === '_User') { if (parseClass.className === '_User') {
classOutputFields = classOutputFields.filter( classOutputFields = classOutputFields.filter(outputField => outputField !== 'password');
(outputField) => outputField !== 'password'
);
} }
if (allowedConstraintFields) { if (allowedConstraintFields) {
classConstraintFields = classCustomFields.filter((field) => { classConstraintFields = classCustomFields.filter(field => {
return allowedConstraintFields.includes(field); return allowedConstraintFields.includes(field);
}); });
} else { } else {
@@ -107,7 +93,7 @@ const getInputFieldsAndConstraints = function (
}); });
} }
} else { } else {
classSortFields = classFields.map((field) => { classSortFields = classFields.map(field => {
return { field, asc: true, desc: true }; return { field, asc: true, desc: true };
}); });
} }
@@ -121,11 +107,7 @@ const getInputFieldsAndConstraints = function (
}; };
}; };
const load = ( const load = (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseGraphQLClassConfig) => {
parseGraphQLSchema,
parseClass,
parseClassConfig: ?ParseGraphQLClassConfig
) => {
const className = parseClass.className; const className = parseClass.className;
const graphQLClassName = transformClassNameToGraphQL(className); const graphQLClassName = transformClassNameToGraphQL(className);
const { const {
@@ -159,8 +141,7 @@ const load = (
[field]: { [field]: {
description: `This is the object ${field}.`, description: `This is the object ${field}.`,
type: type:
(className === '_User' && (className === '_User' && (field === 'username' || field === 'password')) ||
(field === 'username' || field === 'password')) ||
parseClass.fields[field].required parseClass.fields[field].required
? new GraphQLNonNull(type) ? new GraphQLNonNull(type)
: type, : type,
@@ -175,9 +156,7 @@ const load = (
} }
), ),
}); });
classGraphQLCreateType = parseGraphQLSchema.addGraphQLType( classGraphQLCreateType = parseGraphQLSchema.addGraphQLType(classGraphQLCreateType);
classGraphQLCreateType
);
const classGraphQLUpdateTypeName = `Update${graphQLClassName}FieldsInput`; const classGraphQLUpdateTypeName = `Update${graphQLClassName}FieldsInput`;
let classGraphQLUpdateType = new GraphQLInputObjectType({ let classGraphQLUpdateType = new GraphQLInputObjectType({
@@ -208,9 +187,7 @@ const load = (
} }
), ),
}); });
classGraphQLUpdateType = parseGraphQLSchema.addGraphQLType( classGraphQLUpdateType = parseGraphQLSchema.addGraphQLType(classGraphQLUpdateType);
classGraphQLUpdateType
);
const classGraphQLPointerTypeName = `${graphQLClassName}PointerInput`; const classGraphQLPointerTypeName = `${graphQLClassName}PointerInput`;
let classGraphQLPointerType = new GraphQLInputObjectType({ let classGraphQLPointerType = new GraphQLInputObjectType({
@@ -233,8 +210,7 @@ const load = (
}, },
}); });
classGraphQLPointerType = classGraphQLPointerType =
parseGraphQLSchema.addGraphQLType(classGraphQLPointerType) || parseGraphQLSchema.addGraphQLType(classGraphQLPointerType) || defaultGraphQLTypes.OBJECT;
defaultGraphQLTypes.OBJECT;
const classGraphQLRelationTypeName = `${graphQLClassName}RelationInput`; const classGraphQLRelationTypeName = `${graphQLClassName}RelationInput`;
let classGraphQLRelationType = new GraphQLInputObjectType({ let classGraphQLRelationType = new GraphQLInputObjectType({
@@ -261,8 +237,7 @@ const load = (
}, },
}); });
classGraphQLRelationType = classGraphQLRelationType =
parseGraphQLSchema.addGraphQLType(classGraphQLRelationType) || parseGraphQLSchema.addGraphQLType(classGraphQLRelationType) || defaultGraphQLTypes.OBJECT;
defaultGraphQLTypes.OBJECT;
const classGraphQLConstraintsTypeName = `${graphQLClassName}WhereInput`; const classGraphQLConstraintsTypeName = `${graphQLClassName}WhereInput`;
let classGraphQLConstraintsType = new GraphQLInputObjectType({ let classGraphQLConstraintsType = new GraphQLInputObjectType({
@@ -310,8 +285,7 @@ const load = (
}), }),
}); });
classGraphQLConstraintsType = classGraphQLConstraintsType =
parseGraphQLSchema.addGraphQLType(classGraphQLConstraintsType) || parseGraphQLSchema.addGraphQLType(classGraphQLConstraintsType) || defaultGraphQLTypes.OBJECT;
defaultGraphQLTypes.OBJECT;
const classGraphQLRelationConstraintsTypeName = `${graphQLClassName}RelationWhereInput`; const classGraphQLRelationConstraintsTypeName = `${graphQLClassName}RelationWhereInput`;
let classGraphQLRelationConstraintsType = new GraphQLInputObjectType({ 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.`, description: `The ${classGraphQLRelationConstraintsTypeName} input type is used in operations that involve filtering objects of ${graphQLClassName} class.`,
fields: () => ({ fields: () => ({
have: { have: {
description: description: 'Run a relational/pointer query where at least one child object can match.',
'Run a relational/pointer query where at least one child object can match.',
type: classGraphQLConstraintsType, type: classGraphQLConstraintsType,
}, },
haveNot: { haveNot: {
@@ -357,14 +330,11 @@ const load = (
return updatedSortFields; return updatedSortFields;
}, {}), }, {}),
}); });
classGraphQLOrderType = parseGraphQLSchema.addGraphQLType( classGraphQLOrderType = parseGraphQLSchema.addGraphQLType(classGraphQLOrderType);
classGraphQLOrderType
);
const classGraphQLFindArgs = { const classGraphQLFindArgs = {
where: { where: {
description: description: 'These are the conditions that the objects need to match in order to be found.',
'These are the conditions that the objects need to match in order to be found.',
type: classGraphQLConstraintsType, type: classGraphQLConstraintsType,
}, },
order: { order: {
@@ -378,12 +348,9 @@ const load = (
options: defaultGraphQLTypes.READ_OPTIONS_ATT, options: defaultGraphQLTypes.READ_OPTIONS_ATT,
}; };
const classGraphQLOutputTypeName = `${graphQLClassName}`; const classGraphQLOutputTypeName = `${graphQLClassName}`;
const interfaces = [ const interfaces = [defaultGraphQLTypes.PARSE_OBJECT, parseGraphQLSchema.relayNodeInterface];
defaultGraphQLTypes.PARSE_OBJECT,
parseGraphQLSchema.relayNodeInterface,
];
const parseObjectFields = { const parseObjectFields = {
id: globalIdField(className, (obj) => obj.objectId), id: globalIdField(className, obj => obj.objectId),
...defaultGraphQLTypes.PARSE_OBJECT_FIELDS, ...defaultGraphQLTypes.PARSE_OBJECT_FIELDS,
}; };
const outputFields = () => { const outputFields = () => {
@@ -395,44 +362,26 @@ const load = (
); );
if (parseClass.fields[field].type === 'Relation') { if (parseClass.fields[field].type === 'Relation') {
const targetParseClassTypes = const targetParseClassTypes =
parseGraphQLSchema.parseClassTypes[ parseGraphQLSchema.parseClassTypes[parseClass.fields[field].targetClass];
parseClass.fields[field].targetClass const args = targetParseClassTypes ? targetParseClassTypes.classGraphQLFindArgs : undefined;
];
const args = targetParseClassTypes
? targetParseClassTypes.classGraphQLFindArgs
: undefined;
return { return {
...fields, ...fields,
[field]: { [field]: {
description: `This is the object ${field}.`, description: `This is the object ${field}.`,
args, args,
type: parseClass.fields[field].required type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
? new GraphQLNonNull(type)
: type,
async resolve(source, args, context, queryInfo) { async resolve(source, args, context, queryInfo) {
try { try {
const { const { where, order, skip, first, after, last, before, options } = args;
where, const { readPreference, includeReadPreference, subqueryReadPreference } =
order, options || {};
skip,
first,
after,
last,
before,
options,
} = args;
const {
readPreference,
includeReadPreference,
subqueryReadPreference,
} = options || {};
const { config, auth, info } = context; const { config, auth, info } = context;
const selectedFields = getFieldNames(queryInfo); const selectedFields = getFieldNames(queryInfo);
const { keys, include } = extractKeysAndInclude( const { keys, include } = extractKeysAndInclude(
selectedFields selectedFields
.filter((field) => field.startsWith('edges.node.')) .filter(field => field.startsWith('edges.node.'))
.map((field) => field.replace('edges.node.', '')) .map(field => field.replace('edges.node.', ''))
); );
const parseOrder = order && order.join(','); const parseOrder = order && order.join(',');
@@ -478,12 +427,10 @@ const load = (
...fields, ...fields,
[field]: { [field]: {
description: `This is the object ${field}.`, description: `This is the object ${field}.`,
type: parseClass.fields[field].required type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
? new GraphQLNonNull(type)
: type,
async resolve(source) { async resolve(source) {
if (source[field] && source[field].coordinates) { if (source[field] && source[field].coordinates) {
return source[field].coordinates.map((coordinate) => ({ return source[field].coordinates.map(coordinate => ({
latitude: coordinate[0], latitude: coordinate[0],
longitude: coordinate[1], longitude: coordinate[1],
})); }));
@@ -498,17 +445,11 @@ const load = (
...fields, ...fields,
[field]: { [field]: {
description: `Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments`, description: `Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments`,
type: parseClass.fields[field].required type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
? new GraphQLNonNull(type)
: type,
async resolve(source) { async resolve(source) {
if (!source[field]) return null; if (!source[field]) return null;
return source[field].map(async (elem) => { return source[field].map(async elem => {
if ( if (elem.className && elem.objectId && elem.__type === 'Object') {
elem.className &&
elem.objectId &&
elem.__type === 'Object'
) {
return elem; return elem;
} else { } else {
return { value: elem }; return { value: elem };
@@ -522,9 +463,7 @@ const load = (
...fields, ...fields,
[field]: { [field]: {
description: `This is the object ${field}.`, description: `This is the object ${field}.`,
type: parseClass.fields[field].required type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
? new GraphQLNonNull(type)
: type,
}, },
}; };
} else { } else {
@@ -538,9 +477,7 @@ const load = (
interfaces, interfaces,
fields: outputFields, fields: outputFields,
}); });
classGraphQLOutputType = parseGraphQLSchema.addGraphQLType( classGraphQLOutputType = parseGraphQLSchema.addGraphQLType(classGraphQLOutputType);
classGraphQLOutputType
);
const { connectionType, edgeType } = connectionDefinitions({ const { connectionType, edgeType } = connectionDefinitions({
name: graphQLClassName, name: graphQLClassName,

View File

@@ -21,15 +21,17 @@ const load = parseGraphQLSchema => {
functionName = this.args.to; functionName = this.args.to;
} }
return (await FunctionsRouter.handleCloudFunction({ return (
params: { await FunctionsRouter.handleCloudFunction({
functionName, params: {
}, functionName,
config, },
auth, config,
info, auth,
body: args, info,
})).response.result; body: args,
})
).response.result;
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }

View File

@@ -2,10 +2,7 @@ import Parse from 'parse/node';
import { GraphQLNonNull } from 'graphql'; import { GraphQLNonNull } from 'graphql';
import { mutationWithClientMutationId } from 'graphql-relay'; import { mutationWithClientMutationId } from 'graphql-relay';
import * as schemaTypes from './schemaTypes'; import * as schemaTypes from './schemaTypes';
import { import { transformToParse, transformToGraphQL } from '../transformers/schemaFields';
transformToParse,
transformToGraphQL,
} from '../transformers/schemaFields';
import { enforceMasterKeyAccess } from '../parseGraphQLUtils'; import { enforceMasterKeyAccess } from '../parseGraphQLUtils';
import { getClass } from './schemaQueries'; import { getClass } from './schemaQueries';
@@ -42,10 +39,7 @@ const load = parseGraphQLSchema => {
} }
const schema = await config.database.loadSchema({ clearCache: true }); const schema = await config.database.loadSchema({ clearCache: true });
const parseClass = await schema.addClassIfNotExists( const parseClass = await schema.addClassIfNotExists(name, transformToParse(schemaFields));
name,
transformToParse(schemaFields)
);
return { return {
class: { class: {
name: parseClass.className, name: parseClass.className,
@@ -58,18 +52,9 @@ const load = parseGraphQLSchema => {
}, },
}); });
parseGraphQLSchema.addGraphQLType( parseGraphQLSchema.addGraphQLType(createClassMutation.args.input.type.ofType, true, true);
createClassMutation.args.input.type.ofType,
true,
true
);
parseGraphQLSchema.addGraphQLType(createClassMutation.type, true, true); parseGraphQLSchema.addGraphQLType(createClassMutation.type, true, true);
parseGraphQLSchema.addGraphQLMutation( parseGraphQLSchema.addGraphQLMutation('createClass', createClassMutation, true, true);
'createClass',
createClassMutation,
true,
true
);
const updateClassMutation = mutationWithClientMutationId({ const updateClassMutation = mutationWithClientMutationId({
name: 'UpdateClass', name: 'UpdateClass',
@@ -123,23 +108,13 @@ const load = parseGraphQLSchema => {
}, },
}); });
parseGraphQLSchema.addGraphQLType( parseGraphQLSchema.addGraphQLType(updateClassMutation.args.input.type.ofType, true, true);
updateClassMutation.args.input.type.ofType,
true,
true
);
parseGraphQLSchema.addGraphQLType(updateClassMutation.type, true, true); parseGraphQLSchema.addGraphQLType(updateClassMutation.type, true, true);
parseGraphQLSchema.addGraphQLMutation( parseGraphQLSchema.addGraphQLMutation('updateClass', updateClassMutation, true, true);
'updateClass',
updateClassMutation,
true,
true
);
const deleteClassMutation = mutationWithClientMutationId({ const deleteClassMutation = mutationWithClientMutationId({
name: 'DeleteClass', name: 'DeleteClass',
description: description: 'The deleteClass mutation can be used to delete an existing object class.',
'The deleteClass mutation can be used to delete an existing object class.',
inputFields: { inputFields: {
name: schemaTypes.CLASS_NAME_ATT, name: schemaTypes.CLASS_NAME_ATT,
}, },
@@ -178,18 +153,9 @@ const load = parseGraphQLSchema => {
}, },
}); });
parseGraphQLSchema.addGraphQLType( parseGraphQLSchema.addGraphQLType(deleteClassMutation.args.input.type.ofType, true, true);
deleteClassMutation.args.input.type.ofType,
true,
true
);
parseGraphQLSchema.addGraphQLType(deleteClassMutation.type, true, true); parseGraphQLSchema.addGraphQLType(deleteClassMutation.type, true, true);
parseGraphQLSchema.addGraphQLMutation( parseGraphQLSchema.addGraphQLMutation('deleteClass', deleteClassMutation, true, true);
'deleteClass',
deleteClassMutation,
true,
true
);
}; };
export { load }; export { load };

View File

@@ -9,15 +9,9 @@ const getClass = async (name, schema) => {
return await schema.getOneSchema(name, true); return await schema.getOneSchema(name, true);
} catch (e) { } catch (e) {
if (e === undefined) { if (e === undefined) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${name} does not exist.`);
Parse.Error.INVALID_CLASS_NAME,
`Class ${name} does not exist.`
);
} else { } else {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error.');
Parse.Error.INTERNAL_SERVER_ERROR,
'Database adapter error.'
);
} }
} }
}; };
@@ -26,8 +20,7 @@ const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLQuery( parseGraphQLSchema.addGraphQLQuery(
'class', 'class',
{ {
description: description: 'The class query can be used to retrieve an existing object class.',
'The class query can be used to retrieve an existing object class.',
args: { args: {
name: schemaTypes.CLASS_NAME_ATT, name: schemaTypes.CLASS_NAME_ATT,
}, },
@@ -57,11 +50,8 @@ const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLQuery( parseGraphQLSchema.addGraphQLQuery(
'classes', 'classes',
{ {
description: description: 'The classes query can be used to retrieve the existing object classes.',
'The classes query can be used to retrieve the existing object classes.', type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(schemaTypes.CLASS))),
type: new GraphQLNonNull(
new GraphQLList(new GraphQLNonNull(schemaTypes.CLASS))
),
resolve: async (_source, _args, context) => { resolve: async (_source, _args, context) => {
try { try {
const { config, auth } = context; const { config, auth } = context;

View File

@@ -14,8 +14,7 @@ const SCHEMA_FIELD_NAME_ATT = {
const SCHEMA_FIELD_INPUT = new GraphQLInputObjectType({ const SCHEMA_FIELD_INPUT = new GraphQLInputObjectType({
name: 'SchemaFieldInput', name: 'SchemaFieldInput',
description: description: 'The SchemaFieldInput is used to specify a field of an object class schema.',
'The SchemaFieldInput is used to specify a field of an object class schema.',
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
}, },
@@ -57,8 +56,7 @@ const SCHEMA_STRING_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_STRING_FIELD = new GraphQLObjectType({ const SCHEMA_STRING_FIELD = new GraphQLObjectType({
name: 'SchemaStringField', name: 'SchemaStringField',
description: description: 'The SchemaStringField is used to return information of a String field.',
'The SchemaStringField is used to return information of a String field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -76,8 +74,7 @@ const SCHEMA_NUMBER_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_NUMBER_FIELD = new GraphQLObjectType({ const SCHEMA_NUMBER_FIELD = new GraphQLObjectType({
name: 'SchemaNumberField', name: 'SchemaNumberField',
description: description: 'The SchemaNumberField is used to return information of a Number field.',
'The SchemaNumberField is used to return information of a Number field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -95,8 +92,7 @@ const SCHEMA_BOOLEAN_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_BOOLEAN_FIELD = new GraphQLObjectType({ const SCHEMA_BOOLEAN_FIELD = new GraphQLObjectType({
name: 'SchemaBooleanField', name: 'SchemaBooleanField',
description: description: 'The SchemaBooleanField is used to return information of a Boolean field.',
'The SchemaBooleanField is used to return information of a Boolean field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -114,8 +110,7 @@ const SCHEMA_ARRAY_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_ARRAY_FIELD = new GraphQLObjectType({ const SCHEMA_ARRAY_FIELD = new GraphQLObjectType({
name: 'SchemaArrayField', name: 'SchemaArrayField',
description: description: 'The SchemaArrayField is used to return information of an Array field.',
'The SchemaArrayField is used to return information of an Array field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -133,8 +128,7 @@ const SCHEMA_OBJECT_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_OBJECT_FIELD = new GraphQLObjectType({ const SCHEMA_OBJECT_FIELD = new GraphQLObjectType({
name: 'SchemaObjectField', name: 'SchemaObjectField',
description: description: 'The SchemaObjectField is used to return information of an Object field.',
'The SchemaObjectField is used to return information of an Object field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -152,8 +146,7 @@ const SCHEMA_DATE_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_DATE_FIELD = new GraphQLObjectType({ const SCHEMA_DATE_FIELD = new GraphQLObjectType({
name: 'SchemaDateField', name: 'SchemaDateField',
description: description: 'The SchemaDateField is used to return information of a Date field.',
'The SchemaDateField is used to return information of a Date field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -171,8 +164,7 @@ const SCHEMA_FILE_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_FILE_FIELD = new GraphQLObjectType({ const SCHEMA_FILE_FIELD = new GraphQLObjectType({
name: 'SchemaFileField', name: 'SchemaFileField',
description: description: 'The SchemaFileField is used to return information of a File field.',
'The SchemaFileField is used to return information of a File field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -190,8 +182,7 @@ const SCHEMA_GEO_POINT_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_GEO_POINT_FIELD = new GraphQLObjectType({ const SCHEMA_GEO_POINT_FIELD = new GraphQLObjectType({
name: 'SchemaGeoPointField', name: 'SchemaGeoPointField',
description: description: 'The SchemaGeoPointField is used to return information of a Geo Point field.',
'The SchemaGeoPointField is used to return information of a Geo Point field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -209,8 +200,7 @@ const SCHEMA_POLYGON_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_POLYGON_FIELD = new GraphQLObjectType({ const SCHEMA_POLYGON_FIELD = new GraphQLObjectType({
name: 'SchemaPolygonField', name: 'SchemaPolygonField',
description: description: 'The SchemaPolygonField is used to return information of a Polygon field.',
'The SchemaPolygonField is used to return information of a Polygon field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -228,8 +218,7 @@ const SCHEMA_BYTES_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_BYTES_FIELD = new GraphQLObjectType({ const SCHEMA_BYTES_FIELD = new GraphQLObjectType({
name: 'SchemaBytesField', name: 'SchemaBytesField',
description: description: 'The SchemaBytesField is used to return information of a Bytes field.',
'The SchemaBytesField is used to return information of a Bytes field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -253,8 +242,7 @@ const SCHEMA_POINTER_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_POINTER_FIELD = new GraphQLObjectType({ const SCHEMA_POINTER_FIELD = new GraphQLObjectType({
name: 'SchemaPointerField', name: 'SchemaPointerField',
description: description: 'The SchemaPointerField is used to return information of a Pointer field.',
'The SchemaPointerField is used to return information of a Pointer field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -274,8 +262,7 @@ const SCHEMA_RELATION_FIELD_INPUT = new GraphQLInputObjectType({
const SCHEMA_RELATION_FIELD = new GraphQLObjectType({ const SCHEMA_RELATION_FIELD = new GraphQLObjectType({
name: 'SchemaRelationField', name: 'SchemaRelationField',
description: description: 'The SchemaRelationField is used to return information of a Relation field.',
'The SchemaRelationField is used to return information of a Relation field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, name: SCHEMA_FIELD_NAME_ATT,
@@ -285,8 +272,7 @@ const SCHEMA_RELATION_FIELD = new GraphQLObjectType({
const SCHEMA_ACL_FIELD = new GraphQLObjectType({ const SCHEMA_ACL_FIELD = new GraphQLObjectType({
name: 'SchemaACLField', name: 'SchemaACLField',
description: description: 'The SchemaACLField is used to return information of an ACL field.',
'The SchemaACLField is used to return information of an ACL field.',
interfaces: [SCHEMA_FIELD], interfaces: [SCHEMA_FIELD],
fields: { fields: {
name: SCHEMA_FIELD_NAME_ATT, 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.`, description: `The CreateClassSchemaInput type is used to specify the schema for a new object class to be created.`,
fields: { fields: {
addStrings: { addStrings: {
description: description: 'These are the String fields to be added to the class schema.',
'These are the String fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_STRING_FIELD_INPUT)), type: new GraphQLList(new GraphQLNonNull(SCHEMA_STRING_FIELD_INPUT)),
}, },
addNumbers: { addNumbers: {
description: description: 'These are the Number fields to be added to the class schema.',
'These are the Number fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_NUMBER_FIELD_INPUT)), type: new GraphQLList(new GraphQLNonNull(SCHEMA_NUMBER_FIELD_INPUT)),
}, },
addBooleans: { addBooleans: {
description: description: 'These are the Boolean fields to be added to the class schema.',
'These are the Boolean fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_BOOLEAN_FIELD_INPUT)), type: new GraphQLList(new GraphQLNonNull(SCHEMA_BOOLEAN_FIELD_INPUT)),
}, },
addArrays: { addArrays: {
description: description: 'These are the Array fields to be added to the class schema.',
'These are the Array fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_ARRAY_FIELD_INPUT)), type: new GraphQLList(new GraphQLNonNull(SCHEMA_ARRAY_FIELD_INPUT)),
}, },
addObjects: { addObjects: {
description: description: 'These are the Object fields to be added to the class schema.',
'These are the Object fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_OBJECT_FIELD_INPUT)), type: new GraphQLList(new GraphQLNonNull(SCHEMA_OBJECT_FIELD_INPUT)),
}, },
addDates: { addDates: {
@@ -336,23 +317,19 @@ const SCHEMA_FIELDS_INPUT = new GraphQLInputObjectType({
type: SCHEMA_GEO_POINT_FIELD_INPUT, type: SCHEMA_GEO_POINT_FIELD_INPUT,
}, },
addPolygons: { addPolygons: {
description: description: 'These are the Polygon fields to be added to the class schema.',
'These are the Polygon fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_POLYGON_FIELD_INPUT)), type: new GraphQLList(new GraphQLNonNull(SCHEMA_POLYGON_FIELD_INPUT)),
}, },
addBytes: { addBytes: {
description: description: 'These are the Bytes fields to be added to the class schema.',
'These are the Bytes fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_BYTES_FIELD_INPUT)), type: new GraphQLList(new GraphQLNonNull(SCHEMA_BYTES_FIELD_INPUT)),
}, },
addPointers: { addPointers: {
description: description: 'These are the Pointer fields to be added to the class schema.',
'These are the Pointer fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_POINTER_FIELD_INPUT)), type: new GraphQLList(new GraphQLNonNull(SCHEMA_POINTER_FIELD_INPUT)),
}, },
addRelations: { addRelations: {
description: description: 'These are the Relation fields to be added to the class schema.',
'These are the Relation fields to be added to the class schema.',
type: new GraphQLList(new GraphQLNonNull(SCHEMA_RELATION_FIELD_INPUT)), type: new GraphQLList(new GraphQLNonNull(SCHEMA_RELATION_FIELD_INPUT)),
}, },
remove: { remove: {
@@ -374,9 +351,7 @@ const CLASS = new GraphQLObjectType({
name: CLASS_NAME_ATT, name: CLASS_NAME_ATT,
schemaFields: { schemaFields: {
description: "These are the schema's fields of the object class.", description: "These are the schema's fields of the object class.",
type: new GraphQLNonNull( type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(SCHEMA_FIELD))),
new GraphQLList(new GraphQLNonNull(SCHEMA_FIELD))
),
}, },
}, },
}); });

View File

@@ -5,18 +5,10 @@ import rest from '../../rest';
import { extractKeysAndInclude } from './parseClassTypes'; import { extractKeysAndInclude } from './parseClassTypes';
import { Auth } from '../../Auth'; import { Auth } from '../../Auth';
const getUserFromSessionToken = async ( const getUserFromSessionToken = async (context, queryInfo, keysPrefix, userId) => {
context,
queryInfo,
keysPrefix,
userId
) => {
const { info, config } = context; const { info, config } = context;
if (!info || !info.sessionToken) { if (!info || !info.sessionToken) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
Parse.Error.INVALID_SESSION_TOKEN,
'Invalid session token'
);
} }
const sessionToken = info.sessionToken; const sessionToken = info.sessionToken;
const selectedFields = getFieldNames(queryInfo) const selectedFields = getFieldNames(queryInfo)
@@ -70,10 +62,7 @@ const getUserFromSessionToken = async (
info.context info.context
); );
if (!response.results || response.results.length == 0) { if (!response.results || response.results.length == 0) {
throw new Parse.Error( throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
Parse.Error.INVALID_SESSION_TOKEN,
'Invalid session token'
);
} else { } else {
const user = response.results[0]; const user = response.results[0];
return { return {
@@ -91,17 +80,11 @@ const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLQuery( parseGraphQLSchema.addGraphQLQuery(
'viewer', 'viewer',
{ {
description: description: 'The viewer query can be used to return the current user data.',
'The viewer query can be used to return the current user data.',
type: new GraphQLNonNull(parseGraphQLSchema.viewerType), type: new GraphQLNonNull(parseGraphQLSchema.viewerType),
async resolve(_source, _args, context, queryInfo) { async resolve(_source, _args, context, queryInfo) {
try { try {
return await getUserFromSessionToken( return await getUserFromSessionToken(context, queryInfo, 'user.', false);
context,
queryInfo,
'user.',
false
);
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }

View File

@@ -1,11 +1,6 @@
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes'; import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
const transformConstraintTypeToGraphQL = ( const transformConstraintTypeToGraphQL = (parseType, targetClass, parseClassTypes, fieldName) => {
parseType,
targetClass,
parseClassTypes,
fieldName
) => {
if (fieldName === 'id' || fieldName === 'objectId') { if (fieldName === 'id' || fieldName === 'objectId') {
return defaultGraphQLTypes.ID_WHERE_INPUT; return defaultGraphQLTypes.ID_WHERE_INPUT;
} }

View File

@@ -1,16 +1,7 @@
import { import { GraphQLString, GraphQLFloat, GraphQLBoolean, GraphQLList } from 'graphql';
GraphQLString,
GraphQLFloat,
GraphQLBoolean,
GraphQLList,
} from 'graphql';
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes'; import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
const transformInputTypeToGraphQL = ( const transformInputTypeToGraphQL = (parseType, targetClass, parseClassTypes) => {
parseType,
targetClass,
parseClassTypes
) => {
switch (parseType) { switch (parseType) {
case 'String': case 'String':
return GraphQLString; return GraphQLString;

View File

@@ -14,19 +14,13 @@ const transformTypes = async (
classGraphQLUpdateType, classGraphQLUpdateType,
config: { isCreateEnabled, isUpdateEnabled }, config: { isCreateEnabled, isUpdateEnabled },
} = parseGraphQLSchema.parseClassTypes[className]; } = parseGraphQLSchema.parseClassTypes[className];
const parseClass = parseGraphQLSchema.parseClasses.find( const parseClass = parseGraphQLSchema.parseClasses.find(clazz => clazz.className === className);
(clazz) => clazz.className === className
);
if (fields) { if (fields) {
const classGraphQLCreateTypeFields = const classGraphQLCreateTypeFields =
isCreateEnabled && classGraphQLCreateType isCreateEnabled && classGraphQLCreateType ? classGraphQLCreateType.getFields() : null;
? classGraphQLCreateType.getFields()
: null;
const classGraphQLUpdateTypeFields = const classGraphQLUpdateTypeFields =
isUpdateEnabled && classGraphQLUpdateType isUpdateEnabled && classGraphQLUpdateType ? classGraphQLUpdateType.getFields() : null;
? classGraphQLUpdateType.getFields() const promises = Object.keys(fields).map(async field => {
: null;
const promises = Object.keys(fields).map(async (field) => {
let inputTypeField; let inputTypeField;
if (inputType === 'create' && classGraphQLCreateTypeFields) { if (inputType === 'create' && classGraphQLCreateTypeFields) {
inputTypeField = classGraphQLCreateTypeFields[field]; inputTypeField = classGraphQLCreateTypeFields[field];
@@ -84,18 +78,15 @@ const transformers = {
} }
throw new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.'); throw new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.');
}, },
polygon: (value) => ({ polygon: value => ({
__type: 'Polygon', __type: 'Polygon',
coordinates: value.map((geoPoint) => [ coordinates: value.map(geoPoint => [geoPoint.latitude, geoPoint.longitude]),
geoPoint.latitude,
geoPoint.longitude,
]),
}), }),
geoPoint: (value) => ({ geoPoint: value => ({
...value, ...value,
__type: 'GeoPoint', __type: 'GeoPoint',
}), }),
ACL: (value) => { ACL: value => {
const parseACL = {}; const parseACL = {};
if (value.public) { if (value.public) {
parseACL['*'] = { parseACL['*'] = {
@@ -104,7 +95,7 @@ const transformers = {
}; };
} }
if (value.users) { if (value.users) {
value.users.forEach((rule) => { value.users.forEach(rule => {
const globalIdObject = fromGlobalId(rule.userId); const globalIdObject = fromGlobalId(rule.userId);
if (globalIdObject.type === '_User') { if (globalIdObject.type === '_User') {
rule.userId = globalIdObject.id; rule.userId = globalIdObject.id;
@@ -116,7 +107,7 @@ const transformers = {
}); });
} }
if (value.roles) { if (value.roles) {
value.roles.forEach((rule) => { value.roles.forEach(rule => {
parseACL[`role:${rule.roleName}`] = { parseACL[`role:${rule.roleName}`] = {
read: rule.read, read: rule.read,
write: rule.write, write: rule.write,
@@ -125,13 +116,7 @@ const transformers = {
} }
return parseACL; return parseACL;
}, },
relation: async ( relation: async (targetClass, field, value, parseGraphQLSchema, { config, auth, info }) => {
targetClass,
field,
value,
parseGraphQLSchema,
{ config, auth, info }
) => {
if (Object.keys(value).length === 0) if (Object.keys(value).length === 0)
throw new Parse.Error( throw new Parse.Error(
Parse.Error.INVALID_POINTER, Parse.Error.INVALID_POINTER,
@@ -147,22 +132,16 @@ const transformers = {
if (value.createAndAdd) { if (value.createAndAdd) {
nestedObjectsToAdd = ( nestedObjectsToAdd = (
await Promise.all( await Promise.all(
value.createAndAdd.map(async (input) => { value.createAndAdd.map(async input => {
const parseFields = await transformTypes('create', input, { const parseFields = await transformTypes('create', input, {
className: targetClass, className: targetClass,
parseGraphQLSchema, parseGraphQLSchema,
req: { config, auth, info }, req: { config, auth, info },
}); });
return objectsMutations.createObject( return objectsMutations.createObject(targetClass, parseFields, config, auth, info);
targetClass,
parseFields,
config,
auth,
info
);
}) })
) )
).map((object) => ({ ).map(object => ({
__type: 'Pointer', __type: 'Pointer',
className: targetClass, className: targetClass,
objectId: object.objectId, objectId: object.objectId,
@@ -171,7 +150,7 @@ const transformers = {
if (value.add || nestedObjectsToAdd.length > 0) { if (value.add || nestedObjectsToAdd.length > 0) {
if (!value.add) value.add = []; if (!value.add) value.add = [];
value.add = value.add.map((input) => { value.add = value.add.map(input => {
const globalIdObject = fromGlobalId(input); const globalIdObject = fromGlobalId(input);
if (globalIdObject.type === targetClass) { if (globalIdObject.type === targetClass) {
input = globalIdObject.id; input = globalIdObject.id;
@@ -191,7 +170,7 @@ const transformers = {
if (value.remove) { if (value.remove) {
op.ops.push({ op.ops.push({
__op: 'RemoveRelation', __op: 'RemoveRelation',
objects: value.remove.map((input) => { objects: value.remove.map(input => {
const globalIdObject = fromGlobalId(input); const globalIdObject = fromGlobalId(input);
if (globalIdObject.type === targetClass) { if (globalIdObject.type === targetClass) {
input = globalIdObject.id; input = globalIdObject.id;
@@ -206,13 +185,7 @@ const transformers = {
} }
return op; return op;
}, },
pointer: async ( pointer: async (targetClass, field, value, parseGraphQLSchema, { config, auth, info }) => {
targetClass,
field,
value,
parseGraphQLSchema,
{ config, auth, info }
) => {
if (Object.keys(value).length > 1 || Object.keys(value).length === 0) if (Object.keys(value).length > 1 || Object.keys(value).length === 0)
throw new Parse.Error( throw new Parse.Error(
Parse.Error.INVALID_POINTER, Parse.Error.INVALID_POINTER,

View File

@@ -1,17 +1,7 @@
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes'; import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
import { import { GraphQLString, GraphQLFloat, GraphQLBoolean, GraphQLList, GraphQLNonNull } from 'graphql';
GraphQLString,
GraphQLFloat,
GraphQLBoolean,
GraphQLList,
GraphQLNonNull,
} from 'graphql';
const transformOutputTypeToGraphQL = ( const transformOutputTypeToGraphQL = (parseType, targetClass, parseClassTypes) => {
parseType,
targetClass,
parseClassTypes
) => {
switch (parseType) { switch (parseType) {
case 'String': case 'String':
return GraphQLString; return GraphQLString;
@@ -41,9 +31,7 @@ const transformOutputTypeToGraphQL = (
parseClassTypes[targetClass] && parseClassTypes[targetClass] &&
parseClassTypes[targetClass].classGraphQLFindResultType parseClassTypes[targetClass].classGraphQLFindResultType
) { ) {
return new GraphQLNonNull( return new GraphQLNonNull(parseClassTypes[targetClass].classGraphQLFindResultType);
parseClassTypes[targetClass].classGraphQLFindResultType
);
} else { } else {
return new GraphQLNonNull(defaultGraphQLTypes.OBJECT); return new GraphQLNonNull(defaultGraphQLTypes.OBJECT);
} }

View File

@@ -51,9 +51,7 @@ const transformQueryConstraintInputToParse = (
parentConstraints, parentConstraints,
parseClasses parseClasses
) => { ) => {
const fields = parseClasses.find( const fields = parseClasses.find(parseClass => parseClass.className === className).fields;
parseClass => parseClass.className === className
).fields;
if (parentFieldName === 'id' && className) { if (parentFieldName === 'id' && className) {
Object.keys(constraints).forEach(constraintName => { Object.keys(constraints).forEach(constraintName => {
const constraintValue = constraints[constraintName]; const constraintValue = constraints[constraintName];
@@ -110,12 +108,7 @@ const transformQueryConstraintInputToParse = (
* } * }
* } * }
*/ */
if ( if (fieldValue.key && fieldValue.value && parentConstraints && parentFieldName) {
fieldValue.key &&
fieldValue.value &&
parentConstraints &&
parentFieldName
) {
delete parentConstraints[parentFieldName]; delete parentConstraints[parentFieldName];
parentConstraints[`${parentFieldName}.${fieldValue.key}`] = { parentConstraints[`${parentFieldName}.${fieldValue.key}`] = {
...parentConstraints[`${parentFieldName}.${fieldValue.key}`], ...parentConstraints[`${parentFieldName}.${fieldValue.key}`],
@@ -123,8 +116,7 @@ const transformQueryConstraintInputToParse = (
}; };
} else if ( } else if (
fields[parentFieldName] && fields[parentFieldName] &&
(fields[parentFieldName].type === 'Pointer' || (fields[parentFieldName].type === 'Pointer' || fields[parentFieldName].type === 'Relation')
fields[parentFieldName].type === 'Relation')
) { ) {
const { targetClass } = fields[parentFieldName]; const { targetClass } = fields[parentFieldName];
if (fieldName === 'exists') { if (fieldName === 'exists') {
@@ -193,11 +185,7 @@ const transformQueryConstraintInputToParse = (
} }
break; break;
case 'box': case 'box':
if ( if (typeof fieldValue === 'object' && fieldValue.bottomLeft && fieldValue.upperRight) {
typeof fieldValue === 'object' &&
fieldValue.bottomLeft &&
fieldValue.upperRight
) {
fieldValue = [ fieldValue = [
{ {
__type: 'GeoPoint', __type: 'GeoPoint',
@@ -221,11 +209,7 @@ const transformQueryConstraintInputToParse = (
} }
break; break;
case 'centerSphere': case 'centerSphere':
if ( if (typeof fieldValue === 'object' && fieldValue.center && fieldValue.distance) {
typeof fieldValue === 'object' &&
fieldValue.center &&
fieldValue.distance
) {
fieldValue = [ fieldValue = [
{ {
__type: 'GeoPoint', __type: 'GeoPoint',

View File

@@ -22,20 +22,12 @@ const transformToParse = (graphQLSchemaFields, existingFields) => {
} }
if ( if (
graphQLSchemaFields.remove && graphQLSchemaFields.remove &&
graphQLSchemaFields.remove.find( graphQLSchemaFields.remove.find(removeField => removeField.name === field.name)
removeField => removeField.name === field.name
)
) { ) {
return parseSchemaFields; return parseSchemaFields;
} }
if ( if (parseSchemaFields[field.name] || (existingFields && existingFields[field.name])) {
parseSchemaFields[field.name] || throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Duplicated field name: ${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') { if (type === 'Relation' || type === 'Pointer') {
return { return {

View File

@@ -127,7 +127,8 @@ module.exports.ParseServerOptions = {
}, },
emailVerifyTokenReuseIfValid: { emailVerifyTokenReuseIfValid: {
env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_REUSE_IF_VALID', 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, action: parsers.booleanParser,
default: false, default: false,
}, },

View File

@@ -1,3 +1,2 @@
const LiveQueryServerOptions = require('../../Options/Definitions') const LiveQueryServerOptions = require('../../Options/Definitions').LiveQueryServerOptions;
.LiveQueryServerOptions;
export default LiveQueryServerOptions; export default LiveQueryServerOptions;

View File

@@ -1,3 +1,2 @@
const ParseServerDefinitions = require('../../Options/Definitions') const ParseServerDefinitions = require('../../Options/Definitions').ParseServerOptions;
.ParseServerOptions;
export default ParseServerDefinitions; export default ParseServerDefinitions;

View File

@@ -5,7 +5,7 @@ let _definitions;
let _reverseDefinitions; let _reverseDefinitions;
let _defaults; let _defaults;
Command.prototype.loadDefinitions = function(definitions) { Command.prototype.loadDefinitions = function (definitions) {
_definitions = definitions; _definitions = definitions;
Object.keys(definitions).reduce((program, opt) => { Object.keys(definitions).reduce((program, opt) => {
@@ -47,7 +47,7 @@ Command.prototype.loadDefinitions = function(definitions) {
}, {}); }, {});
/* istanbul ignore next */ /* istanbul ignore next */
this.on('--help', function() { this.on('--help', function () {
console.log(' Configure From Environment:'); console.log(' Configure From Environment:');
console.log(''); console.log('');
Object.keys(_reverseDefinitions).forEach(key => { Object.keys(_reverseDefinitions).forEach(key => {
@@ -100,7 +100,7 @@ function parseConfigFile(program) {
return options; return options;
} }
Command.prototype.setValuesIfNeeded = function(options) { Command.prototype.setValuesIfNeeded = function (options) {
Object.keys(options).forEach(key => { Object.keys(options).forEach(key => {
if (!Object.prototype.hasOwnProperty.call(this, key)) { if (!Object.prototype.hasOwnProperty.call(this, key)) {
this[key] = options[key]; this[key] = options[key];
@@ -110,7 +110,7 @@ Command.prototype.setValuesIfNeeded = function(options) {
Command.prototype._parse = Command.prototype.parse; Command.prototype._parse = Command.prototype.parse;
Command.prototype.parse = function(args, env) { Command.prototype.parse = function (args, env) {
this._parse(args); this._parse(args);
// Parse the environment first // Parse the environment first
const envOptions = parseEnvironment(env); const envOptions = parseEnvironment(env);
@@ -123,7 +123,7 @@ Command.prototype.parse = function(args, env) {
this.setValuesIfNeeded(_defaults); this.setValuesIfNeeded(_defaults);
}; };
Command.prototype.getOptions = function() { Command.prototype.getOptions = function () {
return Object.keys(_definitions).reduce((options, key) => { return Object.keys(_definitions).reduce((options, key) => {
if (typeof this[key] !== 'undefined') { if (typeof this[key] !== 'undefined') {
options[key] = this[key]; options[key] = this[key];

View File

@@ -24,7 +24,7 @@ function logStartupOptions(options) {
} }
} }
export default function({ definitions, help, usage, start }) { export default function ({ definitions, help, usage, start }) {
program.loadDefinitions(definitions); program.loadDefinitions(definitions);
if (usage) { if (usage) {
program.usage(usage); program.usage(usage);
@@ -35,7 +35,7 @@ export default function({ definitions, help, usage, start }) {
program.parse(process.argv, process.env); program.parse(process.argv, process.env);
const options = program.getOptions(); const options = program.getOptions();
start(program, options, function() { start(program, options, function () {
logStartupOptions(options); logStartupOptions(options);
}); });
} }