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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,19 +5,17 @@ const httpsRequest = require('./httpsRequest');
// Returns a promise that fulfills iff this user id is valid.
function validateAuthData(authData, options) {
return request(options.janrain_capture_host, authData.access_token).then(
data => {
//successful response will have a "stat" (status) of 'ok' and a result node that stores the uuid, because that's all we asked for
//see: https://docs.janrain.com/api/registration/entity/#entity
if (data && data.stat == 'ok' && data.result == authData.id) {
return;
}
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Janrain capture auth is invalid for this user.'
);
return request(options.janrain_capture_host, authData.access_token).then(data => {
//successful response will have a "stat" (status) of 'ok' and a result node that stores the uuid, because that's all we asked for
//see: https://docs.janrain.com/api/registration/entity/#entity
if (data && data.stat == 'ok' && data.result == authData.id) {
return;
}
);
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Janrain capture auth is invalid for this user.'
);
});
}
// Returns a promise that fulfills iff this app id is valid.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.";
const MISSING_APPIDS =
'OAuth2 configuration is missing the client app IDs ("appIds" config parameter).';
const MISSING_URL =
'OAuth2 token introspection endpoint URL is missing from configuration!';
const MISSING_URL = 'OAuth2 token introspection endpoint URL is missing from configuration!';
// Returns a promise that fulfills if this user id is valid.
function validateAuthData(authData, options) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,17 +4,14 @@ var Parse = require('parse/node').Parse;
// Returns a promise that fulfills iff this user id is valid.
function validateAuthData(authData) {
return graphRequest(
'auth?access_token=' + authData.access_token + '&openid=' + authData.id
).then(function(data) {
if (data.errcode == 0) {
return;
return graphRequest('auth?access_token=' + authData.access_token + '&openid=' + authData.id).then(
function (data) {
if (data.errcode == 0) {
return;
}
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'wechat auth is invalid for this user.');
}
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'wechat auth is invalid for this user.'
);
});
);
}
// Returns a promise that fulfills if this app id is valid.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -127,7 +127,8 @@ module.exports.ParseServerOptions = {
},
emailVerifyTokenReuseIfValid: {
env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_REUSE_IF_VALID',
help: 'an existing email verify token should be reused when resend verification email is requested',
help:
'an existing email verify token should be reused when resend verification email is requested',
action: parsers.booleanParser,
default: false,
},

View File

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

View File

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

View File

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

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);
if (usage) {
program.usage(usage);
@@ -35,7 +35,7 @@ export default function({ definitions, help, usage, start }) {
program.parse(process.argv, process.env);
const options = program.getOptions();
start(program, options, function() {
start(program, options, function () {
logStartupOptions(options);
});
}