Fix Prettier (#7066)
This commit is contained in:
@@ -105,7 +105,7 @@
|
|||||||
"posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} mongodb-runner stop",
|
"posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} mongodb-runner stop",
|
||||||
"coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} TESTING=1 nyc jasmine",
|
"coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} TESTING=1 nyc jasmine",
|
||||||
"start": "node ./bin/parse-server",
|
"start": "node ./bin/parse-server",
|
||||||
"prettier": "prettier --write {src,spec}/{**/*,*}.js",
|
"prettier": "prettier --write '{src,spec}/{**/*,*}.js'",
|
||||||
"prepare": "npm run build",
|
"prepare": "npm run build",
|
||||||
"postinstall": "node -p 'require(\"./postinstall.js\")()'"
|
"postinstall": "node -p 'require(\"./postinstall.js\")()'"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ it('Should delete the password from authData after validation', done => {
|
|||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldap://localhost:${port}`,
|
url: `ldap://localhost:${port}`,
|
||||||
dn: 'uid={{id}}, o=example'
|
dn: 'uid={{id}}, o=example',
|
||||||
};
|
};
|
||||||
|
|
||||||
const authData = { id: 'testuser', password: 'secret' };
|
const authData = { id: 'testuser', password: 'secret' };
|
||||||
@@ -237,22 +237,23 @@ it('Should not save the password in the user record after authentication', done
|
|||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldap://localhost:${port}`,
|
url: `ldap://localhost:${port}`,
|
||||||
dn: 'uid={{id}}, o=example'
|
dn: 'uid={{id}}, o=example',
|
||||||
};
|
};
|
||||||
reconfigureServer({ auth: { ldap: options } }).then(() => {
|
reconfigureServer({ auth: { ldap: options } }).then(() => {
|
||||||
const authData = { authData: { id: 'testuser', password: 'secret' } };
|
const authData = { authData: { id: 'testuser', password: 'secret' } };
|
||||||
Parse.User.logInWith('ldap', authData).then((returnedUser) => {
|
Parse.User.logInWith('ldap', authData).then(returnedUser => {
|
||||||
const query = new Parse.Query("User");
|
const query = new Parse.Query('User');
|
||||||
query
|
query
|
||||||
.equalTo('objectId', returnedUser.id).first({ useMasterKey: true })
|
.equalTo('objectId', returnedUser.id)
|
||||||
.then((user) => {
|
.first({ useMasterKey: true })
|
||||||
expect(user.get('authData')).toEqual({ ldap:{ id: 'testuser' }});
|
.then(user => {
|
||||||
|
expect(user.get('authData')).toEqual({ ldap: { id: 'testuser' } });
|
||||||
expect(user.get('authData').ldap.password).toBeUndefined();
|
expect(user.get('authData').ldap.password).toBeUndefined();
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(done.fail)
|
.catch(done.fail)
|
||||||
.finally(() => server.close())
|
.finally(() => server.close());
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,12 +2,9 @@ var https = require('https'),
|
|||||||
crypto = require('crypto');
|
crypto = require('crypto');
|
||||||
var Parse = require('parse/node').Parse;
|
var Parse = require('parse/node').Parse;
|
||||||
|
|
||||||
var OAuth = function(options) {
|
var OAuth = function (options) {
|
||||||
if (!options) {
|
if (!options) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'No options passed to OAuth');
|
||||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
|
||||||
'No options passed to OAuth'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
this.consumer_key = options.consumer_key;
|
this.consumer_key = options.consumer_key;
|
||||||
this.consumer_secret = options.consumer_secret;
|
this.consumer_secret = options.consumer_secret;
|
||||||
@@ -17,22 +14,22 @@ var OAuth = function(options) {
|
|||||||
this.oauth_params = options.oauth_params || {};
|
this.oauth_params = options.oauth_params || {};
|
||||||
};
|
};
|
||||||
|
|
||||||
OAuth.prototype.send = function(method, path, params, body) {
|
OAuth.prototype.send = function (method, path, params, body) {
|
||||||
var request = this.buildRequest(method, path, params, body);
|
var request = this.buildRequest(method, path, params, body);
|
||||||
// Encode the body properly, the current Parse Implementation don't do it properly
|
// Encode the body properly, the current Parse Implementation don't do it properly
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
var httpRequest = https
|
var httpRequest = https
|
||||||
.request(request, function(res) {
|
.request(request, function (res) {
|
||||||
var data = '';
|
var data = '';
|
||||||
res.on('data', function(chunk) {
|
res.on('data', function (chunk) {
|
||||||
data += chunk;
|
data += chunk;
|
||||||
});
|
});
|
||||||
res.on('end', function() {
|
res.on('end', function () {
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
resolve(data);
|
resolve(data);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.on('error', function() {
|
.on('error', function () {
|
||||||
reject('Failed to make an OAuth request');
|
reject('Failed to make an OAuth request');
|
||||||
});
|
});
|
||||||
if (request.body) {
|
if (request.body) {
|
||||||
@@ -42,7 +39,7 @@ OAuth.prototype.send = function(method, path, params, body) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
OAuth.prototype.buildRequest = function(method, path, params, body) {
|
OAuth.prototype.buildRequest = function (method, path, params, body) {
|
||||||
if (path.indexOf('/') != 0) {
|
if (path.indexOf('/') != 0) {
|
||||||
path = '/' + path;
|
path = '/' + path;
|
||||||
}
|
}
|
||||||
@@ -62,12 +59,7 @@ OAuth.prototype.buildRequest = function(method, path, params, body) {
|
|||||||
oauth_params['oauth_token'] = this.auth_token;
|
oauth_params['oauth_token'] = this.auth_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
request = OAuth.signRequest(
|
request = OAuth.signRequest(request, oauth_params, this.consumer_secret, this.auth_token_secret);
|
||||||
request,
|
|
||||||
oauth_params,
|
|
||||||
this.consumer_secret,
|
|
||||||
this.auth_token_secret
|
|
||||||
);
|
|
||||||
|
|
||||||
if (body && Object.keys(body).length > 0) {
|
if (body && Object.keys(body).length > 0) {
|
||||||
request.body = OAuth.buildParameterString(body);
|
request.body = OAuth.buildParameterString(body);
|
||||||
@@ -75,18 +67,18 @@ OAuth.prototype.buildRequest = function(method, path, params, body) {
|
|||||||
return request;
|
return request;
|
||||||
};
|
};
|
||||||
|
|
||||||
OAuth.prototype.get = function(path, params) {
|
OAuth.prototype.get = function (path, params) {
|
||||||
return this.send('GET', path, params);
|
return this.send('GET', path, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
OAuth.prototype.post = function(path, params, body) {
|
OAuth.prototype.post = function (path, params, body) {
|
||||||
return this.send('POST', path, params, body);
|
return this.send('POST', path, params, body);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Proper string %escape encoding
|
Proper string %escape encoding
|
||||||
*/
|
*/
|
||||||
OAuth.encode = function(str) {
|
OAuth.encode = function (str) {
|
||||||
// discuss at: http://phpjs.org/functions/rawurlencode/
|
// discuss at: http://phpjs.org/functions/rawurlencode/
|
||||||
// original by: Brett Zamir (http://brett-zamir.me)
|
// original by: Brett Zamir (http://brett-zamir.me)
|
||||||
// input by: travc
|
// input by: travc
|
||||||
@@ -126,25 +118,23 @@ OAuth.version = '1.0';
|
|||||||
/*
|
/*
|
||||||
Generate a nonce
|
Generate a nonce
|
||||||
*/
|
*/
|
||||||
OAuth.nonce = function() {
|
OAuth.nonce = function () {
|
||||||
var text = '';
|
var text = '';
|
||||||
var possible =
|
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
||||||
|
|
||||||
for (var i = 0; i < 30; i++)
|
for (var i = 0; i < 30; i++) text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
OAuth.buildParameterString = function(obj) {
|
OAuth.buildParameterString = function (obj) {
|
||||||
// Sort keys and encode values
|
// Sort keys and encode values
|
||||||
if (obj) {
|
if (obj) {
|
||||||
var keys = Object.keys(obj).sort();
|
var keys = Object.keys(obj).sort();
|
||||||
|
|
||||||
// Map key=value, join them by &
|
// Map key=value, join them by &
|
||||||
return keys
|
return keys
|
||||||
.map(function(key) {
|
.map(function (key) {
|
||||||
return key + '=' + OAuth.encode(obj[key]);
|
return key + '=' + OAuth.encode(obj[key]);
|
||||||
})
|
})
|
||||||
.join('&');
|
.join('&');
|
||||||
@@ -157,33 +147,19 @@ OAuth.buildParameterString = function(obj) {
|
|||||||
Build the signature string from the object
|
Build the signature string from the object
|
||||||
*/
|
*/
|
||||||
|
|
||||||
OAuth.buildSignatureString = function(method, url, parameters) {
|
OAuth.buildSignatureString = function (method, url, parameters) {
|
||||||
return [
|
return [method.toUpperCase(), OAuth.encode(url), OAuth.encode(parameters)].join('&');
|
||||||
method.toUpperCase(),
|
|
||||||
OAuth.encode(url),
|
|
||||||
OAuth.encode(parameters),
|
|
||||||
].join('&');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Retuns encoded HMAC-SHA1 from key and text
|
Retuns encoded HMAC-SHA1 from key and text
|
||||||
*/
|
*/
|
||||||
OAuth.signature = function(text, key) {
|
OAuth.signature = function (text, key) {
|
||||||
crypto = require('crypto');
|
crypto = require('crypto');
|
||||||
return OAuth.encode(
|
return OAuth.encode(crypto.createHmac('sha1', key).update(text).digest('base64'));
|
||||||
crypto
|
|
||||||
.createHmac('sha1', key)
|
|
||||||
.update(text)
|
|
||||||
.digest('base64')
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OAuth.signRequest = function(
|
OAuth.signRequest = function (request, oauth_parameters, consumer_secret, auth_token_secret) {
|
||||||
request,
|
|
||||||
oauth_parameters,
|
|
||||||
consumer_secret,
|
|
||||||
auth_token_secret
|
|
||||||
) {
|
|
||||||
oauth_parameters = oauth_parameters || {};
|
oauth_parameters = oauth_parameters || {};
|
||||||
|
|
||||||
// Set default values
|
// Set default values
|
||||||
@@ -224,16 +200,9 @@ OAuth.signRequest = function(
|
|||||||
// Build the signature string
|
// Build the signature string
|
||||||
var url = 'https://' + request.host + '' + request.path;
|
var url = 'https://' + request.host + '' + request.path;
|
||||||
|
|
||||||
var signatureString = OAuth.buildSignatureString(
|
var signatureString = OAuth.buildSignatureString(request.method, url, parameterString);
|
||||||
request.method,
|
|
||||||
url,
|
|
||||||
parameterString
|
|
||||||
);
|
|
||||||
// Hash the signature string
|
// Hash the signature string
|
||||||
var signatureKey = [
|
var signatureKey = [OAuth.encode(consumer_secret), OAuth.encode(auth_token_secret)].join('&');
|
||||||
OAuth.encode(consumer_secret),
|
|
||||||
OAuth.encode(auth_token_secret),
|
|
||||||
].join('&');
|
|
||||||
|
|
||||||
var signature = OAuth.signature(signatureString, signatureKey);
|
var signature = OAuth.signature(signatureString, signatureKey);
|
||||||
|
|
||||||
@@ -246,7 +215,7 @@ OAuth.signRequest = function(
|
|||||||
// Set the authorization header
|
// Set the authorization header
|
||||||
var authHeader = Object.keys(oauth_parameters)
|
var authHeader = Object.keys(oauth_parameters)
|
||||||
.sort()
|
.sort()
|
||||||
.map(function(key) {
|
.map(function (key) {
|
||||||
var value = oauth_parameters[key];
|
var value = oauth_parameters[key];
|
||||||
return key + '="' + value + '"';
|
return key + '="' + value + '"';
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -33,24 +33,15 @@ const getAppleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
|
|||||||
const getHeaderFromToken = token => {
|
const getHeaderFromToken = token => {
|
||||||
const decodedToken = jwt.decode(token, { complete: true });
|
const decodedToken = jwt.decode(token, { complete: true });
|
||||||
if (!decodedToken) {
|
if (!decodedToken) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `provided token does not decode as JWT`);
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
`provided token does not decode as JWT`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return decodedToken.header;
|
return decodedToken.header;
|
||||||
};
|
};
|
||||||
|
|
||||||
const verifyIdToken = async (
|
const verifyIdToken = async ({ token, id }, { clientId, cacheMaxEntries, cacheMaxAge }) => {
|
||||||
{ token, id },
|
|
||||||
{ clientId, cacheMaxEntries, cacheMaxAge }
|
|
||||||
) => {
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
`id token is invalid for this user.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
|
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
|
||||||
@@ -60,11 +51,7 @@ const verifyIdToken = async (
|
|||||||
cacheMaxAge = cacheMaxAge || ONE_HOUR_IN_MS;
|
cacheMaxAge = cacheMaxAge || ONE_HOUR_IN_MS;
|
||||||
cacheMaxEntries = cacheMaxEntries || 5;
|
cacheMaxEntries = cacheMaxEntries || 5;
|
||||||
|
|
||||||
const appleKey = await getAppleKeyByKeyId(
|
const appleKey = await getAppleKeyByKeyId(keyId, cacheMaxEntries, cacheMaxAge);
|
||||||
keyId,
|
|
||||||
cacheMaxEntries,
|
|
||||||
cacheMaxAge
|
|
||||||
);
|
|
||||||
const signingKey = appleKey.publicKey || appleKey.rsaPublicKey;
|
const signingKey = appleKey.publicKey || appleKey.rsaPublicKey;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -87,10 +74,7 @@ const verifyIdToken = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (jwtClaims.sub !== id) {
|
if (jwtClaims.sub !== id) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
`auth data is invalid for this user.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return jwtClaims;
|
return jwtClaims;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,20 +19,12 @@ function getAppSecretPath(authData, options = {}) {
|
|||||||
// Returns a promise that fulfills iff this user id is valid.
|
// Returns a promise that fulfills iff this user id is valid.
|
||||||
function validateAuthData(authData, options) {
|
function validateAuthData(authData, options) {
|
||||||
return graphRequest(
|
return graphRequest(
|
||||||
'me?fields=id&access_token=' +
|
'me?fields=id&access_token=' + authData.access_token + getAppSecretPath(authData, options)
|
||||||
authData.access_token +
|
|
||||||
getAppSecretPath(authData, options)
|
|
||||||
).then(data => {
|
).then(data => {
|
||||||
if (
|
if ((data && data.id == authData.id) || (process.env.TESTING && authData.id === 'test')) {
|
||||||
(data && data.id == authData.id) ||
|
|
||||||
(process.env.TESTING && authData.id === 'test')
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Facebook auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,10 +35,7 @@ function validateAppId(appIds, authData, options) {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
if (!appIds.length) {
|
if (!appIds.length) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is not configured.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Facebook auth is not configured.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return graphRequest(
|
return graphRequest(
|
||||||
'app?access_token=' + access_token + getAppSecretPath(authData, options)
|
'app?access_token=' + access_token + getAppSecretPath(authData, options)
|
||||||
@@ -54,10 +43,7 @@ function validateAppId(appIds, authData, options) {
|
|||||||
if (data && appIds.indexOf(data.id) != -1) {
|
if (data && appIds.indexOf(data.id) != -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Facebook auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,20 +96,14 @@ function verifySignature(publicKey, authData) {
|
|||||||
verifier.update(authData.salt, 'base64');
|
verifier.update(authData.salt, 'base64');
|
||||||
|
|
||||||
if (!verifier.verify(publicKey, authData.signature, 'base64')) {
|
if (!verifier.verify(publicKey, authData.signature, 'base64')) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple Game Center - invalid signature');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Apple Game Center - invalid signature'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a promise that fulfills if this user id is valid.
|
// Returns a promise that fulfills if this user id is valid.
|
||||||
async function validateAuthData(authData) {
|
async function validateAuthData(authData) {
|
||||||
if (!authData.id) {
|
if (!authData.id) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple Game Center - authData id missing');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Apple Game Center - authData id missing'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
authData.playerId = authData.id;
|
authData.playerId = authData.id;
|
||||||
const publicKey = await getAppleCertificate(authData.publicKeyUrl);
|
const publicKey = await getAppleCertificate(authData.publicKeyUrl);
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ function validateAuthData(authData) {
|
|||||||
if (data && data.id == authData.id) {
|
if (data && data.id == authData.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Github auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Github auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,9 +39,7 @@ function getGoogleKeyByKeyId(keyId) {
|
|||||||
|
|
||||||
if (expire) {
|
if (expire) {
|
||||||
cache = Object.assign({}, pems, {
|
cache = Object.assign({}, pems, {
|
||||||
expiresAt: new Date(
|
expiresAt: new Date(new Date().getTime() + Number(expire[1]) * 1000),
|
||||||
new Date().getTime() + Number(expire[1]) * 1000
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,10 +55,7 @@ function getHeaderFromToken(token) {
|
|||||||
const decodedToken = jwt.decode(token, { complete: true });
|
const decodedToken = jwt.decode(token, { complete: true });
|
||||||
|
|
||||||
if (!decodedToken) {
|
if (!decodedToken) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `provided token does not decode as JWT`);
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
`provided token does not decode as JWT`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return decodedToken.header;
|
return decodedToken.header;
|
||||||
@@ -68,10 +63,7 @@ function getHeaderFromToken(token) {
|
|||||||
|
|
||||||
async function verifyIdToken({ id_token: token, id }, { clientId }) {
|
async function verifyIdToken({ id_token: token, id }, { clientId }) {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
`id token is invalid for this user.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
|
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
|
||||||
@@ -96,10 +88,7 @@ async function verifyIdToken({ id_token: token, id }, { clientId }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (jwtClaims.sub !== id) {
|
if (jwtClaims.sub !== id) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
`auth data is invalid for this user.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientId && jwtClaims.aud !== clientId) {
|
if (clientId && jwtClaims.aud !== clientId) {
|
||||||
@@ -140,9 +129,7 @@ function rsaPublicKeyToPEM(modulusB64, exponentB64) {
|
|||||||
const encodedExplen = encodeLengthHex(explen);
|
const encodedExplen = encodeLengthHex(explen);
|
||||||
const encodedPubkey =
|
const encodedPubkey =
|
||||||
'30' +
|
'30' +
|
||||||
encodeLengthHex(
|
encodeLengthHex(modlen + explen + encodedModlen.length / 2 + encodedExplen.length / 2 + 2) +
|
||||||
modlen + explen + encodedModlen.length / 2 + encodedExplen.length / 2 + 2
|
|
||||||
) +
|
|
||||||
'02' +
|
'02' +
|
||||||
encodedModlen +
|
encodedModlen +
|
||||||
modulusHex +
|
modulusHex +
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const https = require('https');
|
const https = require('https');
|
||||||
|
|
||||||
function makeCallback(resolve, reject, noJSON) {
|
function makeCallback(resolve, reject, noJSON) {
|
||||||
return function(res) {
|
return function (res) {
|
||||||
let data = '';
|
let data = '';
|
||||||
res.on('data', chunk => {
|
res.on('data', chunk => {
|
||||||
data += chunk;
|
data += chunk;
|
||||||
@@ -23,9 +23,7 @@ function makeCallback(resolve, reject, noJSON) {
|
|||||||
|
|
||||||
function get(options, noJSON = false) {
|
function get(options, noJSON = false) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
https
|
https.get(options, makeCallback(resolve, reject, noJSON)).on('error', reject);
|
||||||
.get(options, makeCallback(resolve, reject, noJSON))
|
|
||||||
.on('error', reject);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,11 +92,7 @@ function loadAuthAdapter(provider, authOptions) {
|
|||||||
|
|
||||||
// Try the configuration methods
|
// Try the configuration methods
|
||||||
if (providerOptions) {
|
if (providerOptions) {
|
||||||
const optionalAdapter = loadAdapter(
|
const optionalAdapter = loadAdapter(providerOptions, undefined, providerOptions);
|
||||||
providerOptions,
|
|
||||||
undefined,
|
|
||||||
providerOptions
|
|
||||||
);
|
|
||||||
if (optionalAdapter) {
|
if (optionalAdapter) {
|
||||||
['validateAuthData', 'validateAppId'].forEach(key => {
|
['validateAuthData', 'validateAppId'].forEach(key => {
|
||||||
if (optionalAdapter[key]) {
|
if (optionalAdapter[key]) {
|
||||||
@@ -128,10 +124,7 @@ module.exports = function (authOptions = {}, enableAnonymousUsers = true) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { adapter, appIds, providerOptions } = loadAuthAdapter(
|
const { adapter, appIds, providerOptions } = loadAuthAdapter(provider, authOptions);
|
||||||
provider,
|
|
||||||
authOptions
|
|
||||||
);
|
|
||||||
|
|
||||||
return authDataValidator(adapter, appIds, providerOptions);
|
return authDataValidator(adapter, appIds, providerOptions);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,10 +11,7 @@ function validateAuthData(authData) {
|
|||||||
if (response && response.data && response.data.id == authData.id) {
|
if (response && response.data && response.data.id == authData.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Instagram auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Instagram auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,19 +5,17 @@ const httpsRequest = require('./httpsRequest');
|
|||||||
|
|
||||||
// Returns a promise that fulfills iff this user id is valid.
|
// Returns a promise that fulfills iff this user id is valid.
|
||||||
function validateAuthData(authData, options) {
|
function validateAuthData(authData, options) {
|
||||||
return request(options.janrain_capture_host, authData.access_token).then(
|
return request(options.janrain_capture_host, authData.access_token).then(data => {
|
||||||
data => {
|
//successful response will have a "stat" (status) of 'ok' and a result node that stores the uuid, because that's all we asked for
|
||||||
//successful response will have a "stat" (status) of 'ok' and a result node that stores the uuid, because that's all we asked for
|
//see: https://docs.janrain.com/api/registration/entity/#entity
|
||||||
//see: https://docs.janrain.com/api/registration/entity/#entity
|
if (data && data.stat == 'ok' && data.result == authData.id) {
|
||||||
if (data && data.stat == 'ok' && data.result == authData.id) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Parse.Error(
|
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Janrain capture auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
throw new Parse.Error(
|
||||||
|
Parse.Error.OBJECT_NOT_FOUND,
|
||||||
|
'Janrain capture auth is invalid for this user.'
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a promise that fulfills iff this app id is valid.
|
// Returns a promise that fulfills iff this app id is valid.
|
||||||
|
|||||||
@@ -37,12 +37,7 @@ const { Parse } = require('parse/node');
|
|||||||
const httpsRequest = require('./httpsRequest');
|
const httpsRequest = require('./httpsRequest');
|
||||||
|
|
||||||
const arraysEqual = (_arr1, _arr2) => {
|
const arraysEqual = (_arr1, _arr2) => {
|
||||||
if (
|
if (!Array.isArray(_arr1) || !Array.isArray(_arr2) || _arr1.length !== _arr2.length) return false;
|
||||||
!Array.isArray(_arr1) ||
|
|
||||||
!Array.isArray(_arr2) ||
|
|
||||||
_arr1.length !== _arr2.length
|
|
||||||
)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var arr1 = _arr1.concat().sort();
|
var arr1 = _arr1.concat().sort();
|
||||||
var arr2 = _arr2.concat().sort();
|
var arr2 = _arr2.concat().sort();
|
||||||
@@ -54,21 +49,12 @@ const arraysEqual = (_arr1, _arr2) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAuth = async (
|
const handleAuth = async ({ access_token, id, roles, groups } = {}, { config } = {}) => {
|
||||||
{ access_token, id, roles, groups } = {},
|
|
||||||
{ config } = {}
|
|
||||||
) => {
|
|
||||||
if (!(access_token && id)) {
|
if (!(access_token && id)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Missing access token and/or User id');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Missing access token and/or User id'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!config || !(config['auth-server-url'] && config['realm'])) {
|
if (!config || !(config['auth-server-url'] && config['realm'])) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Missing keycloak configuration');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Missing keycloak configuration'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await httpsRequest.get({
|
const response = await httpsRequest.get({
|
||||||
@@ -87,10 +73,7 @@ const handleAuth = async (
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid authentication');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Invalid authentication'
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Parse.Error) {
|
if (e instanceof Parse.Error) {
|
||||||
throw e;
|
throw e;
|
||||||
|
|||||||
@@ -4,16 +4,12 @@ const Parse = require('parse/node').Parse;
|
|||||||
function validateAuthData(authData, options) {
|
function validateAuthData(authData, options) {
|
||||||
if (!optionsAreValid(options)) {
|
if (!optionsAreValid(options)) {
|
||||||
return new Promise((_, reject) => {
|
return new Promise((_, reject) => {
|
||||||
reject(
|
reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP auth configuration missing'));
|
||||||
new Parse.Error(
|
|
||||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
|
||||||
'LDAP auth configuration missing'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const clientOptions = (options.url.startsWith("ldaps://")) ?
|
const clientOptions = options.url.startsWith('ldaps://')
|
||||||
{ url: options.url, tlsOptions: options.tlsOptions } : { url: options.url };
|
? { url: options.url, tlsOptions: options.tlsOptions }
|
||||||
|
: { url: options.url };
|
||||||
|
|
||||||
const client = ldapjs.createClient(clientOptions);
|
const client = ldapjs.createClient(clientOptions);
|
||||||
const userCn =
|
const userCn =
|
||||||
@@ -23,28 +19,31 @@ function validateAuthData(authData, options) {
|
|||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
client.bind(userCn, authData.password, ldapError => {
|
client.bind(userCn, authData.password, ldapError => {
|
||||||
delete(authData.password);
|
delete authData.password;
|
||||||
if (ldapError) {
|
if (ldapError) {
|
||||||
let error;
|
let error;
|
||||||
switch (ldapError.code) {
|
switch (ldapError.code) {
|
||||||
case 49:
|
case 49:
|
||||||
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAP: Wrong username or password');
|
error = new Parse.Error(
|
||||||
|
Parse.Error.OBJECT_NOT_FOUND,
|
||||||
|
'LDAP: Wrong username or password'
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case "DEPTH_ZERO_SELF_SIGNED_CERT":
|
case 'DEPTH_ZERO_SELF_SIGNED_CERT':
|
||||||
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAPS: Certificate mismatch');
|
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAPS: Certificate mismatch');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'LDAP: Somthing went wrong (' + ldapError.code + ')');
|
error = new Parse.Error(
|
||||||
|
Parse.Error.OBJECT_NOT_FOUND,
|
||||||
|
'LDAP: Somthing went wrong (' + ldapError.code + ')'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
reject(error);
|
reject(error);
|
||||||
client.destroy(ldapError);
|
client.destroy(ldapError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (typeof options.groupCn === 'string' && typeof options.groupFilter === 'string') {
|
||||||
typeof options.groupCn === 'string' &&
|
|
||||||
typeof options.groupFilter === 'string'
|
|
||||||
) {
|
|
||||||
searchForGroup(client, options, authData.id, resolve, reject);
|
searchForGroup(client, options, authData.id, resolve, reject);
|
||||||
} else {
|
} else {
|
||||||
client.unbind();
|
client.unbind();
|
||||||
@@ -61,7 +60,7 @@ function optionsAreValid(options) {
|
|||||||
typeof options.suffix === 'string' &&
|
typeof options.suffix === 'string' &&
|
||||||
typeof options.url === 'string' &&
|
typeof options.url === 'string' &&
|
||||||
(options.url.startsWith('ldap://') ||
|
(options.url.startsWith('ldap://') ||
|
||||||
options.url.startsWith('ldaps://') && typeof options.tlsOptions === 'object')
|
(options.url.startsWith('ldaps://') && typeof options.tlsOptions === 'object'))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,12 +75,7 @@ function searchForGroup(client, options, id, resolve, reject) {
|
|||||||
if (searchError) {
|
if (searchError) {
|
||||||
client.unbind();
|
client.unbind();
|
||||||
client.destroy();
|
client.destroy();
|
||||||
return reject(
|
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
|
||||||
new Parse.Error(
|
|
||||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
|
||||||
'LDAP group search failed'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
res.on('searchEntry', entry => {
|
res.on('searchEntry', entry => {
|
||||||
if (entry.object.cn === options.groupCn) {
|
if (entry.object.cn === options.groupCn) {
|
||||||
@@ -96,20 +90,12 @@ function searchForGroup(client, options, id, resolve, reject) {
|
|||||||
client.unbind();
|
client.unbind();
|
||||||
client.destroy();
|
client.destroy();
|
||||||
return reject(
|
return reject(
|
||||||
new Parse.Error(
|
new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP: User not in group')
|
||||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
|
||||||
'LDAP: User not in group'
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
res.on('error', () => {
|
res.on('error', () => {
|
||||||
return reject(
|
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
|
||||||
new Parse.Error(
|
|
||||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
|
||||||
'LDAP group search failed'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ function validateAuthData(authData) {
|
|||||||
if (response && response.userId && response.userId === authData.id) {
|
if (response && response.userId && response.userId === authData.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Line auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Line auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,12 @@ const httpsRequest = require('./httpsRequest');
|
|||||||
|
|
||||||
// Returns a promise that fulfills iff this user id is valid.
|
// Returns a promise that fulfills iff this user id is valid.
|
||||||
function validateAuthData(authData) {
|
function validateAuthData(authData) {
|
||||||
return request('me', authData.access_token, authData.is_mobile_sdk).then(
|
return request('me', authData.access_token, authData.is_mobile_sdk).then(data => {
|
||||||
data => {
|
if (data && data.id == authData.id) {
|
||||||
if (data && data.id == authData.id) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Parse.Error(
|
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Linkedin auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Linkedin auth is invalid for this user.');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a promise that fulfills iff this app id is valid.
|
// Returns a promise that fulfills iff this app id is valid.
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ function validateAuthData(authData) {
|
|||||||
if (data && data.id == authData.id) {
|
if (data && data.id == authData.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Meetup auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Meetup auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,15 @@ const httpsRequest = require('./httpsRequest');
|
|||||||
|
|
||||||
// Returns a promise that fulfills if this user mail is valid.
|
// Returns a promise that fulfills if this user mail is valid.
|
||||||
function validateAuthData(authData) {
|
function validateAuthData(authData) {
|
||||||
return request('me', authData.access_token).then(
|
return request('me', authData.access_token).then(response => {
|
||||||
response => {
|
if (response && response.id && response.id == authData.id) {
|
||||||
if (response && response.id && response.id == authData.id) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Parse.Error(
|
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Microsoft Graph auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
throw new Parse.Error(
|
||||||
|
Parse.Error.OBJECT_NOT_FOUND,
|
||||||
|
'Microsoft Graph auth is invalid for this user.'
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a promise that fulfills if this app id is valid.
|
// Returns a promise that fulfills if this app id is valid.
|
||||||
|
|||||||
@@ -63,8 +63,7 @@ const INVALID_ACCESS_APPID =
|
|||||||
"OAuth2: the access_token's appID is empty or is not in the list of permitted appIDs in the auth configuration.";
|
"OAuth2: the access_token's appID is empty or is not in the list of permitted appIDs in the auth configuration.";
|
||||||
const MISSING_APPIDS =
|
const MISSING_APPIDS =
|
||||||
'OAuth2 configuration is missing the client app IDs ("appIds" config parameter).';
|
'OAuth2 configuration is missing the client app IDs ("appIds" config parameter).';
|
||||||
const MISSING_URL =
|
const MISSING_URL = 'OAuth2 token introspection endpoint URL is missing from configuration!';
|
||||||
'OAuth2 token introspection endpoint URL is missing from configuration!';
|
|
||||||
|
|
||||||
// Returns a promise that fulfills if this user id is valid.
|
// Returns a promise that fulfills if this user id is valid.
|
||||||
function validateAuthData(authData, options) {
|
function validateAuthData(authData, options) {
|
||||||
|
|||||||
@@ -14,10 +14,7 @@ function validateAuthData(authData) {
|
|||||||
if (data && data.sub == authData.id) {
|
if (data && data.sub == authData.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'PhantAuth auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'PhantAuth auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,16 +4,11 @@ var Parse = require('parse/node').Parse;
|
|||||||
|
|
||||||
// Returns a promise that fulfills iff this user id is valid.
|
// Returns a promise that fulfills iff this user id is valid.
|
||||||
function validateAuthData(authData) {
|
function validateAuthData(authData) {
|
||||||
return graphRequest('me?access_token=' + authData.access_token).then(function(
|
return graphRequest('me?access_token=' + authData.access_token).then(function (data) {
|
||||||
data
|
|
||||||
) {
|
|
||||||
if (data && data.openid == authData.id) {
|
if (data && data.openid == authData.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'qq auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'qq auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,21 +19,16 @@ function validateAppId() {
|
|||||||
|
|
||||||
// A promisey wrapper for qq graph requests.
|
// A promisey wrapper for qq graph requests.
|
||||||
function graphRequest(path) {
|
function graphRequest(path) {
|
||||||
return httpsRequest
|
return httpsRequest.get('https://graph.qq.com/oauth2.0/' + path, true).then(data => {
|
||||||
.get('https://graph.qq.com/oauth2.0/' + path, true)
|
return parseResponseData(data);
|
||||||
.then(data => {
|
});
|
||||||
return parseResponseData(data);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseResponseData(data) {
|
function parseResponseData(data) {
|
||||||
const starPos = data.indexOf('(');
|
const starPos = data.indexOf('(');
|
||||||
const endPos = data.indexOf(')');
|
const endPos = data.indexOf(')');
|
||||||
if (starPos == -1 || endPos == -1) {
|
if (starPos == -1 || endPos == -1) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'qq auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'qq auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
data = data.substring(starPos + 1, endPos - 1);
|
data = data.substring(starPos + 1, endPos - 1);
|
||||||
return JSON.parse(data);
|
return JSON.parse(data);
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ function validateAuthData(authData) {
|
|||||||
if (data && data.id == authData.id) {
|
if (data && data.id == authData.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Spotify auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Spotify auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,19 +16,13 @@ function validateAuthData(authData) {
|
|||||||
function validateAppId(appIds, authData) {
|
function validateAppId(appIds, authData) {
|
||||||
var access_token = authData.access_token;
|
var access_token = authData.access_token;
|
||||||
if (!appIds.length) {
|
if (!appIds.length) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Spotify auth is not configured.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Spotify auth is not configured.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return request('me', access_token).then(data => {
|
return request('me', access_token).then(data => {
|
||||||
if (data && appIds.indexOf(data.id) != -1) {
|
if (data && appIds.indexOf(data.id) != -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Spotify auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Spotify auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ var Parse = require('parse/node').Parse;
|
|||||||
// Returns a promise that fulfills iff this user id is valid.
|
// Returns a promise that fulfills iff this user id is valid.
|
||||||
function validateAuthData(authData, options) {
|
function validateAuthData(authData, options) {
|
||||||
if (!options) {
|
if (!options) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Twitter auth configuration missing');
|
||||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
|
||||||
'Twitter auth configuration missing'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
options = handleMultipleConfigurations(authData, options);
|
options = handleMultipleConfigurations(authData, options);
|
||||||
var client = new OAuth(options);
|
var client = new OAuth(options);
|
||||||
@@ -20,10 +17,7 @@ function validateAuthData(authData, options) {
|
|||||||
if (data && data.id_str == '' + authData.id) {
|
if (data && data.id_str == '' + authData.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Twitter auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Twitter auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,20 +30,14 @@ function handleMultipleConfigurations(authData, options) {
|
|||||||
if (Array.isArray(options)) {
|
if (Array.isArray(options)) {
|
||||||
const consumer_key = authData.consumer_key;
|
const consumer_key = authData.consumer_key;
|
||||||
if (!consumer_key) {
|
if (!consumer_key) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Twitter auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Twitter auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
options = options.filter(option => {
|
options = options.filter(option => {
|
||||||
return option.consumer_key == consumer_key;
|
return option.consumer_key == consumer_key;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (options.length == 0) {
|
if (options.length == 0) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Twitter auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Twitter auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
options = options[0];
|
options = options[0];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,7 @@ function validateAuthData(authData, params) {
|
|||||||
if (response && response.access_token) {
|
if (response && response.access_token) {
|
||||||
return request(
|
return request(
|
||||||
'api.vk.com',
|
'api.vk.com',
|
||||||
'method/users.get?access_token=' +
|
'method/users.get?access_token=' + authData.access_token + '&v=' + params.apiVersion
|
||||||
authData.access_token +
|
|
||||||
'&v=' +
|
|
||||||
params.apiVersion
|
|
||||||
).then(function (response) {
|
).then(function (response) {
|
||||||
if (
|
if (
|
||||||
response &&
|
response &&
|
||||||
@@ -24,16 +21,10 @@ function validateAuthData(authData, params) {
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Vk auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Vk auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Vk appIds or appSecret is incorrect.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Vk appIds or appSecret is incorrect.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,14 @@ var Parse = require('parse/node').Parse;
|
|||||||
|
|
||||||
// Returns a promise that fulfills iff this user id is valid.
|
// Returns a promise that fulfills iff this user id is valid.
|
||||||
function validateAuthData(authData) {
|
function validateAuthData(authData) {
|
||||||
return graphRequest(
|
return graphRequest('auth?access_token=' + authData.access_token + '&openid=' + authData.id).then(
|
||||||
'auth?access_token=' + authData.access_token + '&openid=' + authData.id
|
function (data) {
|
||||||
).then(function(data) {
|
if (data.errcode == 0) {
|
||||||
if (data.errcode == 0) {
|
return;
|
||||||
return;
|
}
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'wechat auth is invalid for this user.');
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
);
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'wechat auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a promise that fulfills if this app id is valid.
|
// Returns a promise that fulfills if this app id is valid.
|
||||||
|
|||||||
@@ -5,14 +5,11 @@ var querystring = require('querystring');
|
|||||||
|
|
||||||
// Returns a promise that fulfills iff this user id is valid.
|
// Returns a promise that fulfills iff this user id is valid.
|
||||||
function validateAuthData(authData) {
|
function validateAuthData(authData) {
|
||||||
return graphRequest(authData.access_token).then(function(data) {
|
return graphRequest(authData.access_token).then(function (data) {
|
||||||
if (data && data.uid == authData.id) {
|
if (data && data.uid == authData.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'weibo auth is invalid for this user.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'weibo auth is invalid for this user.'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ function debug() {
|
|||||||
logger.debug.apply(logger, ['RedisCacheAdapter', ...arguments]);
|
logger.debug.apply(logger, ['RedisCacheAdapter', ...arguments]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isValidTTL = (ttl) => typeof ttl === 'number' && ttl > 0;
|
const isValidTTL = ttl => typeof ttl === 'number' && ttl > 0;
|
||||||
|
|
||||||
export class RedisCacheAdapter {
|
export class RedisCacheAdapter {
|
||||||
constructor(redisCtx, ttl = DEFAULT_REDIS_TTL) {
|
constructor(redisCtx, ttl = DEFAULT_REDIS_TTL) {
|
||||||
@@ -22,8 +22,8 @@ export class RedisCacheAdapter {
|
|||||||
if (!this.client) {
|
if (!this.client) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
return new Promise((resolve) => {
|
return new Promise(resolve => {
|
||||||
this.client.quit((err) => {
|
this.client.quit(err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error('RedisCacheAdapter error on shutdown', { error: err });
|
logger.error('RedisCacheAdapter error on shutdown', { error: err });
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ export class RedisCacheAdapter {
|
|||||||
return this.queue.enqueue(
|
return this.queue.enqueue(
|
||||||
key,
|
key,
|
||||||
() =>
|
() =>
|
||||||
new Promise((resolve) => {
|
new Promise(resolve => {
|
||||||
this.client.get(key, function (err, res) {
|
this.client.get(key, function (err, res) {
|
||||||
debug('-> get', key, res);
|
debug('-> get', key, res);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
@@ -62,7 +62,7 @@ export class RedisCacheAdapter {
|
|||||||
return this.queue.enqueue(
|
return this.queue.enqueue(
|
||||||
key,
|
key,
|
||||||
() =>
|
() =>
|
||||||
new Promise((resolve) => {
|
new Promise(resolve => {
|
||||||
this.client.set(key, value, function () {
|
this.client.set(key, value, function () {
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
@@ -77,7 +77,7 @@ export class RedisCacheAdapter {
|
|||||||
return this.queue.enqueue(
|
return this.queue.enqueue(
|
||||||
key,
|
key,
|
||||||
() =>
|
() =>
|
||||||
new Promise((resolve) => {
|
new Promise(resolve => {
|
||||||
this.client.psetex(key, ttl, value, function () {
|
this.client.psetex(key, ttl, value, function () {
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
@@ -90,7 +90,7 @@ export class RedisCacheAdapter {
|
|||||||
return this.queue.enqueue(
|
return this.queue.enqueue(
|
||||||
key,
|
key,
|
||||||
() =>
|
() =>
|
||||||
new Promise((resolve) => {
|
new Promise(resolve => {
|
||||||
this.client.del(key, function () {
|
this.client.del(key, function () {
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
@@ -103,7 +103,7 @@ export class RedisCacheAdapter {
|
|||||||
return this.queue.enqueue(
|
return this.queue.enqueue(
|
||||||
FLUSH_DB_KEY,
|
FLUSH_DB_KEY,
|
||||||
() =>
|
() =>
|
||||||
new Promise((resolve) => {
|
new Promise(resolve => {
|
||||||
this.client.flushdb(function () {
|
this.client.flushdb(function () {
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,12 +38,7 @@ export class FilesAdapter {
|
|||||||
*
|
*
|
||||||
* @return {Promise} a promise that should fail if the storage didn't succeed
|
* @return {Promise} a promise that should fail if the storage didn't succeed
|
||||||
*/
|
*/
|
||||||
createFile(
|
createFile(filename: string, data, contentType: string, options: Object): Promise {}
|
||||||
filename: string,
|
|
||||||
data,
|
|
||||||
contentType: string,
|
|
||||||
options: Object
|
|
||||||
): Promise {}
|
|
||||||
|
|
||||||
/** Responsible for deleting the specified file
|
/** Responsible for deleting the specified file
|
||||||
*
|
*
|
||||||
@@ -111,10 +106,7 @@ export function validateFilename(filename): ?Parse.Error {
|
|||||||
|
|
||||||
const regx = /^[_a-zA-Z0-9][a-zA-Z0-9@. ~_-]*$/;
|
const regx = /^[_a-zA-Z0-9][a-zA-Z0-9@. ~_-]*$/;
|
||||||
if (!filename.match(regx)) {
|
if (!filename.match(regx)) {
|
||||||
return new Parse.Error(
|
return new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename contains invalid characters.');
|
||||||
Parse.Error.INVALID_FILE_NAME,
|
|
||||||
'Filename contains invalid characters.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,11 +28,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
|||||||
this._algorithm = 'aes-256-gcm';
|
this._algorithm = 'aes-256-gcm';
|
||||||
this._encryptionKey =
|
this._encryptionKey =
|
||||||
encryptionKey !== undefined
|
encryptionKey !== undefined
|
||||||
? crypto
|
? crypto.createHash('sha256').update(String(encryptionKey)).digest('base64').substr(0, 32)
|
||||||
.createHash('sha256')
|
|
||||||
.update(String(encryptionKey))
|
|
||||||
.digest('base64')
|
|
||||||
.substr(0, 32)
|
|
||||||
: null;
|
: null;
|
||||||
const defaultMongoOptions = {
|
const defaultMongoOptions = {
|
||||||
useNewUrlParser: true,
|
useNewUrlParser: true,
|
||||||
@@ -43,13 +39,12 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
|||||||
|
|
||||||
_connect() {
|
_connect() {
|
||||||
if (!this._connectionPromise) {
|
if (!this._connectionPromise) {
|
||||||
this._connectionPromise = MongoClient.connect(
|
this._connectionPromise = MongoClient.connect(this._databaseURI, this._mongoOptions).then(
|
||||||
this._databaseURI,
|
client => {
|
||||||
this._mongoOptions
|
this._client = client;
|
||||||
).then(client => {
|
return client.db(client.s.options.dbName);
|
||||||
this._client = client;
|
}
|
||||||
return client.db(client.s.options.dbName);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return this._connectionPromise;
|
return this._connectionPromise;
|
||||||
}
|
}
|
||||||
@@ -68,11 +63,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
|||||||
if (this._encryptionKey !== null) {
|
if (this._encryptionKey !== null) {
|
||||||
try {
|
try {
|
||||||
const iv = crypto.randomBytes(16);
|
const iv = crypto.randomBytes(16);
|
||||||
const cipher = crypto.createCipheriv(
|
const cipher = crypto.createCipheriv(this._algorithm, this._encryptionKey, iv);
|
||||||
this._algorithm,
|
|
||||||
this._encryptionKey,
|
|
||||||
iv
|
|
||||||
);
|
|
||||||
const encryptedResult = Buffer.concat([
|
const encryptedResult = Buffer.concat([
|
||||||
cipher.update(data),
|
cipher.update(data),
|
||||||
cipher.final(),
|
cipher.final(),
|
||||||
@@ -126,16 +117,9 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
|||||||
const authTag = data.slice(authTagLocation);
|
const authTag = data.slice(authTagLocation);
|
||||||
const iv = data.slice(ivLocation, authTagLocation);
|
const iv = data.slice(ivLocation, authTagLocation);
|
||||||
const encrypted = data.slice(0, ivLocation);
|
const encrypted = data.slice(0, ivLocation);
|
||||||
const decipher = crypto.createDecipheriv(
|
const decipher = crypto.createDecipheriv(this._algorithm, this._encryptionKey, iv);
|
||||||
this._algorithm,
|
|
||||||
this._encryptionKey,
|
|
||||||
iv
|
|
||||||
);
|
|
||||||
decipher.setAuthTag(authTag);
|
decipher.setAuthTag(authTag);
|
||||||
const decrypted = Buffer.concat([
|
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
||||||
decipher.update(encrypted),
|
|
||||||
decipher.final(),
|
|
||||||
]);
|
|
||||||
return resolve(decrypted);
|
return resolve(decrypted);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return reject(err);
|
return reject(err);
|
||||||
@@ -160,10 +144,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
|||||||
options.oldKey
|
options.oldKey
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
oldKeyFileAdapter = new GridFSBucketAdapter(
|
oldKeyFileAdapter = new GridFSBucketAdapter(this._databaseURI, this._mongoOptions);
|
||||||
this._databaseURI,
|
|
||||||
this._mongoOptions
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (options.fileNames !== undefined) {
|
if (options.fileNames !== undefined) {
|
||||||
fileNames = options.fileNames;
|
fileNames = options.fileNames;
|
||||||
@@ -186,9 +167,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
|||||||
this.createFile(fileName, plainTextData)
|
this.createFile(fileName, plainTextData)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
fileNamesRotated.push(fileName);
|
fileNamesRotated.push(fileName);
|
||||||
fileNamesNotRotated = fileNamesNotRotated.filter(function (
|
fileNamesNotRotated = fileNamesNotRotated.filter(function (value) {
|
||||||
value
|
|
||||||
) {
|
|
||||||
return value !== fileName;
|
return value !== fileName;
|
||||||
});
|
});
|
||||||
fileNameIndex += 1;
|
fileNameIndex += 1;
|
||||||
@@ -223,13 +202,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFileLocation(config, filename) {
|
getFileLocation(config, filename) {
|
||||||
return (
|
return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
|
||||||
config.mount +
|
|
||||||
'/files/' +
|
|
||||||
config.applicationId +
|
|
||||||
'/' +
|
|
||||||
encodeURIComponent(filename)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMetadata(filename) {
|
async getMetadata(filename) {
|
||||||
|
|||||||
@@ -30,13 +30,12 @@ export class GridStoreAdapter extends FilesAdapter {
|
|||||||
|
|
||||||
_connect() {
|
_connect() {
|
||||||
if (!this._connectionPromise) {
|
if (!this._connectionPromise) {
|
||||||
this._connectionPromise = MongoClient.connect(
|
this._connectionPromise = MongoClient.connect(this._databaseURI, this._mongoOptions).then(
|
||||||
this._databaseURI,
|
client => {
|
||||||
this._mongoOptions
|
this._client = client;
|
||||||
).then(client => {
|
return client.db(client.s.options.dbName);
|
||||||
this._client = client;
|
}
|
||||||
return client.db(client.s.options.dbName);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return this._connectionPromise;
|
return this._connectionPromise;
|
||||||
}
|
}
|
||||||
@@ -85,13 +84,7 @@ export class GridStoreAdapter extends FilesAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFileLocation(config, filename) {
|
getFileLocation(config, filename) {
|
||||||
return (
|
return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
|
||||||
config.mount +
|
|
||||||
'/files/' +
|
|
||||||
config.applicationId +
|
|
||||||
'/' +
|
|
||||||
encodeURIComponent(filename)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleFileStream(filename: string, req, res, contentType) {
|
async handleFileStream(filename: string, req, res, contentType) {
|
||||||
@@ -152,14 +145,14 @@ function handleRangeRequest(stream, req, res, contentType) {
|
|||||||
'Content-Type': contentType,
|
'Content-Type': contentType,
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.seek(start, function() {
|
stream.seek(start, function () {
|
||||||
// Get gridFile stream
|
// Get gridFile stream
|
||||||
const gridFileStream = stream.stream(true);
|
const gridFileStream = stream.stream(true);
|
||||||
let bufferAvail = 0;
|
let bufferAvail = 0;
|
||||||
let remainingBytesToWrite = contentLength;
|
let remainingBytesToWrite = contentLength;
|
||||||
let totalBytesWritten = 0;
|
let totalBytesWritten = 0;
|
||||||
// Write to response
|
// Write to response
|
||||||
gridFileStream.on('data', function(data) {
|
gridFileStream.on('data', function (data) {
|
||||||
bufferAvail += data.length;
|
bufferAvail += data.length;
|
||||||
if (bufferAvail > 0) {
|
if (bufferAvail > 0) {
|
||||||
// slice returns the same buffer if overflowing
|
// slice returns the same buffer if overflowing
|
||||||
|
|||||||
@@ -20,11 +20,7 @@ function configureTransports(options) {
|
|||||||
{
|
{
|
||||||
filename: 'parse-server.info',
|
filename: 'parse-server.info',
|
||||||
json: true,
|
json: true,
|
||||||
format: format.combine(
|
format: format.combine(format.timestamp(), format.splat(), format.json()),
|
||||||
format.timestamp(),
|
|
||||||
format.splat(),
|
|
||||||
format.json()
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
@@ -37,11 +33,7 @@ function configureTransports(options) {
|
|||||||
{
|
{
|
||||||
filename: 'parse-server.err',
|
filename: 'parse-server.err',
|
||||||
json: true,
|
json: true,
|
||||||
format: format.combine(
|
format: format.combine(format.timestamp(), format.splat(), format.json()),
|
||||||
format.timestamp(),
|
|
||||||
format.splat(),
|
|
||||||
format.json()
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
options,
|
options,
|
||||||
{ level: 'error' }
|
{ level: 'error' }
|
||||||
@@ -120,9 +112,7 @@ export function addTransport(transport) {
|
|||||||
|
|
||||||
export function removeTransport(transport) {
|
export function removeTransport(transport) {
|
||||||
const matchingTransport = logger.transports.find(t1 => {
|
const matchingTransport = logger.transports.find(t1 => {
|
||||||
return typeof transport === 'string'
|
return typeof transport === 'string' ? t1.name === transport : t1 === transport;
|
||||||
? t1.name === transport
|
|
||||||
: t1 === transport;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (matchingTransport) {
|
if (matchingTransport) {
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ export class WinstonLoggerAdapter extends LoggerAdapter {
|
|||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
// defaults to 7 days prior
|
// defaults to 7 days prior
|
||||||
const from =
|
const from = options.from || new Date(Date.now() - 7 * MILLISECONDS_IN_A_DAY);
|
||||||
options.from || new Date(Date.now() - 7 * MILLISECONDS_IN_A_DAY);
|
|
||||||
const until = options.until || new Date();
|
const until = options.until || new Date();
|
||||||
const limit = options.size || 10;
|
const limit = options.size || 10;
|
||||||
const order = options.order || 'desc';
|
const order = options.order || 'desc';
|
||||||
|
|||||||
@@ -15,17 +15,7 @@ export default class MongoCollection {
|
|||||||
// idea. Or even if this behavior is a good idea.
|
// idea. Or even if this behavior is a good idea.
|
||||||
find(
|
find(
|
||||||
query,
|
query,
|
||||||
{
|
{ skip, limit, sort, keys, maxTimeMS, readPreference, hint, caseInsensitive, explain } = {}
|
||||||
skip,
|
|
||||||
limit,
|
|
||||||
sort,
|
|
||||||
keys,
|
|
||||||
maxTimeMS,
|
|
||||||
readPreference,
|
|
||||||
hint,
|
|
||||||
caseInsensitive,
|
|
||||||
explain,
|
|
||||||
} = {}
|
|
||||||
) {
|
) {
|
||||||
// Support for Full Text Search - $text
|
// Support for Full Text Search - $text
|
||||||
if (keys && keys.$score) {
|
if (keys && keys.$score) {
|
||||||
@@ -44,10 +34,7 @@ export default class MongoCollection {
|
|||||||
explain,
|
explain,
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
// Check for "no geoindex" error
|
// Check for "no geoindex" error
|
||||||
if (
|
if (error.code != 17007 && !error.message.match(/unable to find index for .geoNear/)) {
|
||||||
error.code != 17007 &&
|
|
||||||
!error.message.match(/unable to find index for .geoNear/)
|
|
||||||
) {
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
// Figure out what key needs an index
|
// Figure out what key needs an index
|
||||||
@@ -88,17 +75,7 @@ export default class MongoCollection {
|
|||||||
|
|
||||||
_rawFind(
|
_rawFind(
|
||||||
query,
|
query,
|
||||||
{
|
{ skip, limit, sort, keys, maxTimeMS, readPreference, hint, caseInsensitive, explain } = {}
|
||||||
skip,
|
|
||||||
limit,
|
|
||||||
sort,
|
|
||||||
keys,
|
|
||||||
maxTimeMS,
|
|
||||||
readPreference,
|
|
||||||
hint,
|
|
||||||
caseInsensitive,
|
|
||||||
explain,
|
|
||||||
} = {}
|
|
||||||
) {
|
) {
|
||||||
let findOperation = this._mongoCollection.find(query, {
|
let findOperation = this._mongoCollection.find(query, {
|
||||||
skip,
|
skip,
|
||||||
@@ -113,9 +90,7 @@ export default class MongoCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (caseInsensitive) {
|
if (caseInsensitive) {
|
||||||
findOperation = findOperation.collation(
|
findOperation = findOperation.collation(MongoCollection.caseInsensitiveCollation());
|
||||||
MongoCollection.caseInsensitiveCollation()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxTimeMS) {
|
if (maxTimeMS) {
|
||||||
|
|||||||
@@ -41,9 +41,7 @@ function mongoFieldToParseSchemaField(type) {
|
|||||||
|
|
||||||
const nonFieldSchemaKeys = ['_id', '_metadata', '_client_permissions'];
|
const nonFieldSchemaKeys = ['_id', '_metadata', '_client_permissions'];
|
||||||
function mongoSchemaFieldsToParseSchemaFields(schema) {
|
function mongoSchemaFieldsToParseSchemaFields(schema) {
|
||||||
var fieldNames = Object.keys(schema).filter(
|
var fieldNames = Object.keys(schema).filter(key => nonFieldSchemaKeys.indexOf(key) === -1);
|
||||||
(key) => nonFieldSchemaKeys.indexOf(key) === -1
|
|
||||||
);
|
|
||||||
var response = fieldNames.reduce((obj, fieldName) => {
|
var response = fieldNames.reduce((obj, fieldName) => {
|
||||||
obj[fieldName] = mongoFieldToParseSchemaField(schema[fieldName]);
|
obj[fieldName] = mongoFieldToParseSchemaField(schema[fieldName]);
|
||||||
if (
|
if (
|
||||||
@@ -110,7 +108,7 @@ function mongoSchemaToParseSchema(mongoSchema) {
|
|||||||
function _mongoSchemaQueryFromNameQuery(name: string, query) {
|
function _mongoSchemaQueryFromNameQuery(name: string, query) {
|
||||||
const object = { _id: name };
|
const object = { _id: name };
|
||||||
if (query) {
|
if (query) {
|
||||||
Object.keys(query).forEach((key) => {
|
Object.keys(query).forEach(key => {
|
||||||
object[key] = query[key];
|
object[key] = query[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -156,15 +154,13 @@ class MongoSchemaCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fetchAllSchemasFrom_SCHEMA() {
|
_fetchAllSchemasFrom_SCHEMA() {
|
||||||
return this._collection
|
return this._collection._rawFind({}).then(schemas => schemas.map(mongoSchemaToParseSchema));
|
||||||
._rawFind({})
|
|
||||||
.then((schemas) => schemas.map(mongoSchemaToParseSchema));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_fetchOneSchemaFrom_SCHEMA(name: string) {
|
_fetchOneSchemaFrom_SCHEMA(name: string) {
|
||||||
return this._collection
|
return this._collection
|
||||||
._rawFind(_mongoSchemaQueryFromNameQuery(name), { limit: 1 })
|
._rawFind(_mongoSchemaQueryFromNameQuery(name), { limit: 1 })
|
||||||
.then((results) => {
|
.then(results => {
|
||||||
if (results.length === 1) {
|
if (results.length === 1) {
|
||||||
return mongoSchemaToParseSchema(results[0]);
|
return mongoSchemaToParseSchema(results[0]);
|
||||||
} else {
|
} else {
|
||||||
@@ -175,22 +171,17 @@ class MongoSchemaCollection {
|
|||||||
|
|
||||||
// Atomically find and delete an object based on query.
|
// Atomically find and delete an object based on query.
|
||||||
findAndDeleteSchema(name: string) {
|
findAndDeleteSchema(name: string) {
|
||||||
return this._collection._mongoCollection.findOneAndDelete(
|
return this._collection._mongoCollection.findOneAndDelete(_mongoSchemaQueryFromNameQuery(name));
|
||||||
_mongoSchemaQueryFromNameQuery(name)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
insertSchema(schema: any) {
|
insertSchema(schema: any) {
|
||||||
return this._collection
|
return this._collection
|
||||||
.insertOne(schema)
|
.insertOne(schema)
|
||||||
.then((result) => mongoSchemaToParseSchema(result.ops[0]))
|
.then(result => mongoSchemaToParseSchema(result.ops[0]))
|
||||||
.catch((error) => {
|
.catch(error => {
|
||||||
if (error.code === 11000) {
|
if (error.code === 11000) {
|
||||||
//Mongo's duplicate key error
|
//Mongo's duplicate key error
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Class already exists.');
|
||||||
Parse.Error.DUPLICATE_VALUE,
|
|
||||||
'Class already exists.'
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@@ -198,17 +189,11 @@ class MongoSchemaCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateSchema(name: string, update) {
|
updateSchema(name: string, update) {
|
||||||
return this._collection.updateOne(
|
return this._collection.updateOne(_mongoSchemaQueryFromNameQuery(name), update);
|
||||||
_mongoSchemaQueryFromNameQuery(name),
|
|
||||||
update
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
upsertSchema(name: string, query: string, update) {
|
upsertSchema(name: string, query: string, update) {
|
||||||
return this._collection.upsertOne(
|
return this._collection.upsertOne(_mongoSchemaQueryFromNameQuery(name, query), update);
|
||||||
_mongoSchemaQueryFromNameQuery(name, query),
|
|
||||||
update
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a field to the schema. If database does not support the field
|
// Add a field to the schema. If database does not support the field
|
||||||
@@ -225,7 +210,7 @@ class MongoSchemaCollection {
|
|||||||
addFieldIfNotExists(className: string, fieldName: string, fieldType: string) {
|
addFieldIfNotExists(className: string, fieldName: string, fieldType: string) {
|
||||||
return this._fetchOneSchemaFrom_SCHEMA(className)
|
return this._fetchOneSchemaFrom_SCHEMA(className)
|
||||||
.then(
|
.then(
|
||||||
(schema) => {
|
schema => {
|
||||||
// If a field with this name already exists, it will be handled elsewhere.
|
// If a field with this name already exists, it will be handled elsewhere.
|
||||||
if (schema.fields[fieldName] != undefined) {
|
if (schema.fields[fieldName] != undefined) {
|
||||||
return;
|
return;
|
||||||
@@ -235,8 +220,7 @@ class MongoSchemaCollection {
|
|||||||
// Make sure there are not other geopoint fields
|
// Make sure there are not other geopoint fields
|
||||||
if (
|
if (
|
||||||
Object.keys(schema.fields).some(
|
Object.keys(schema.fields).some(
|
||||||
(existingField) =>
|
existingField => schema.fields[existingField].type === 'GeoPoint'
|
||||||
schema.fields[existingField].type === 'GeoPoint'
|
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
@@ -247,7 +231,7 @@ class MongoSchemaCollection {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
(error) => {
|
error => {
|
||||||
// If error is undefined, the schema doesn't exist, and we can create the schema with the field.
|
// If error is undefined, the schema doesn't exist, and we can create the schema with the field.
|
||||||
// If some other error, reject with it.
|
// If some other error, reject with it.
|
||||||
if (error === undefined) {
|
if (error === undefined) {
|
||||||
|
|||||||
@@ -2,16 +2,8 @@
|
|||||||
import MongoCollection from './MongoCollection';
|
import MongoCollection from './MongoCollection';
|
||||||
import MongoSchemaCollection from './MongoSchemaCollection';
|
import MongoSchemaCollection from './MongoSchemaCollection';
|
||||||
import { StorageAdapter } from '../StorageAdapter';
|
import { StorageAdapter } from '../StorageAdapter';
|
||||||
import type {
|
import type { SchemaType, QueryType, StorageClass, QueryOptions } from '../StorageAdapter';
|
||||||
SchemaType,
|
import { parse as parseUrl, format as formatUrl } from '../../../vendor/mongodbUrl';
|
||||||
QueryType,
|
|
||||||
StorageClass,
|
|
||||||
QueryOptions,
|
|
||||||
} from '../StorageAdapter';
|
|
||||||
import {
|
|
||||||
parse as parseUrl,
|
|
||||||
format as formatUrl,
|
|
||||||
} from '../../../vendor/mongodbUrl';
|
|
||||||
import {
|
import {
|
||||||
parseObjectToMongoObjectForCreate,
|
parseObjectToMongoObjectForCreate,
|
||||||
mongoObjectToParseObject,
|
mongoObjectToParseObject,
|
||||||
@@ -45,9 +37,7 @@ const storageAdapterAllCollections = mongoAdapter => {
|
|||||||
}
|
}
|
||||||
// TODO: If you have one app with a collection prefix that happens to be a prefix of another
|
// TODO: If you have one app with a collection prefix that happens to be a prefix of another
|
||||||
// apps prefix, this will go very very badly. We should fix that somehow.
|
// apps prefix, this will go very very badly. We should fix that somehow.
|
||||||
return (
|
return collection.collectionName.indexOf(mongoAdapter._collectionPrefix) == 0;
|
||||||
collection.collectionName.indexOf(mongoAdapter._collectionPrefix) == 0
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -85,16 +75,13 @@ const mongoSchemaFromFieldsAndClassNameAndCLP = (
|
|||||||
|
|
||||||
for (const fieldName in fields) {
|
for (const fieldName in fields) {
|
||||||
const { type, targetClass, ...fieldOptions } = fields[fieldName];
|
const { type, targetClass, ...fieldOptions } = fields[fieldName];
|
||||||
mongoObject[
|
mongoObject[fieldName] = MongoSchemaCollection.parseFieldTypeToMongoFieldType({
|
||||||
fieldName
|
|
||||||
] = MongoSchemaCollection.parseFieldTypeToMongoFieldType({
|
|
||||||
type,
|
type,
|
||||||
targetClass,
|
targetClass,
|
||||||
});
|
});
|
||||||
if (fieldOptions && Object.keys(fieldOptions).length > 0) {
|
if (fieldOptions && Object.keys(fieldOptions).length > 0) {
|
||||||
mongoObject._metadata = mongoObject._metadata || {};
|
mongoObject._metadata = mongoObject._metadata || {};
|
||||||
mongoObject._metadata.fields_options =
|
mongoObject._metadata.fields_options = mongoObject._metadata.fields_options || {};
|
||||||
mongoObject._metadata.fields_options || {};
|
|
||||||
mongoObject._metadata.fields_options[fieldName] = fieldOptions;
|
mongoObject._metadata.fields_options[fieldName] = fieldOptions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,11 +95,7 @@ const mongoSchemaFromFieldsAndClassNameAndCLP = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (indexes && typeof indexes === 'object' && Object.keys(indexes).length > 0) {
|
||||||
indexes &&
|
|
||||||
typeof indexes === 'object' &&
|
|
||||||
Object.keys(indexes).length > 0
|
|
||||||
) {
|
|
||||||
mongoObject._metadata = mongoObject._metadata || {};
|
mongoObject._metadata = mongoObject._metadata || {};
|
||||||
mongoObject._metadata.indexes = indexes;
|
mongoObject._metadata.indexes = indexes;
|
||||||
}
|
}
|
||||||
@@ -137,11 +120,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
_maxTimeMS: ?number;
|
_maxTimeMS: ?number;
|
||||||
canSortOnJoinTables: boolean;
|
canSortOnJoinTables: boolean;
|
||||||
|
|
||||||
constructor({
|
constructor({ uri = defaults.DefaultMongoURI, collectionPrefix = '', mongoOptions = {} }: any) {
|
||||||
uri = defaults.DefaultMongoURI,
|
|
||||||
collectionPrefix = '',
|
|
||||||
mongoOptions = {},
|
|
||||||
}: any) {
|
|
||||||
this._uri = uri;
|
this._uri = uri;
|
||||||
this._collectionPrefix = collectionPrefix;
|
this._collectionPrefix = collectionPrefix;
|
||||||
this._mongoOptions = mongoOptions;
|
this._mongoOptions = mongoOptions;
|
||||||
@@ -225,9 +204,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
classExists(name: string) {
|
classExists(name: string) {
|
||||||
return this.connect()
|
return this.connect()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.database
|
return this.database.listCollections({ name: this._collectionPrefix + name }).toArray();
|
||||||
.listCollections({ name: this._collectionPrefix + name })
|
|
||||||
.toArray();
|
|
||||||
})
|
})
|
||||||
.then(collections => {
|
.then(collections => {
|
||||||
return collections.length > 0;
|
return collections.length > 0;
|
||||||
@@ -262,10 +239,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
Object.keys(submittedIndexes).forEach(name => {
|
Object.keys(submittedIndexes).forEach(name => {
|
||||||
const field = submittedIndexes[name];
|
const field = submittedIndexes[name];
|
||||||
if (existingIndexes[name] && field.__op !== 'Delete') {
|
if (existingIndexes[name] && field.__op !== 'Delete') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, `Index ${name} exists, cannot update.`);
|
||||||
Parse.Error.INVALID_QUERY,
|
|
||||||
`Index ${name} exists, cannot update.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!existingIndexes[name] && field.__op === 'Delete') {
|
if (!existingIndexes[name] && field.__op === 'Delete') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
@@ -349,26 +323,15 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
schema.indexes
|
schema.indexes
|
||||||
);
|
);
|
||||||
mongoObject._id = className;
|
mongoObject._id = className;
|
||||||
return this.setIndexesWithSchemaFormat(
|
return this.setIndexesWithSchemaFormat(className, schema.indexes, {}, schema.fields)
|
||||||
className,
|
|
||||||
schema.indexes,
|
|
||||||
{},
|
|
||||||
schema.fields
|
|
||||||
)
|
|
||||||
.then(() => this._schemaCollection())
|
.then(() => this._schemaCollection())
|
||||||
.then(schemaCollection => schemaCollection.insertSchema(mongoObject))
|
.then(schemaCollection => schemaCollection.insertSchema(mongoObject))
|
||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
addFieldIfNotExists(
|
addFieldIfNotExists(className: string, fieldName: string, type: any): Promise<void> {
|
||||||
className: string,
|
|
||||||
fieldName: string,
|
|
||||||
type: any
|
|
||||||
): Promise<void> {
|
|
||||||
return this._schemaCollection()
|
return this._schemaCollection()
|
||||||
.then(schemaCollection =>
|
.then(schemaCollection => schemaCollection.addFieldIfNotExists(className, fieldName, type))
|
||||||
schemaCollection.addFieldIfNotExists(className, fieldName, type)
|
|
||||||
)
|
|
||||||
.then(() => this.createIndexesIfNeeded(className, fieldName, type))
|
.then(() => this.createIndexesIfNeeded(className, fieldName, type))
|
||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
@@ -388,9 +351,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
})
|
})
|
||||||
// We've dropped the collection, now remove the _SCHEMA document
|
// We've dropped the collection, now remove the _SCHEMA document
|
||||||
.then(() => this._schemaCollection())
|
.then(() => this._schemaCollection())
|
||||||
.then(schemaCollection =>
|
.then(schemaCollection => schemaCollection.findAndDeleteSchema(className))
|
||||||
schemaCollection.findAndDeleteSchema(className)
|
|
||||||
)
|
|
||||||
.catch(err => this.handleError(err))
|
.catch(err => this.handleError(err))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -398,9 +359,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
deleteAllClasses(fast: boolean) {
|
deleteAllClasses(fast: boolean) {
|
||||||
return storageAdapterAllCollections(this).then(collections =>
|
return storageAdapterAllCollections(this).then(collections =>
|
||||||
Promise.all(
|
Promise.all(
|
||||||
collections.map(collection =>
|
collections.map(collection => (fast ? collection.deleteMany({}) : collection.drop()))
|
||||||
fast ? collection.deleteMany({}) : collection.drop()
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -450,13 +409,9 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection =>
|
.then(collection => collection.updateMany(collectionFilter, collectionUpdate))
|
||||||
collection.updateMany(collectionFilter, collectionUpdate)
|
|
||||||
)
|
|
||||||
.then(() => this._schemaCollection())
|
.then(() => this._schemaCollection())
|
||||||
.then(schemaCollection =>
|
.then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate))
|
||||||
schemaCollection.updateSchema(className, schemaUpdate)
|
|
||||||
)
|
|
||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,9 +420,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
// rejection reason are TBD.
|
// rejection reason are TBD.
|
||||||
getAllClasses(): Promise<StorageClass[]> {
|
getAllClasses(): Promise<StorageClass[]> {
|
||||||
return this._schemaCollection()
|
return this._schemaCollection()
|
||||||
.then(schemasCollection =>
|
.then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA())
|
||||||
schemasCollection._fetchAllSchemasFrom_SCHEMA()
|
|
||||||
)
|
|
||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,31 +429,18 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
// undefined as the reason.
|
// undefined as the reason.
|
||||||
getClass(className: string): Promise<StorageClass> {
|
getClass(className: string): Promise<StorageClass> {
|
||||||
return this._schemaCollection()
|
return this._schemaCollection()
|
||||||
.then(schemasCollection =>
|
.then(schemasCollection => schemasCollection._fetchOneSchemaFrom_SCHEMA(className))
|
||||||
schemasCollection._fetchOneSchemaFrom_SCHEMA(className)
|
|
||||||
)
|
|
||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema,
|
// TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema,
|
||||||
// and should infer from the type. Or maybe does need the schema for validations. Or maybe needs
|
// and should infer from the type. Or maybe does need the schema for validations. Or maybe needs
|
||||||
// the schema only for the legacy mongo format. We'll figure that out later.
|
// the schema only for the legacy mongo format. We'll figure that out later.
|
||||||
createObject(
|
createObject(className: string, schema: SchemaType, object: any, transactionalSession: ?any) {
|
||||||
className: string,
|
|
||||||
schema: SchemaType,
|
|
||||||
object: any,
|
|
||||||
transactionalSession: ?any
|
|
||||||
) {
|
|
||||||
schema = convertParseSchemaToMongoSchema(schema);
|
schema = convertParseSchemaToMongoSchema(schema);
|
||||||
const mongoObject = parseObjectToMongoObjectForCreate(
|
const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema);
|
||||||
className,
|
|
||||||
object,
|
|
||||||
schema
|
|
||||||
);
|
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection =>
|
.then(collection => collection.insertOne(mongoObject, transactionalSession))
|
||||||
collection.insertOne(mongoObject, transactionalSession)
|
|
||||||
)
|
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error.code === 11000) {
|
if (error.code === 11000) {
|
||||||
// Duplicate value
|
// Duplicate value
|
||||||
@@ -510,9 +450,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
);
|
);
|
||||||
err.underlyingError = error;
|
err.underlyingError = error;
|
||||||
if (error.message) {
|
if (error.message) {
|
||||||
const matches = error.message.match(
|
const matches = error.message.match(/index:[\sa-zA-Z0-9_\-\.]+\$?([a-zA-Z_-]+)_1/);
|
||||||
/index:[\sa-zA-Z0-9_\-\.]+\$?([a-zA-Z_-]+)_1/
|
|
||||||
);
|
|
||||||
if (matches && Array.isArray(matches)) {
|
if (matches && Array.isArray(matches)) {
|
||||||
err.userInfo = { duplicated_field: matches[1] };
|
err.userInfo = { duplicated_field: matches[1] };
|
||||||
}
|
}
|
||||||
@@ -543,18 +481,12 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
.then(
|
.then(
|
||||||
({ result }) => {
|
({ result }) => {
|
||||||
if (result.n === 0) {
|
if (result.n === 0) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Object not found.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error');
|
||||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
|
||||||
'Database adapter error'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -571,9 +503,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
const mongoUpdate = transformUpdate(className, update, schema);
|
const mongoUpdate = transformUpdate(className, update, schema);
|
||||||
const mongoWhere = transformWhere(className, query, schema);
|
const mongoWhere = transformWhere(className, query, schema);
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection =>
|
.then(collection => collection.updateMany(mongoWhere, mongoUpdate, transactionalSession))
|
||||||
collection.updateMany(mongoWhere, mongoUpdate, transactionalSession)
|
|
||||||
)
|
|
||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,9 +551,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
const mongoUpdate = transformUpdate(className, update, schema);
|
const mongoUpdate = transformUpdate(className, update, schema);
|
||||||
const mongoWhere = transformWhere(className, query, schema);
|
const mongoWhere = transformWhere(className, query, schema);
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection =>
|
.then(collection => collection.upsertOne(mongoWhere, mongoUpdate, transactionalSession))
|
||||||
collection.upsertOne(mongoWhere, mongoUpdate, transactionalSession)
|
|
||||||
)
|
|
||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,16 +560,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
className: string,
|
className: string,
|
||||||
schema: SchemaType,
|
schema: SchemaType,
|
||||||
query: QueryType,
|
query: QueryType,
|
||||||
{
|
{ skip, limit, sort, keys, readPreference, hint, caseInsensitive, explain }: QueryOptions
|
||||||
skip,
|
|
||||||
limit,
|
|
||||||
sort,
|
|
||||||
keys,
|
|
||||||
readPreference,
|
|
||||||
hint,
|
|
||||||
caseInsensitive,
|
|
||||||
explain,
|
|
||||||
}: QueryOptions
|
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
schema = convertParseSchemaToMongoSchema(schema);
|
schema = convertParseSchemaToMongoSchema(schema);
|
||||||
const mongoWhere = transformWhere(className, query, schema);
|
const mongoWhere = transformWhere(className, query, schema);
|
||||||
@@ -689,9 +608,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
if (explain) {
|
if (explain) {
|
||||||
return objects;
|
return objects;
|
||||||
}
|
}
|
||||||
return objects.map(object =>
|
return objects.map(object => mongoObjectToParseObject(className, object, schema));
|
||||||
mongoObjectToParseObject(className, object, schema)
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
@@ -706,18 +623,14 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
schema = convertParseSchemaToMongoSchema(schema);
|
schema = convertParseSchemaToMongoSchema(schema);
|
||||||
const indexCreationRequest = {};
|
const indexCreationRequest = {};
|
||||||
const mongoFieldNames = fieldNames.map(fieldName =>
|
const mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema));
|
||||||
transformKey(className, fieldName, schema)
|
|
||||||
);
|
|
||||||
mongoFieldNames.forEach(fieldName => {
|
mongoFieldNames.forEach(fieldName => {
|
||||||
indexCreationRequest[fieldName] =
|
indexCreationRequest[fieldName] = options.indexType !== undefined ? options.indexType : 1;
|
||||||
options.indexType !== undefined ? options.indexType : 1;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultOptions: Object = { background: true, sparse: true };
|
const defaultOptions: Object = { background: true, sparse: true };
|
||||||
const indexNameOptions: Object = indexName ? { name: indexName } : {};
|
const indexNameOptions: Object = indexName ? { name: indexName } : {};
|
||||||
const ttlOptions: Object =
|
const ttlOptions: Object = options.ttl !== undefined ? { expireAfterSeconds: options.ttl } : {};
|
||||||
options.ttl !== undefined ? { expireAfterSeconds: options.ttl } : {};
|
|
||||||
const caseInsensitiveOptions: Object = caseInsensitive
|
const caseInsensitiveOptions: Object = caseInsensitive
|
||||||
? { collation: MongoCollection.caseInsensitiveCollation() }
|
? { collation: MongoCollection.caseInsensitiveCollation() }
|
||||||
: {};
|
: {};
|
||||||
@@ -732,10 +645,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
.then(
|
.then(
|
||||||
collection =>
|
collection =>
|
||||||
new Promise((resolve, reject) =>
|
new Promise((resolve, reject) =>
|
||||||
collection._mongoCollection.createIndex(
|
collection._mongoCollection.createIndex(indexCreationRequest, indexOptions, error =>
|
||||||
indexCreationRequest,
|
error ? reject(error) : resolve()
|
||||||
indexOptions,
|
|
||||||
error => (error ? reject(error) : resolve())
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -747,23 +658,15 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
// As such, we shouldn't expose this function to users of parse until we have an out-of-band
|
// As such, we shouldn't expose this function to users of parse until we have an out-of-band
|
||||||
// Way of determining if a field is nullable. Undefined doesn't count against uniqueness,
|
// Way of determining if a field is nullable. Undefined doesn't count against uniqueness,
|
||||||
// which is why we use sparse indexes.
|
// which is why we use sparse indexes.
|
||||||
ensureUniqueness(
|
ensureUniqueness(className: string, schema: SchemaType, fieldNames: string[]) {
|
||||||
className: string,
|
|
||||||
schema: SchemaType,
|
|
||||||
fieldNames: string[]
|
|
||||||
) {
|
|
||||||
schema = convertParseSchemaToMongoSchema(schema);
|
schema = convertParseSchemaToMongoSchema(schema);
|
||||||
const indexCreationRequest = {};
|
const indexCreationRequest = {};
|
||||||
const mongoFieldNames = fieldNames.map(fieldName =>
|
const mongoFieldNames = fieldNames.map(fieldName => transformKey(className, fieldName, schema));
|
||||||
transformKey(className, fieldName, schema)
|
|
||||||
);
|
|
||||||
mongoFieldNames.forEach(fieldName => {
|
mongoFieldNames.forEach(fieldName => {
|
||||||
indexCreationRequest[fieldName] = 1;
|
indexCreationRequest[fieldName] = 1;
|
||||||
});
|
});
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection =>
|
.then(collection => collection._ensureSparseUniqueIndexInBackground(indexCreationRequest))
|
||||||
collection._ensureSparseUniqueIndexInBackground(indexCreationRequest)
|
|
||||||
)
|
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error.code === 11000) {
|
if (error.code === 11000) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
@@ -808,23 +711,14 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
distinct(
|
distinct(className: string, schema: SchemaType, query: QueryType, fieldName: string) {
|
||||||
className: string,
|
|
||||||
schema: SchemaType,
|
|
||||||
query: QueryType,
|
|
||||||
fieldName: string
|
|
||||||
) {
|
|
||||||
schema = convertParseSchemaToMongoSchema(schema);
|
schema = convertParseSchemaToMongoSchema(schema);
|
||||||
const isPointerField =
|
const isPointerField = schema.fields[fieldName] && schema.fields[fieldName].type === 'Pointer';
|
||||||
schema.fields[fieldName] && schema.fields[fieldName].type === 'Pointer';
|
|
||||||
const transformField = transformKey(className, fieldName, schema);
|
const transformField = transformKey(className, fieldName, schema);
|
||||||
|
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection =>
|
.then(collection =>
|
||||||
collection.distinct(
|
collection.distinct(transformField, transformWhere(className, query, schema))
|
||||||
transformField,
|
|
||||||
transformWhere(className, query, schema)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.then(objects => {
|
.then(objects => {
|
||||||
objects = objects.filter(obj => obj != null);
|
objects = objects.filter(obj => obj != null);
|
||||||
@@ -862,16 +756,10 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
stage.$match = this._parseAggregateArgs(schema, stage.$match);
|
stage.$match = this._parseAggregateArgs(schema, stage.$match);
|
||||||
}
|
}
|
||||||
if (stage.$project) {
|
if (stage.$project) {
|
||||||
stage.$project = this._parseAggregateProjectArgs(
|
stage.$project = this._parseAggregateProjectArgs(schema, stage.$project);
|
||||||
schema,
|
|
||||||
stage.$project
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (stage.$geoNear && stage.$geoNear.query) {
|
if (stage.$geoNear && stage.$geoNear.query) {
|
||||||
stage.$geoNear.query = this._parseAggregateArgs(
|
stage.$geoNear.query = this._parseAggregateArgs(schema, stage.$geoNear.query);
|
||||||
schema,
|
|
||||||
stage.$geoNear.query
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return stage;
|
return stage;
|
||||||
});
|
});
|
||||||
@@ -894,8 +782,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
if (
|
if (
|
||||||
result._id == null ||
|
result._id == null ||
|
||||||
result._id == undefined ||
|
result._id == undefined ||
|
||||||
(['object', 'string'].includes(typeof result._id) &&
|
(['object', 'string'].includes(typeof result._id) && _.isEmpty(result._id))
|
||||||
_.isEmpty(result._id))
|
|
||||||
) {
|
) {
|
||||||
result._id = null;
|
result._id = null;
|
||||||
}
|
}
|
||||||
@@ -905,11 +792,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
});
|
});
|
||||||
return results;
|
return results;
|
||||||
})
|
})
|
||||||
.then(objects =>
|
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)))
|
||||||
objects.map(object =>
|
|
||||||
mongoObjectToParseObject(className, object, schema)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -945,20 +828,12 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
// Pass objects down to MongoDB...this is more than likely an $exists operator.
|
// Pass objects down to MongoDB...this is more than likely an $exists operator.
|
||||||
returnValue[`_p_${field}`] = pipeline[field];
|
returnValue[`_p_${field}`] = pipeline[field];
|
||||||
} else {
|
} else {
|
||||||
returnValue[
|
returnValue[`_p_${field}`] = `${schema.fields[field].targetClass}$${pipeline[field]}`;
|
||||||
`_p_${field}`
|
|
||||||
] = `${schema.fields[field].targetClass}$${pipeline[field]}`;
|
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (schema.fields[field] && schema.fields[field].type === 'Date') {
|
||||||
schema.fields[field] &&
|
|
||||||
schema.fields[field].type === 'Date'
|
|
||||||
) {
|
|
||||||
returnValue[field] = this._convertToDate(pipeline[field]);
|
returnValue[field] = this._convertToDate(pipeline[field]);
|
||||||
} else {
|
} else {
|
||||||
returnValue[field] = this._parseAggregateArgs(
|
returnValue[field] = this._parseAggregateArgs(schema, pipeline[field]);
|
||||||
schema,
|
|
||||||
pipeline[field]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field === 'objectId') {
|
if (field === 'objectId') {
|
||||||
@@ -1011,16 +886,11 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
// updatedAt or objectId and change it accordingly.
|
// updatedAt or objectId and change it accordingly.
|
||||||
_parseAggregateGroupArgs(schema: any, pipeline: any): any {
|
_parseAggregateGroupArgs(schema: any, pipeline: any): any {
|
||||||
if (Array.isArray(pipeline)) {
|
if (Array.isArray(pipeline)) {
|
||||||
return pipeline.map(value =>
|
return pipeline.map(value => this._parseAggregateGroupArgs(schema, value));
|
||||||
this._parseAggregateGroupArgs(schema, value)
|
|
||||||
);
|
|
||||||
} else if (typeof pipeline === 'object') {
|
} else if (typeof pipeline === 'object') {
|
||||||
const returnValue = {};
|
const returnValue = {};
|
||||||
for (const field in pipeline) {
|
for (const field in pipeline) {
|
||||||
returnValue[field] = this._parseAggregateGroupArgs(
|
returnValue[field] = this._parseAggregateGroupArgs(schema, pipeline[field]);
|
||||||
schema,
|
|
||||||
pipeline[field]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return returnValue;
|
return returnValue;
|
||||||
} else if (typeof pipeline === 'string') {
|
} else if (typeof pipeline === 'string') {
|
||||||
@@ -1077,10 +947,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
case '':
|
case '':
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Not supported read preference.');
|
||||||
Parse.Error.INVALID_QUERY,
|
|
||||||
'Not supported read preference.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return readPreference;
|
return readPreference;
|
||||||
}
|
}
|
||||||
@@ -1111,11 +978,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
createTextIndexesIfNeeded(
|
createTextIndexesIfNeeded(className: string, query: QueryType, schema: any): Promise<void> {
|
||||||
className: string,
|
|
||||||
query: QueryType,
|
|
||||||
schema: any
|
|
||||||
): Promise<void> {
|
|
||||||
for (const fieldName in query) {
|
for (const fieldName in query) {
|
||||||
if (!query[fieldName] || !query[fieldName].$text) {
|
if (!query[fieldName] || !query[fieldName].$text) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -20,27 +20,16 @@ const transformKey = (className, fieldName, schema) => {
|
|||||||
return 'times_used';
|
return 'times_used';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (schema.fields[fieldName] && schema.fields[fieldName].__type == 'Pointer') {
|
||||||
schema.fields[fieldName] &&
|
|
||||||
schema.fields[fieldName].__type == 'Pointer'
|
|
||||||
) {
|
|
||||||
fieldName = '_p_' + fieldName;
|
fieldName = '_p_' + fieldName;
|
||||||
} else if (
|
} else if (schema.fields[fieldName] && schema.fields[fieldName].type == 'Pointer') {
|
||||||
schema.fields[fieldName] &&
|
|
||||||
schema.fields[fieldName].type == 'Pointer'
|
|
||||||
) {
|
|
||||||
fieldName = '_p_' + fieldName;
|
fieldName = '_p_' + fieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fieldName;
|
return fieldName;
|
||||||
};
|
};
|
||||||
|
|
||||||
const transformKeyValueForUpdate = (
|
const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSchema) => {
|
||||||
className,
|
|
||||||
restKey,
|
|
||||||
restValue,
|
|
||||||
parseFormatSchema
|
|
||||||
) => {
|
|
||||||
// Check if the schema is known since it's a built-in field.
|
// Check if the schema is known since it's a built-in field.
|
||||||
var key = restKey;
|
var key = restKey;
|
||||||
var timeField = false;
|
var timeField = false;
|
||||||
@@ -109,11 +98,8 @@ const transformKeyValueForUpdate = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(parseFormatSchema.fields[key] &&
|
(parseFormatSchema.fields[key] && parseFormatSchema.fields[key].type === 'Pointer') ||
|
||||||
parseFormatSchema.fields[key].type === 'Pointer') ||
|
(!parseFormatSchema.fields[key] && restValue && restValue.__type == 'Pointer')
|
||||||
(!parseFormatSchema.fields[key] &&
|
|
||||||
restValue &&
|
|
||||||
restValue.__type == 'Pointer')
|
|
||||||
) {
|
) {
|
||||||
key = '_p_' + key;
|
key = '_p_' + key;
|
||||||
}
|
}
|
||||||
@@ -179,7 +165,7 @@ const isAllValuesRegexOrNone = values => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isAnyValueRegex = values => {
|
const isAnyValueRegex = values => {
|
||||||
return values.some(function(value) {
|
return values.some(function (value) {
|
||||||
return isRegex(value);
|
return isRegex(value);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -292,9 +278,7 @@ function transformQueryKeyValue(className, key, value, schema, count = false) {
|
|||||||
case '$nor':
|
case '$nor':
|
||||||
return {
|
return {
|
||||||
key: key,
|
key: key,
|
||||||
value: value.map(subQuery =>
|
value: value.map(subQuery => transformWhere(className, subQuery, schema, count)),
|
||||||
transformWhere(className, subQuery, schema, count)
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
case 'lastUsed':
|
case 'lastUsed':
|
||||||
if (valueAsDate(value)) {
|
if (valueAsDate(value)) {
|
||||||
@@ -315,17 +299,13 @@ function transformQueryKeyValue(className, key, value, schema, count = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const expectedTypeIsArray =
|
const expectedTypeIsArray = schema && schema.fields[key] && schema.fields[key].type === 'Array';
|
||||||
schema && schema.fields[key] && schema.fields[key].type === 'Array';
|
|
||||||
|
|
||||||
const expectedTypeIsPointer =
|
const expectedTypeIsPointer =
|
||||||
schema && schema.fields[key] && schema.fields[key].type === 'Pointer';
|
schema && schema.fields[key] && schema.fields[key].type === 'Pointer';
|
||||||
|
|
||||||
const field = schema && schema.fields[key];
|
const field = schema && schema.fields[key];
|
||||||
if (
|
if (expectedTypeIsPointer || (!schema && value && value.__type === 'Pointer')) {
|
||||||
expectedTypeIsPointer ||
|
|
||||||
(!schema && value && value.__type === 'Pointer')
|
|
||||||
) {
|
|
||||||
key = '_p_' + key;
|
key = '_p_' + key;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,23 +342,13 @@ function transformQueryKeyValue(className, key, value, schema, count = false) {
|
|||||||
function transformWhere(className, restWhere, schema, count = false) {
|
function transformWhere(className, restWhere, schema, count = false) {
|
||||||
const mongoWhere = {};
|
const mongoWhere = {};
|
||||||
for (const restKey in restWhere) {
|
for (const restKey in restWhere) {
|
||||||
const out = transformQueryKeyValue(
|
const out = transformQueryKeyValue(className, restKey, restWhere[restKey], schema, count);
|
||||||
className,
|
|
||||||
restKey,
|
|
||||||
restWhere[restKey],
|
|
||||||
schema,
|
|
||||||
count
|
|
||||||
);
|
|
||||||
mongoWhere[out.key] = out.value;
|
mongoWhere[out.key] = out.value;
|
||||||
}
|
}
|
||||||
return mongoWhere;
|
return mongoWhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseObjectKeyValueToMongoObjectKeyValue = (
|
const parseObjectKeyValueToMongoObjectKeyValue = (restKey, restValue, schema) => {
|
||||||
restKey,
|
|
||||||
restValue,
|
|
||||||
schema
|
|
||||||
) => {
|
|
||||||
// Check if the schema is known since it's a built-in field.
|
// Check if the schema is known since it's a built-in field.
|
||||||
let transformedValue;
|
let transformedValue;
|
||||||
let coercedToDate;
|
let coercedToDate;
|
||||||
@@ -388,37 +358,27 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
|
|||||||
case 'expiresAt':
|
case 'expiresAt':
|
||||||
transformedValue = transformTopLevelAtom(restValue);
|
transformedValue = transformTopLevelAtom(restValue);
|
||||||
coercedToDate =
|
coercedToDate =
|
||||||
typeof transformedValue === 'string'
|
typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
|
||||||
? new Date(transformedValue)
|
|
||||||
: transformedValue;
|
|
||||||
return { key: 'expiresAt', value: coercedToDate };
|
return { key: 'expiresAt', value: coercedToDate };
|
||||||
case '_email_verify_token_expires_at':
|
case '_email_verify_token_expires_at':
|
||||||
transformedValue = transformTopLevelAtom(restValue);
|
transformedValue = transformTopLevelAtom(restValue);
|
||||||
coercedToDate =
|
coercedToDate =
|
||||||
typeof transformedValue === 'string'
|
typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
|
||||||
? new Date(transformedValue)
|
|
||||||
: transformedValue;
|
|
||||||
return { key: '_email_verify_token_expires_at', value: coercedToDate };
|
return { key: '_email_verify_token_expires_at', value: coercedToDate };
|
||||||
case '_account_lockout_expires_at':
|
case '_account_lockout_expires_at':
|
||||||
transformedValue = transformTopLevelAtom(restValue);
|
transformedValue = transformTopLevelAtom(restValue);
|
||||||
coercedToDate =
|
coercedToDate =
|
||||||
typeof transformedValue === 'string'
|
typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
|
||||||
? new Date(transformedValue)
|
|
||||||
: transformedValue;
|
|
||||||
return { key: '_account_lockout_expires_at', value: coercedToDate };
|
return { key: '_account_lockout_expires_at', value: coercedToDate };
|
||||||
case '_perishable_token_expires_at':
|
case '_perishable_token_expires_at':
|
||||||
transformedValue = transformTopLevelAtom(restValue);
|
transformedValue = transformTopLevelAtom(restValue);
|
||||||
coercedToDate =
|
coercedToDate =
|
||||||
typeof transformedValue === 'string'
|
typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
|
||||||
? new Date(transformedValue)
|
|
||||||
: transformedValue;
|
|
||||||
return { key: '_perishable_token_expires_at', value: coercedToDate };
|
return { key: '_perishable_token_expires_at', value: coercedToDate };
|
||||||
case '_password_changed_at':
|
case '_password_changed_at':
|
||||||
transformedValue = transformTopLevelAtom(restValue);
|
transformedValue = transformTopLevelAtom(restValue);
|
||||||
coercedToDate =
|
coercedToDate =
|
||||||
typeof transformedValue === 'string'
|
typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue;
|
||||||
? new Date(transformedValue)
|
|
||||||
: transformedValue;
|
|
||||||
return { key: '_password_changed_at', value: coercedToDate };
|
return { key: '_password_changed_at', value: coercedToDate };
|
||||||
case '_failed_login_count':
|
case '_failed_login_count':
|
||||||
case '_rperm':
|
case '_rperm':
|
||||||
@@ -432,10 +392,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
|
|||||||
default:
|
default:
|
||||||
// Auth data should have been transformed already
|
// Auth data should have been transformed already
|
||||||
if (restKey.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
if (restKey.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'can only query on ' + restKey);
|
||||||
Parse.Error.INVALID_KEY_NAME,
|
|
||||||
'can only query on ' + restKey
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// Trust that the auth data has been transformed and save it directly
|
// Trust that the auth data has been transformed and save it directly
|
||||||
if (restKey.match(/^_auth_data_[a-zA-Z0-9_]+$/)) {
|
if (restKey.match(/^_auth_data_[a-zA-Z0-9_]+$/)) {
|
||||||
@@ -473,9 +430,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle normal objects by recursing
|
// Handle normal objects by recursing
|
||||||
if (
|
if (Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))) {
|
||||||
Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))
|
|
||||||
) {
|
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.INVALID_NESTED_KEY,
|
Parse.Error.INVALID_NESTED_KEY,
|
||||||
"Nested keys should not contain the '$' or '.' characters"
|
"Nested keys should not contain the '$' or '.' characters"
|
||||||
@@ -504,15 +459,11 @@ const parseObjectToMongoObjectForCreate = (className, restCreate, schema) => {
|
|||||||
|
|
||||||
// Use the legacy mongo format for createdAt and updatedAt
|
// Use the legacy mongo format for createdAt and updatedAt
|
||||||
if (mongoCreate.createdAt) {
|
if (mongoCreate.createdAt) {
|
||||||
mongoCreate._created_at = new Date(
|
mongoCreate._created_at = new Date(mongoCreate.createdAt.iso || mongoCreate.createdAt);
|
||||||
mongoCreate.createdAt.iso || mongoCreate.createdAt
|
|
||||||
);
|
|
||||||
delete mongoCreate.createdAt;
|
delete mongoCreate.createdAt;
|
||||||
}
|
}
|
||||||
if (mongoCreate.updatedAt) {
|
if (mongoCreate.updatedAt) {
|
||||||
mongoCreate._updated_at = new Date(
|
mongoCreate._updated_at = new Date(mongoCreate.updatedAt.iso || mongoCreate.updatedAt);
|
||||||
mongoCreate.updatedAt.iso || mongoCreate.updatedAt
|
|
||||||
);
|
|
||||||
delete mongoCreate.updatedAt;
|
delete mongoCreate.updatedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,22 +544,14 @@ function CannotTransform() {}
|
|||||||
|
|
||||||
const transformInteriorAtom = atom => {
|
const transformInteriorAtom = atom => {
|
||||||
// TODO: check validity harder for the __type-defined types
|
// TODO: check validity harder for the __type-defined types
|
||||||
if (
|
if (typeof atom === 'object' && atom && !(atom instanceof Date) && atom.__type === 'Pointer') {
|
||||||
typeof atom === 'object' &&
|
|
||||||
atom &&
|
|
||||||
!(atom instanceof Date) &&
|
|
||||||
atom.__type === 'Pointer'
|
|
||||||
) {
|
|
||||||
return {
|
return {
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
className: atom.className,
|
className: atom.className,
|
||||||
objectId: atom.objectId,
|
objectId: atom.objectId,
|
||||||
};
|
};
|
||||||
} else if (typeof atom === 'function' || typeof atom === 'symbol') {
|
} else if (typeof atom === 'function' || typeof atom === 'symbol') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `cannot transform value: ${atom}`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`cannot transform value: ${atom}`
|
|
||||||
);
|
|
||||||
} else if (DateCoder.isValidJSON(atom)) {
|
} else if (DateCoder.isValidJSON(atom)) {
|
||||||
return DateCoder.JSONToDatabase(atom);
|
return DateCoder.JSONToDatabase(atom);
|
||||||
} else if (BytesCoder.isValidJSON(atom)) {
|
} else if (BytesCoder.isValidJSON(atom)) {
|
||||||
@@ -640,10 +583,7 @@ function transformTopLevelAtom(atom, field) {
|
|||||||
return atom;
|
return atom;
|
||||||
case 'symbol':
|
case 'symbol':
|
||||||
case 'function':
|
case 'function':
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `cannot transform value: ${atom}`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`cannot transform value: ${atom}`
|
|
||||||
);
|
|
||||||
case 'object':
|
case 'object':
|
||||||
if (atom instanceof Date) {
|
if (atom instanceof Date) {
|
||||||
// Technically dates are not rest format, but, it seems pretty
|
// Technically dates are not rest format, but, it seems pretty
|
||||||
@@ -822,16 +762,11 @@ function transformConstraint(constraint, field, count = false) {
|
|||||||
if (typeof constraint !== 'object' || !constraint) {
|
if (typeof constraint !== 'object' || !constraint) {
|
||||||
return CannotTransform;
|
return CannotTransform;
|
||||||
}
|
}
|
||||||
const transformFunction = inArray
|
const transformFunction = inArray ? transformInteriorAtom : transformTopLevelAtom;
|
||||||
? transformInteriorAtom
|
|
||||||
: transformTopLevelAtom;
|
|
||||||
const transformer = atom => {
|
const transformer = atom => {
|
||||||
const result = transformFunction(atom, field);
|
const result = transformFunction(atom, field);
|
||||||
if (result === CannotTransform) {
|
if (result === CannotTransform) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad atom: ${JSON.stringify(atom)}`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`bad atom: ${JSON.stringify(atom)}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
@@ -839,9 +774,7 @@ function transformConstraint(constraint, field, count = false) {
|
|||||||
// This is a hack so that:
|
// This is a hack so that:
|
||||||
// $regex is handled before $options
|
// $regex is handled before $options
|
||||||
// $nearSphere is handled before $maxDistance
|
// $nearSphere is handled before $maxDistance
|
||||||
var keys = Object.keys(constraint)
|
var keys = Object.keys(constraint).sort().reverse();
|
||||||
.sort()
|
|
||||||
.reverse();
|
|
||||||
var answer = {};
|
var answer = {};
|
||||||
for (var key of keys) {
|
for (var key of keys) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@@ -892,10 +825,7 @@ function transformConstraint(constraint, field, count = false) {
|
|||||||
case '$nin': {
|
case '$nin': {
|
||||||
const arr = constraint[key];
|
const arr = constraint[key];
|
||||||
if (!(arr instanceof Array)) {
|
if (!(arr instanceof Array)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value');
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
'bad ' + key + ' value'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
answer[key] = _.flatMap(arr, value => {
|
answer[key] = _.flatMap(arr, value => {
|
||||||
return (atom => {
|
return (atom => {
|
||||||
@@ -911,10 +841,7 @@ function transformConstraint(constraint, field, count = false) {
|
|||||||
case '$all': {
|
case '$all': {
|
||||||
const arr = constraint[key];
|
const arr = constraint[key];
|
||||||
if (!(arr instanceof Array)) {
|
if (!(arr instanceof Array)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value');
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
'bad ' + key + ' value'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
answer[key] = arr.map(transformInteriorAtom);
|
answer[key] = arr.map(transformInteriorAtom);
|
||||||
|
|
||||||
@@ -939,10 +866,7 @@ function transformConstraint(constraint, field, count = false) {
|
|||||||
case '$containedBy': {
|
case '$containedBy': {
|
||||||
const arr = constraint[key];
|
const arr = constraint[key];
|
||||||
if (!(arr instanceof Array)) {
|
if (!(arr instanceof Array)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $containedBy: should be an array`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`bad $containedBy: should be an array`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
answer.$elemMatch = {
|
answer.$elemMatch = {
|
||||||
$nin: arr.map(transformer),
|
$nin: arr.map(transformer),
|
||||||
@@ -956,33 +880,21 @@ function transformConstraint(constraint, field, count = false) {
|
|||||||
case '$text': {
|
case '$text': {
|
||||||
const search = constraint[key].$search;
|
const search = constraint[key].$search;
|
||||||
if (typeof search !== 'object') {
|
if (typeof search !== 'object') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $search, should be object`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`bad $text: $search, should be object`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!search.$term || typeof search.$term !== 'string') {
|
if (!search.$term || typeof search.$term !== 'string') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $term, should be string`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`bad $text: $term, should be string`
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
answer[key] = {
|
answer[key] = {
|
||||||
$search: search.$term,
|
$search: search.$term,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (search.$language && typeof search.$language !== 'string') {
|
if (search.$language && typeof search.$language !== 'string') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $language, should be string`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`bad $text: $language, should be string`
|
|
||||||
);
|
|
||||||
} else if (search.$language) {
|
} else if (search.$language) {
|
||||||
answer[key].$language = search.$language;
|
answer[key].$language = search.$language;
|
||||||
}
|
}
|
||||||
if (
|
if (search.$caseSensitive && typeof search.$caseSensitive !== 'boolean') {
|
||||||
search.$caseSensitive &&
|
|
||||||
typeof search.$caseSensitive !== 'boolean'
|
|
||||||
) {
|
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.INVALID_JSON,
|
Parse.Error.INVALID_JSON,
|
||||||
`bad $text: $caseSensitive, should be boolean`
|
`bad $text: $caseSensitive, should be boolean`
|
||||||
@@ -990,10 +902,7 @@ function transformConstraint(constraint, field, count = false) {
|
|||||||
} else if (search.$caseSensitive) {
|
} else if (search.$caseSensitive) {
|
||||||
answer[key].$caseSensitive = search.$caseSensitive;
|
answer[key].$caseSensitive = search.$caseSensitive;
|
||||||
}
|
}
|
||||||
if (
|
if (search.$diacriticSensitive && typeof search.$diacriticSensitive !== 'boolean') {
|
||||||
search.$diacriticSensitive &&
|
|
||||||
typeof search.$diacriticSensitive !== 'boolean'
|
|
||||||
) {
|
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.INVALID_JSON,
|
Parse.Error.INVALID_JSON,
|
||||||
`bad $text: $diacriticSensitive, should be boolean`
|
`bad $text: $diacriticSensitive, should be boolean`
|
||||||
@@ -1007,10 +916,7 @@ function transformConstraint(constraint, field, count = false) {
|
|||||||
const point = constraint[key];
|
const point = constraint[key];
|
||||||
if (count) {
|
if (count) {
|
||||||
answer.$geoWithin = {
|
answer.$geoWithin = {
|
||||||
$centerSphere: [
|
$centerSphere: [[point.longitude, point.latitude], constraint.$maxDistance],
|
||||||
[point.longitude, point.latitude],
|
|
||||||
constraint.$maxDistance,
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
answer[key] = [point.longitude, point.latitude];
|
answer[key] = [point.longitude, point.latitude];
|
||||||
@@ -1046,10 +952,7 @@ function transformConstraint(constraint, field, count = false) {
|
|||||||
case '$within':
|
case '$within':
|
||||||
var box = constraint[key]['$box'];
|
var box = constraint[key]['$box'];
|
||||||
if (!box || box.length != 2) {
|
if (!box || box.length != 2) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'malformatted $within arg');
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
'malformatted $within arg'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
answer[key] = {
|
answer[key] = {
|
||||||
$box: [
|
$box: [
|
||||||
@@ -1092,10 +995,7 @@ function transformConstraint(constraint, field, count = false) {
|
|||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
if (!GeoPointCoder.isValidJSON(point)) {
|
if (!GeoPointCoder.isValidJSON(point)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value');
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
'bad $geoWithin value'
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
Parse.GeoPoint._validate(point.latitude, point.longitude);
|
Parse.GeoPoint._validate(point.latitude, point.longitude);
|
||||||
}
|
}
|
||||||
@@ -1156,10 +1056,7 @@ function transformConstraint(constraint, field, count = false) {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if (key.match(/^\$+/)) {
|
if (key.match(/^\$+/)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad constraint: ' + key);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
'bad constraint: ' + key
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return CannotTransform;
|
return CannotTransform;
|
||||||
}
|
}
|
||||||
@@ -1188,10 +1085,7 @@ function transformUpdateOperator({ __op, amount, objects }, flatten) {
|
|||||||
|
|
||||||
case 'Increment':
|
case 'Increment':
|
||||||
if (typeof amount !== 'number') {
|
if (typeof amount !== 'number') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'incrementing must provide a number');
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
'incrementing must provide a number'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (flatten) {
|
if (flatten) {
|
||||||
return amount;
|
return amount;
|
||||||
@@ -1202,10 +1096,7 @@ function transformUpdateOperator({ __op, amount, objects }, flatten) {
|
|||||||
case 'Add':
|
case 'Add':
|
||||||
case 'AddUnique':
|
case 'AddUnique':
|
||||||
if (!(objects instanceof Array)) {
|
if (!(objects instanceof Array)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
'objects to add must be an array'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
var toAdd = objects.map(transformInteriorAtom);
|
var toAdd = objects.map(transformInteriorAtom);
|
||||||
if (flatten) {
|
if (flatten) {
|
||||||
@@ -1220,10 +1111,7 @@ function transformUpdateOperator({ __op, amount, objects }, flatten) {
|
|||||||
|
|
||||||
case 'Remove':
|
case 'Remove':
|
||||||
if (!(objects instanceof Array)) {
|
if (!(objects instanceof Array)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to remove must be an array');
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
'objects to remove must be an array'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
var toRemove = objects.map(transformInteriorAtom);
|
var toRemove = objects.map(transformInteriorAtom);
|
||||||
if (flatten) {
|
if (flatten) {
|
||||||
@@ -1379,15 +1267,11 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
|
|||||||
break;
|
break;
|
||||||
case 'updatedAt':
|
case 'updatedAt':
|
||||||
case '_updated_at':
|
case '_updated_at':
|
||||||
restObject['updatedAt'] = Parse._encode(
|
restObject['updatedAt'] = Parse._encode(new Date(mongoObject[key])).iso;
|
||||||
new Date(mongoObject[key])
|
|
||||||
).iso;
|
|
||||||
break;
|
break;
|
||||||
case 'createdAt':
|
case 'createdAt':
|
||||||
case '_created_at':
|
case '_created_at':
|
||||||
restObject['createdAt'] = Parse._encode(
|
restObject['createdAt'] = Parse._encode(new Date(mongoObject[key])).iso;
|
||||||
new Date(mongoObject[key])
|
|
||||||
).iso;
|
|
||||||
break;
|
break;
|
||||||
case 'expiresAt':
|
case 'expiresAt':
|
||||||
case '_expiresAt':
|
case '_expiresAt':
|
||||||
@@ -1395,9 +1279,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
|
|||||||
break;
|
break;
|
||||||
case 'lastUsed':
|
case 'lastUsed':
|
||||||
case '_last_used':
|
case '_last_used':
|
||||||
restObject['lastUsed'] = Parse._encode(
|
restObject['lastUsed'] = Parse._encode(new Date(mongoObject[key])).iso;
|
||||||
new Date(mongoObject[key])
|
|
||||||
).iso;
|
|
||||||
break;
|
break;
|
||||||
case 'timesUsed':
|
case 'timesUsed':
|
||||||
case 'times_used':
|
case 'times_used':
|
||||||
@@ -1445,11 +1327,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
|
|||||||
if (mongoObject[key] === null) {
|
if (mongoObject[key] === null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
restObject[newKey] = transformPointerString(
|
restObject[newKey] = transformPointerString(schema, newKey, mongoObject[key]);
|
||||||
schema,
|
|
||||||
newKey,
|
|
||||||
mongoObject[key]
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
} else if (key[0] == '_' && key != '__type') {
|
} else if (key[0] == '_' && key != '__type') {
|
||||||
throw 'bad key in untransform: ' + key;
|
throw 'bad key in untransform: ' + key;
|
||||||
@@ -1488,9 +1366,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
restObject[key] = nestedMongoObjectToNestedParseObject(
|
restObject[key] = nestedMongoObjectToNestedParseObject(mongoObject[key]);
|
||||||
mongoObject[key]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1518,16 +1394,12 @@ var DateCoder = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
isValidJSON(value) {
|
isValidJSON(value) {
|
||||||
return (
|
return typeof value === 'object' && value !== null && value.__type === 'Date';
|
||||||
typeof value === 'object' && value !== null && value.__type === 'Date'
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var BytesCoder = {
|
var BytesCoder = {
|
||||||
base64Pattern: new RegExp(
|
base64Pattern: new RegExp('^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'),
|
||||||
'^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'
|
|
||||||
),
|
|
||||||
isBase64Value(object) {
|
isBase64Value(object) {
|
||||||
if (typeof object !== 'string') {
|
if (typeof object !== 'string') {
|
||||||
return false;
|
return false;
|
||||||
@@ -1557,9 +1429,7 @@ var BytesCoder = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
isValidJSON(value) {
|
isValidJSON(value) {
|
||||||
return (
|
return typeof value === 'object' && value !== null && value.__type === 'Bytes';
|
||||||
typeof value === 'object' && value !== null && value.__type === 'Bytes'
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1581,9 +1451,7 @@ var GeoPointCoder = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
isValidJSON(value) {
|
isValidJSON(value) {
|
||||||
return (
|
return typeof value === 'object' && value !== null && value.__type === 'GeoPoint';
|
||||||
typeof value === 'object' && value !== null && value.__type === 'GeoPoint'
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1648,9 +1516,7 @@ var PolygonCoder = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
isValidJSON(value) {
|
isValidJSON(value) {
|
||||||
return (
|
return typeof value === 'object' && value !== null && value.__type === 'Polygon';
|
||||||
typeof value === 'object' && value !== null && value.__type === 'Polygon'
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1671,9 +1537,7 @@ var FileCoder = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
isValidJSON(value) {
|
isValidJSON(value) {
|
||||||
return (
|
return typeof value === 'object' && value !== null && value.__type === 'File';
|
||||||
typeof value === 'object' && value !== null && value.__type === 'File'
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ function getDatabaseOptionsFromURI(uri) {
|
|||||||
|
|
||||||
databaseOptions.host = parsedURI.hostname || 'localhost';
|
databaseOptions.host = parsedURI.hostname || 'localhost';
|
||||||
databaseOptions.port = parsedURI.port ? parseInt(parsedURI.port) : 5432;
|
databaseOptions.port = parsedURI.port ? parseInt(parsedURI.port) : 5432;
|
||||||
databaseOptions.database = parsedURI.pathname
|
databaseOptions.database = parsedURI.pathname ? parsedURI.pathname.substr(1) : undefined;
|
||||||
? parsedURI.pathname.substr(1)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
databaseOptions.user = authParts.length > 0 ? authParts[0] : '';
|
databaseOptions.user = authParts.length > 0 ? authParts[0] : '';
|
||||||
databaseOptions.password = authParts.length > 1 ? authParts[1] : '';
|
databaseOptions.password = authParts.length > 1 ? authParts[1] : '';
|
||||||
@@ -55,14 +53,11 @@ function getDatabaseOptionsFromURI(uri) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
databaseOptions.binary =
|
databaseOptions.binary =
|
||||||
queryParams.binary && queryParams.binary.toLowerCase() === 'true'
|
queryParams.binary && queryParams.binary.toLowerCase() === 'true' ? true : false;
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
|
|
||||||
databaseOptions.client_encoding = queryParams.client_encoding;
|
databaseOptions.client_encoding = queryParams.client_encoding;
|
||||||
databaseOptions.application_name = queryParams.application_name;
|
databaseOptions.application_name = queryParams.application_name;
|
||||||
databaseOptions.fallback_application_name =
|
databaseOptions.fallback_application_name = queryParams.fallback_application_name;
|
||||||
queryParams.fallback_application_name;
|
|
||||||
|
|
||||||
if (queryParams.poolSize) {
|
if (queryParams.poolSize) {
|
||||||
databaseOptions.poolSize = parseInt(queryParams.poolSize) || 10;
|
databaseOptions.poolSize = parseInt(queryParams.poolSize) || 10;
|
||||||
@@ -77,8 +72,7 @@ function getDatabaseOptionsFromURI(uri) {
|
|||||||
databaseOptions.idleTimeoutMillis = parseInt(queryParams.idleTimeoutMillis);
|
databaseOptions.idleTimeoutMillis = parseInt(queryParams.idleTimeoutMillis);
|
||||||
}
|
}
|
||||||
if (queryParams.keepAlive) {
|
if (queryParams.keepAlive) {
|
||||||
databaseOptions.keepAlive =
|
databaseOptions.keepAlive = queryParams.keepAlive.toLowerCase() === 'true' ? true : false;
|
||||||
queryParams.keepAlive.toLowerCase() === 'true' ? true : false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return databaseOptions;
|
return databaseOptions;
|
||||||
|
|||||||
@@ -253,12 +253,7 @@ interface WhereClause {
|
|||||||
sorts: Array<any>;
|
sorts: Array<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildWhereClause = ({
|
const buildWhereClause = ({ schema, query, index, caseInsensitive }): WhereClause => {
|
||||||
schema,
|
|
||||||
query,
|
|
||||||
index,
|
|
||||||
caseInsensitive,
|
|
||||||
}): WhereClause => {
|
|
||||||
const patterns = [];
|
const patterns = [];
|
||||||
let values = [];
|
let values = [];
|
||||||
const sorts = [];
|
const sorts = [];
|
||||||
@@ -266,9 +261,7 @@ const buildWhereClause = ({
|
|||||||
schema = toPostgresSchema(schema);
|
schema = toPostgresSchema(schema);
|
||||||
for (const fieldName in query) {
|
for (const fieldName in query) {
|
||||||
const isArrayField =
|
const isArrayField =
|
||||||
schema.fields &&
|
schema.fields && schema.fields[fieldName] && schema.fields[fieldName].type === 'Array';
|
||||||
schema.fields[fieldName] &&
|
|
||||||
schema.fields[fieldName].type === 'Array';
|
|
||||||
const initialPatternsLength = patterns.length;
|
const initialPatternsLength = patterns.length;
|
||||||
const fieldValue = query[fieldName];
|
const fieldValue = query[fieldName];
|
||||||
|
|
||||||
@@ -284,10 +277,7 @@ const buildWhereClause = ({
|
|||||||
if (authDataMatch) {
|
if (authDataMatch) {
|
||||||
// TODO: Handle querying by _auth_data_provider, authData is stored in authData field
|
// TODO: Handle querying by _auth_data_provider, authData is stored in authData field
|
||||||
continue;
|
continue;
|
||||||
} else if (
|
} else if (caseInsensitive && (fieldName === 'username' || fieldName === 'email')) {
|
||||||
caseInsensitive &&
|
|
||||||
(fieldName === 'username' || fieldName === 'email')
|
|
||||||
) {
|
|
||||||
patterns.push(`LOWER($${index}:name) = LOWER($${index + 1})`);
|
patterns.push(`LOWER($${index}:name) = LOWER($${index + 1})`);
|
||||||
values.push(fieldName, fieldValue);
|
values.push(fieldName, fieldValue);
|
||||||
index += 2;
|
index += 2;
|
||||||
@@ -324,10 +314,7 @@ const buildWhereClause = ({
|
|||||||
} else if (typeof fieldValue === 'boolean') {
|
} else if (typeof fieldValue === 'boolean') {
|
||||||
patterns.push(`$${index}:name = $${index + 1}`);
|
patterns.push(`$${index}:name = $${index + 1}`);
|
||||||
// Can't cast boolean to double precision
|
// Can't cast boolean to double precision
|
||||||
if (
|
if (schema.fields[fieldName] && schema.fields[fieldName].type === 'Number') {
|
||||||
schema.fields[fieldName] &&
|
|
||||||
schema.fields[fieldName].type === 'Number'
|
|
||||||
) {
|
|
||||||
// Should always return zero results
|
// Should always return zero results
|
||||||
const MAX_INT_PLUS_ONE = 9223372036854775808;
|
const MAX_INT_PLUS_ONE = 9223372036854775808;
|
||||||
values.push(fieldName, MAX_INT_PLUS_ONE);
|
values.push(fieldName, MAX_INT_PLUS_ONE);
|
||||||
@@ -377,9 +364,7 @@ const buildWhereClause = ({
|
|||||||
// if not null, we need to manually exclude null
|
// if not null, we need to manually exclude null
|
||||||
if (fieldValue.$ne.__type === 'GeoPoint') {
|
if (fieldValue.$ne.__type === 'GeoPoint') {
|
||||||
patterns.push(
|
patterns.push(
|
||||||
`($${index}:name <> POINT($${index + 1}, $${
|
`($${index}:name <> POINT($${index + 1}, $${index + 2}) OR $${index}:name IS NULL)`
|
||||||
index + 2
|
|
||||||
}) OR $${index}:name IS NULL)`
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (fieldName.indexOf('.') >= 0) {
|
if (fieldName.indexOf('.') >= 0) {
|
||||||
@@ -388,9 +373,7 @@ const buildWhereClause = ({
|
|||||||
`(${constraintFieldName} <> $${index} OR ${constraintFieldName} IS NULL)`
|
`(${constraintFieldName} <> $${index} OR ${constraintFieldName} IS NULL)`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
patterns.push(
|
patterns.push(`($${index}:name <> $${index + 1} OR $${index}:name IS NULL)`);
|
||||||
`($${index}:name <> $${index + 1} OR $${index}:name IS NULL)`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -421,8 +404,7 @@ const buildWhereClause = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const isInOrNin =
|
const isInOrNin = Array.isArray(fieldValue.$in) || Array.isArray(fieldValue.$nin);
|
||||||
Array.isArray(fieldValue.$in) || Array.isArray(fieldValue.$nin);
|
|
||||||
if (
|
if (
|
||||||
Array.isArray(fieldValue.$in) &&
|
Array.isArray(fieldValue.$in) &&
|
||||||
isArrayField &&
|
isArrayField &&
|
||||||
@@ -441,9 +423,7 @@ const buildWhereClause = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (allowNull) {
|
if (allowNull) {
|
||||||
patterns.push(
|
patterns.push(`($${index}:name IS NULL OR $${index}:name && ARRAY[${inPatterns.join()}])`);
|
||||||
`($${index}:name IS NULL OR $${index}:name && ARRAY[${inPatterns.join()}])`
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
patterns.push(`$${index}:name && ARRAY[${inPatterns.join()}]`);
|
patterns.push(`$${index}:name && ARRAY[${inPatterns.join()}]`);
|
||||||
}
|
}
|
||||||
@@ -453,9 +433,7 @@ const buildWhereClause = ({
|
|||||||
const not = notIn ? ' NOT ' : '';
|
const not = notIn ? ' NOT ' : '';
|
||||||
if (baseArray.length > 0) {
|
if (baseArray.length > 0) {
|
||||||
if (isArrayField) {
|
if (isArrayField) {
|
||||||
patterns.push(
|
patterns.push(`${not} array_contains($${index}:name, $${index + 1})`);
|
||||||
`${not} array_contains($${index}:name, $${index + 1})`
|
|
||||||
);
|
|
||||||
values.push(fieldName, JSON.stringify(baseArray));
|
values.push(fieldName, JSON.stringify(baseArray));
|
||||||
index += 2;
|
index += 2;
|
||||||
} else {
|
} else {
|
||||||
@@ -518,13 +496,9 @@ const buildWhereClause = ({
|
|||||||
const value = processRegexPattern(fieldValue.$all[i].$regex);
|
const value = processRegexPattern(fieldValue.$all[i].$regex);
|
||||||
fieldValue.$all[i] = value.substring(1) + '%';
|
fieldValue.$all[i] = value.substring(1) + '%';
|
||||||
}
|
}
|
||||||
patterns.push(
|
patterns.push(`array_contains_all_regex($${index}:name, $${index + 1}::jsonb)`);
|
||||||
`array_contains_all_regex($${index}:name, $${index + 1}::jsonb)`
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
patterns.push(
|
patterns.push(`array_contains_all($${index}:name, $${index + 1}::jsonb)`);
|
||||||
`array_contains_all($${index}:name, $${index + 1}::jsonb)`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
values.push(fieldName, JSON.stringify(fieldValue.$all));
|
values.push(fieldName, JSON.stringify(fieldValue.$all));
|
||||||
index += 2;
|
index += 2;
|
||||||
@@ -549,10 +523,7 @@ const buildWhereClause = ({
|
|||||||
if (fieldValue.$containedBy) {
|
if (fieldValue.$containedBy) {
|
||||||
const arr = fieldValue.$containedBy;
|
const arr = fieldValue.$containedBy;
|
||||||
if (!(arr instanceof Array)) {
|
if (!(arr instanceof Array)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $containedBy: should be an array`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`bad $containedBy: should be an array`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
patterns.push(`$${index}:name <@ $${index + 1}::jsonb`);
|
patterns.push(`$${index}:name <@ $${index + 1}::jsonb`);
|
||||||
@@ -564,22 +535,13 @@ const buildWhereClause = ({
|
|||||||
const search = fieldValue.$text.$search;
|
const search = fieldValue.$text.$search;
|
||||||
let language = 'english';
|
let language = 'english';
|
||||||
if (typeof search !== 'object') {
|
if (typeof search !== 'object') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $search, should be object`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`bad $text: $search, should be object`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!search.$term || typeof search.$term !== 'string') {
|
if (!search.$term || typeof search.$term !== 'string') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $term, should be string`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`bad $text: $term, should be string`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (search.$language && typeof search.$language !== 'string') {
|
if (search.$language && typeof search.$language !== 'string') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $language, should be string`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`bad $text: $language, should be string`
|
|
||||||
);
|
|
||||||
} else if (search.$language) {
|
} else if (search.$language) {
|
||||||
language = search.$language;
|
language = search.$language;
|
||||||
}
|
}
|
||||||
@@ -594,10 +556,7 @@ const buildWhereClause = ({
|
|||||||
`bad $text: $caseSensitive not supported, please use $regex or create a separate lower case column.`
|
`bad $text: $caseSensitive not supported, please use $regex or create a separate lower case column.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (
|
if (search.$diacriticSensitive && typeof search.$diacriticSensitive !== 'boolean') {
|
||||||
search.$diacriticSensitive &&
|
|
||||||
typeof search.$diacriticSensitive !== 'boolean'
|
|
||||||
) {
|
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.INVALID_JSON,
|
Parse.Error.INVALID_JSON,
|
||||||
`bad $text: $diacriticSensitive, should be boolean`
|
`bad $text: $diacriticSensitive, should be boolean`
|
||||||
@@ -609,9 +568,7 @@ const buildWhereClause = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
patterns.push(
|
patterns.push(
|
||||||
`to_tsvector($${index}, $${index + 1}:name) @@ to_tsquery($${
|
`to_tsvector($${index}, $${index + 1}:name) @@ to_tsquery($${index + 2}, $${index + 3})`
|
||||||
index + 2
|
|
||||||
}, $${index + 3})`
|
|
||||||
);
|
);
|
||||||
values.push(language, fieldName, language, search.$term);
|
values.push(language, fieldName, language, search.$term);
|
||||||
index += 4;
|
index += 4;
|
||||||
@@ -716,10 +673,7 @@ const buildWhereClause = ({
|
|||||||
return `(${point[0]}, ${point[1]})`;
|
return `(${point[0]}, ${point[1]})`;
|
||||||
}
|
}
|
||||||
if (typeof point !== 'object' || point.__type !== 'GeoPoint') {
|
if (typeof point !== 'object' || point.__type !== 'GeoPoint') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value');
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
'bad $geoWithin value'
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
Parse.GeoPoint._validate(point.latitude, point.longitude);
|
Parse.GeoPoint._validate(point.latitude, point.longitude);
|
||||||
}
|
}
|
||||||
@@ -830,9 +784,7 @@ const buildWhereClause = ({
|
|||||||
if (initialPatternsLength === patterns.length) {
|
if (initialPatternsLength === patterns.length) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.OPERATION_FORBIDDEN,
|
Parse.Error.OPERATION_FORBIDDEN,
|
||||||
`Postgres doesn't support this query type yet ${JSON.stringify(
|
`Postgres doesn't support this query type yet ${JSON.stringify(fieldValue)}`
|
||||||
fieldValue
|
|
||||||
)}`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -903,12 +855,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
const self = this;
|
const self = this;
|
||||||
await this._client.task('set-class-level-permissions', async t => {
|
await this._client.task('set-class-level-permissions', async t => {
|
||||||
await self._ensureSchemaCollectionExists(t);
|
await self._ensureSchemaCollectionExists(t);
|
||||||
const values = [
|
const values = [className, 'schema', 'classLevelPermissions', JSON.stringify(CLPs)];
|
||||||
className,
|
|
||||||
'schema',
|
|
||||||
'classLevelPermissions',
|
|
||||||
JSON.stringify(CLPs),
|
|
||||||
];
|
|
||||||
await t.none(
|
await t.none(
|
||||||
`UPDATE "_SCHEMA" SET $2:name = json_object_set_key($2:name, $3::text, $4::jsonb) WHERE "className" = $1`,
|
`UPDATE "_SCHEMA" SET $2:name = json_object_set_key($2:name, $3::text, $4::jsonb) WHERE "className" = $1`,
|
||||||
values
|
values
|
||||||
@@ -936,10 +883,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
Object.keys(submittedIndexes).forEach(name => {
|
Object.keys(submittedIndexes).forEach(name => {
|
||||||
const field = submittedIndexes[name];
|
const field = submittedIndexes[name];
|
||||||
if (existingIndexes[name] && field.__op !== 'Delete') {
|
if (existingIndexes[name] && field.__op !== 'Delete') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, `Index ${name} exists, cannot update.`);
|
||||||
Parse.Error.INVALID_QUERY,
|
|
||||||
`Index ${name} exists, cannot update.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!existingIndexes[name] && field.__op === 'Delete') {
|
if (!existingIndexes[name] && field.__op === 'Delete') {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
@@ -990,24 +934,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
'INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)',
|
'INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)',
|
||||||
{ className, schema }
|
{ className, schema }
|
||||||
);
|
);
|
||||||
await this.setIndexesWithSchemaFormat(
|
await this.setIndexesWithSchemaFormat(className, schema.indexes, {}, schema.fields, t);
|
||||||
className,
|
|
||||||
schema.indexes,
|
|
||||||
{},
|
|
||||||
schema.fields,
|
|
||||||
t
|
|
||||||
);
|
|
||||||
return toParseSchema(schema);
|
return toParseSchema(schema);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (
|
if (err.code === PostgresUniqueIndexViolationError && err.detail.includes(className)) {
|
||||||
err.code === PostgresUniqueIndexViolationError &&
|
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, `Class ${className} already exists.`);
|
||||||
err.detail.includes(className)
|
|
||||||
) {
|
|
||||||
throw new Parse.Error(
|
|
||||||
Parse.Error.DUPLICATE_VALUE,
|
|
||||||
`Class ${className} already exists.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
@@ -1093,24 +1025,14 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
const newColumns = Object.keys(schema.fields)
|
const newColumns = Object.keys(schema.fields)
|
||||||
.filter(item => columns.indexOf(item) === -1)
|
.filter(item => columns.indexOf(item) === -1)
|
||||||
.map(fieldName =>
|
.map(fieldName =>
|
||||||
self.addFieldIfNotExists(
|
self.addFieldIfNotExists(className, fieldName, schema.fields[fieldName], t)
|
||||||
className,
|
|
||||||
fieldName,
|
|
||||||
schema.fields[fieldName],
|
|
||||||
t
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await t.batch(newColumns);
|
await t.batch(newColumns);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async addFieldIfNotExists(
|
async addFieldIfNotExists(className: string, fieldName: string, type: any, conn: any) {
|
||||||
className: string,
|
|
||||||
fieldName: string,
|
|
||||||
type: any,
|
|
||||||
conn: any
|
|
||||||
) {
|
|
||||||
// TODO: Must be revised for invalid logic...
|
// TODO: Must be revised for invalid logic...
|
||||||
debug('addFieldIfNotExists', { className, fieldName, type });
|
debug('addFieldIfNotExists', { className, fieldName, type });
|
||||||
conn = conn || this._client;
|
conn = conn || this._client;
|
||||||
@@ -1128,11 +1050,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === PostgresRelationDoesNotExistError) {
|
if (error.code === PostgresRelationDoesNotExistError) {
|
||||||
return self.createClass(
|
return self.createClass(className, { fields: { [fieldName]: type } }, t);
|
||||||
className,
|
|
||||||
{ fields: { [fieldName]: type } },
|
|
||||||
t
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (error.code !== PostgresDuplicateColumnError) {
|
if (error.code !== PostgresDuplicateColumnError) {
|
||||||
throw error;
|
throw error;
|
||||||
@@ -1234,11 +1152,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
// may do so.
|
// may do so.
|
||||||
|
|
||||||
// Returns a Promise.
|
// Returns a Promise.
|
||||||
async deleteFields(
|
async deleteFields(className: string, schema: SchemaType, fieldNames: string[]): Promise<void> {
|
||||||
className: string,
|
|
||||||
schema: SchemaType,
|
|
||||||
fieldNames: string[]
|
|
||||||
): Promise<void> {
|
|
||||||
debug('deleteFields', className, fieldNames);
|
debug('deleteFields', className, fieldNames);
|
||||||
fieldNames = fieldNames.reduce((list: Array<string>, fieldName: string) => {
|
fieldNames = fieldNames.reduce((list: Array<string>, fieldName: string) => {
|
||||||
const field = schema.fields[fieldName];
|
const field = schema.fields[fieldName];
|
||||||
@@ -1257,15 +1171,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
.join(', DROP COLUMN');
|
.join(', DROP COLUMN');
|
||||||
|
|
||||||
await this._client.tx('delete-fields', async t => {
|
await this._client.tx('delete-fields', async t => {
|
||||||
await t.none(
|
await t.none('UPDATE "_SCHEMA" SET "schema" = $<schema> WHERE "className" = $<className>', {
|
||||||
'UPDATE "_SCHEMA" SET "schema" = $<schema> WHERE "className" = $<className>',
|
schema,
|
||||||
{ schema, className }
|
className,
|
||||||
);
|
});
|
||||||
if (values.length > 1) {
|
if (values.length > 1) {
|
||||||
await t.none(
|
await t.none(`ALTER TABLE $1:name DROP COLUMN IF EXISTS ${columns}`, values);
|
||||||
`ALTER TABLE $1:name DROP COLUMN IF EXISTS ${columns}`,
|
|
||||||
values
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1412,10 +1323,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
const fieldName = columnsArray[index];
|
const fieldName = columnsArray[index];
|
||||||
if (['_rperm', '_wperm'].indexOf(fieldName) >= 0) {
|
if (['_rperm', '_wperm'].indexOf(fieldName) >= 0) {
|
||||||
termination = '::text[]';
|
termination = '::text[]';
|
||||||
} else if (
|
} else if (schema.fields[fieldName] && schema.fields[fieldName].type === 'Array') {
|
||||||
schema.fields[fieldName] &&
|
|
||||||
schema.fields[fieldName].type === 'Array'
|
|
||||||
) {
|
|
||||||
termination = '::jsonb';
|
termination = '::jsonb';
|
||||||
}
|
}
|
||||||
return `$${index + 2 + columnsArray.length}${termination}`;
|
return `$${index + 2 + columnsArray.length}${termination}`;
|
||||||
@@ -1427,18 +1335,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
return `POINT($${l}, $${l + 1})`;
|
return `POINT($${l}, $${l + 1})`;
|
||||||
});
|
});
|
||||||
|
|
||||||
const columnsPattern = columnsArray
|
const columnsPattern = columnsArray.map((col, index) => `$${index + 2}:name`).join();
|
||||||
.map((col, index) => `$${index + 2}:name`)
|
|
||||||
.join();
|
|
||||||
const valuesPattern = initialValues.concat(geoPointsInjects).join();
|
const valuesPattern = initialValues.concat(geoPointsInjects).join();
|
||||||
|
|
||||||
const qs = `INSERT INTO $1:name (${columnsPattern}) VALUES (${valuesPattern})`;
|
const qs = `INSERT INTO $1:name (${columnsPattern}) VALUES (${valuesPattern})`;
|
||||||
const values = [className, ...columnsArray, ...valuesArray];
|
const values = [className, ...columnsArray, ...valuesArray];
|
||||||
debug(qs, values);
|
debug(qs, values);
|
||||||
const promise = (transactionalSession
|
const promise = (transactionalSession ? transactionalSession.t : this._client)
|
||||||
? transactionalSession.t
|
|
||||||
: this._client
|
|
||||||
)
|
|
||||||
.none(qs, values)
|
.none(qs, values)
|
||||||
.then(() => ({ ops: [object] }))
|
.then(() => ({ ops: [object] }))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -1488,17 +1391,11 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
}
|
}
|
||||||
const qs = `WITH deleted AS (DELETE FROM $1:name WHERE ${where.pattern} RETURNING *) SELECT count(*) FROM deleted`;
|
const qs = `WITH deleted AS (DELETE FROM $1:name WHERE ${where.pattern} RETURNING *) SELECT count(*) FROM deleted`;
|
||||||
debug(qs, values);
|
debug(qs, values);
|
||||||
const promise = (transactionalSession
|
const promise = (transactionalSession ? transactionalSession.t : this._client)
|
||||||
? transactionalSession.t
|
|
||||||
: this._client
|
|
||||||
)
|
|
||||||
.one(qs, values, a => +a.count)
|
.one(qs, values, a => +a.count)
|
||||||
.then(count => {
|
.then(count => {
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
'Object not found.'
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@@ -1523,13 +1420,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
transactionalSession: ?any
|
transactionalSession: ?any
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
debug('findOneAndUpdate', className, query, update);
|
debug('findOneAndUpdate', className, query, update);
|
||||||
return this.updateObjectsByQuery(
|
return this.updateObjectsByQuery(className, schema, query, update, transactionalSession).then(
|
||||||
className,
|
val => val[0]
|
||||||
schema,
|
);
|
||||||
query,
|
|
||||||
update,
|
|
||||||
transactionalSession
|
|
||||||
).then(val => val[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the update to all objects that match the given Parse Query.
|
// Apply the update to all objects that match the given Parse Query.
|
||||||
@@ -1592,39 +1485,28 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
const fieldNameIndex = index;
|
const fieldNameIndex = index;
|
||||||
index += 1;
|
index += 1;
|
||||||
values.push(fieldName);
|
values.push(fieldName);
|
||||||
const update = Object.keys(fieldValue).reduce(
|
const update = Object.keys(fieldValue).reduce((lastKey: string, key: string) => {
|
||||||
(lastKey: string, key: string) => {
|
const str = generate(lastKey, `$${index}::text`, `$${index + 1}::jsonb`);
|
||||||
const str = generate(
|
index += 2;
|
||||||
lastKey,
|
let value = fieldValue[key];
|
||||||
`$${index}::text`,
|
if (value) {
|
||||||
`$${index + 1}::jsonb`
|
if (value.__op === 'Delete') {
|
||||||
);
|
value = null;
|
||||||
index += 2;
|
} else {
|
||||||
let value = fieldValue[key];
|
value = JSON.stringify(value);
|
||||||
if (value) {
|
|
||||||
if (value.__op === 'Delete') {
|
|
||||||
value = null;
|
|
||||||
} else {
|
|
||||||
value = JSON.stringify(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
values.push(key, value);
|
}
|
||||||
return str;
|
values.push(key, value);
|
||||||
},
|
return str;
|
||||||
lastKey
|
}, lastKey);
|
||||||
);
|
|
||||||
updatePatterns.push(`$${fieldNameIndex}:name = ${update}`);
|
updatePatterns.push(`$${fieldNameIndex}:name = ${update}`);
|
||||||
} else if (fieldValue.__op === 'Increment') {
|
} else if (fieldValue.__op === 'Increment') {
|
||||||
updatePatterns.push(
|
updatePatterns.push(`$${index}:name = COALESCE($${index}:name, 0) + $${index + 1}`);
|
||||||
`$${index}:name = COALESCE($${index}:name, 0) + $${index + 1}`
|
|
||||||
);
|
|
||||||
values.push(fieldName, fieldValue.amount);
|
values.push(fieldName, fieldValue.amount);
|
||||||
index += 2;
|
index += 2;
|
||||||
} else if (fieldValue.__op === 'Add') {
|
} else if (fieldValue.__op === 'Add') {
|
||||||
updatePatterns.push(
|
updatePatterns.push(
|
||||||
`$${index}:name = array_add(COALESCE($${index}:name, '[]'::jsonb), $${
|
`$${index}:name = array_add(COALESCE($${index}:name, '[]'::jsonb), $${index + 1}::jsonb)`
|
||||||
index + 1
|
|
||||||
}::jsonb)`
|
|
||||||
);
|
);
|
||||||
values.push(fieldName, JSON.stringify(fieldValue.objects));
|
values.push(fieldName, JSON.stringify(fieldValue.objects));
|
||||||
index += 2;
|
index += 2;
|
||||||
@@ -1678,9 +1560,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
values.push(fieldName, toPostgresValue(fieldValue));
|
values.push(fieldName, toPostgresValue(fieldValue));
|
||||||
index += 2;
|
index += 2;
|
||||||
} else if (fieldValue.__type === 'GeoPoint') {
|
} else if (fieldValue.__type === 'GeoPoint') {
|
||||||
updatePatterns.push(
|
updatePatterns.push(`$${index}:name = POINT($${index + 1}, $${index + 2})`);
|
||||||
`$${index}:name = POINT($${index + 1}, $${index + 2})`
|
|
||||||
);
|
|
||||||
values.push(fieldName, fieldValue.longitude, fieldValue.latitude);
|
values.push(fieldName, fieldValue.longitude, fieldValue.latitude);
|
||||||
index += 3;
|
index += 3;
|
||||||
} else if (fieldValue.__type === 'Polygon') {
|
} else if (fieldValue.__type === 'Polygon') {
|
||||||
@@ -1745,12 +1625,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
})
|
})
|
||||||
.map(k => k.split('.')[1]);
|
.map(k => k.split('.')[1]);
|
||||||
|
|
||||||
const deletePatterns = keysToDelete.reduce(
|
const deletePatterns = keysToDelete.reduce((p: string, c: string, i: number) => {
|
||||||
(p: string, c: string, i: number) => {
|
return p + ` - '$${index + 1 + i}:value'`;
|
||||||
return p + ` - '$${index + 1 + i}:value'`;
|
}, '');
|
||||||
},
|
|
||||||
''
|
|
||||||
);
|
|
||||||
// Override Object
|
// Override Object
|
||||||
let updateObject = "'{}'::jsonb";
|
let updateObject = "'{}'::jsonb";
|
||||||
|
|
||||||
@@ -1799,14 +1676,10 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
});
|
});
|
||||||
values.push(...where.values);
|
values.push(...where.values);
|
||||||
|
|
||||||
const whereClause =
|
const whereClause = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||||
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
|
||||||
const qs = `UPDATE $1:name SET ${updatePatterns.join()} ${whereClause} RETURNING *`;
|
const qs = `UPDATE $1:name SET ${updatePatterns.join()} ${whereClause} RETURNING *`;
|
||||||
debug('update: ', qs, values);
|
debug('update: ', qs, values);
|
||||||
const promise = (transactionalSession
|
const promise = (transactionalSession ? transactionalSession.t : this._client).any(qs, values);
|
||||||
? transactionalSession.t
|
|
||||||
: this._client
|
|
||||||
).any(qs, values);
|
|
||||||
if (transactionalSession) {
|
if (transactionalSession) {
|
||||||
transactionalSession.batch.push(promise);
|
transactionalSession.batch.push(promise);
|
||||||
}
|
}
|
||||||
@@ -1823,23 +1696,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
) {
|
) {
|
||||||
debug('upsertOneObject', { className, query, update });
|
debug('upsertOneObject', { className, query, update });
|
||||||
const createValue = Object.assign({}, query, update);
|
const createValue = Object.assign({}, query, update);
|
||||||
return this.createObject(
|
return this.createObject(className, schema, createValue, transactionalSession).catch(error => {
|
||||||
className,
|
|
||||||
schema,
|
|
||||||
createValue,
|
|
||||||
transactionalSession
|
|
||||||
).catch(error => {
|
|
||||||
// ignore duplicate value errors as it's upsert
|
// ignore duplicate value errors as it's upsert
|
||||||
if (error.code !== Parse.Error.DUPLICATE_VALUE) {
|
if (error.code !== Parse.Error.DUPLICATE_VALUE) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
return this.findOneAndUpdate(
|
return this.findOneAndUpdate(className, schema, query, update, transactionalSession);
|
||||||
className,
|
|
||||||
schema,
|
|
||||||
query,
|
|
||||||
update,
|
|
||||||
transactionalSession
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1868,8 +1730,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
});
|
});
|
||||||
values.push(...where.values);
|
values.push(...where.values);
|
||||||
|
|
||||||
const wherePattern =
|
const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||||
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
|
||||||
const limitPattern = hasLimit ? `LIMIT $${values.length + 1}` : '';
|
const limitPattern = hasLimit ? `LIMIT $${values.length + 1}` : '';
|
||||||
if (hasLimit) {
|
if (hasLimit) {
|
||||||
values.push(limit);
|
values.push(limit);
|
||||||
@@ -1892,10 +1753,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
return `${transformKey} DESC`;
|
return `${transformKey} DESC`;
|
||||||
})
|
})
|
||||||
.join();
|
.join();
|
||||||
sortPattern =
|
sortPattern = sort !== undefined && Object.keys(sort).length > 0 ? `ORDER BY ${sorting}` : '';
|
||||||
sort !== undefined && Object.keys(sort).length > 0
|
|
||||||
? `ORDER BY ${sorting}`
|
|
||||||
: '';
|
|
||||||
}
|
}
|
||||||
if (where.sorts && Object.keys((where.sorts: any)).length > 0) {
|
if (where.sorts && Object.keys((where.sorts: any)).length > 0) {
|
||||||
sortPattern = `ORDER BY ${where.sorts.join()}`;
|
sortPattern = `ORDER BY ${where.sorts.join()}`;
|
||||||
@@ -1926,9 +1784,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const originalQuery = `SELECT ${columns} FROM $1:name ${wherePattern} ${sortPattern} ${limitPattern} ${skipPattern}`;
|
const originalQuery = `SELECT ${columns} FROM $1:name ${wherePattern} ${sortPattern} ${limitPattern} ${skipPattern}`;
|
||||||
const qs = explain
|
const qs = explain ? this.createExplainableQuery(originalQuery) : originalQuery;
|
||||||
? this.createExplainableQuery(originalQuery)
|
|
||||||
: originalQuery;
|
|
||||||
debug(qs, values);
|
debug(qs, values);
|
||||||
return this._client
|
return this._client
|
||||||
.any(qs, values)
|
.any(qs, values)
|
||||||
@@ -1943,9 +1799,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
if (explain) {
|
if (explain) {
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
return results.map(object =>
|
return results.map(object => this.postgresObjectToParseObject(className, object, schema));
|
||||||
this.postgresObjectToParseObject(className, object, schema)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1977,10 +1831,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
let coords = object[fieldName];
|
let coords = object[fieldName];
|
||||||
coords = coords.substr(2, coords.length - 4).split('),(');
|
coords = coords.substr(2, coords.length - 4).split('),(');
|
||||||
coords = coords.map(point => {
|
coords = coords.map(point => {
|
||||||
return [
|
return [parseFloat(point.split(',')[1]), parseFloat(point.split(',')[0])];
|
||||||
parseFloat(point.split(',')[1]),
|
|
||||||
parseFloat(point.split(',')[0]),
|
|
||||||
];
|
|
||||||
});
|
});
|
||||||
object[fieldName] = {
|
object[fieldName] = {
|
||||||
__type: 'Polygon',
|
__type: 'Polygon',
|
||||||
@@ -2052,37 +1903,26 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
// As such, we shouldn't expose this function to users of parse until we have an out-of-band
|
// As such, we shouldn't expose this function to users of parse until we have an out-of-band
|
||||||
// Way of determining if a field is nullable. Undefined doesn't count against uniqueness,
|
// Way of determining if a field is nullable. Undefined doesn't count against uniqueness,
|
||||||
// which is why we use sparse indexes.
|
// which is why we use sparse indexes.
|
||||||
async ensureUniqueness(
|
async ensureUniqueness(className: string, schema: SchemaType, fieldNames: string[]) {
|
||||||
className: string,
|
|
||||||
schema: SchemaType,
|
|
||||||
fieldNames: string[]
|
|
||||||
) {
|
|
||||||
const constraintName = `${className}_unique_${fieldNames.sort().join('_')}`;
|
const constraintName = `${className}_unique_${fieldNames.sort().join('_')}`;
|
||||||
const constraintPatterns = fieldNames.map(
|
const constraintPatterns = fieldNames.map((fieldName, index) => `$${index + 3}:name`);
|
||||||
(fieldName, index) => `$${index + 3}:name`
|
|
||||||
);
|
|
||||||
const qs = `CREATE UNIQUE INDEX IF NOT EXISTS $2:name ON $1:name(${constraintPatterns.join()})`;
|
const qs = `CREATE UNIQUE INDEX IF NOT EXISTS $2:name ON $1:name(${constraintPatterns.join()})`;
|
||||||
return this._client
|
return this._client.none(qs, [className, constraintName, ...fieldNames]).catch(error => {
|
||||||
.none(qs, [className, constraintName, ...fieldNames])
|
if (error.code === PostgresDuplicateRelationError && error.message.includes(constraintName)) {
|
||||||
.catch(error => {
|
// Index already exists. Ignore error.
|
||||||
if (
|
} else if (
|
||||||
error.code === PostgresDuplicateRelationError &&
|
error.code === PostgresUniqueIndexViolationError &&
|
||||||
error.message.includes(constraintName)
|
error.message.includes(constraintName)
|
||||||
) {
|
) {
|
||||||
// Index already exists. Ignore error.
|
// Cast the error into the proper parse error
|
||||||
} else if (
|
throw new Parse.Error(
|
||||||
error.code === PostgresUniqueIndexViolationError &&
|
Parse.Error.DUPLICATE_VALUE,
|
||||||
error.message.includes(constraintName)
|
'A duplicate value for a field with unique values was provided'
|
||||||
) {
|
);
|
||||||
// Cast the error into the proper parse error
|
} else {
|
||||||
throw new Parse.Error(
|
throw error;
|
||||||
Parse.Error.DUPLICATE_VALUE,
|
}
|
||||||
'A duplicate value for a field with unique values was provided'
|
});
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executes a count.
|
// Executes a count.
|
||||||
@@ -2103,15 +1943,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
});
|
});
|
||||||
values.push(...where.values);
|
values.push(...where.values);
|
||||||
|
|
||||||
const wherePattern =
|
const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||||
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
|
||||||
let qs = '';
|
let qs = '';
|
||||||
|
|
||||||
if (where.pattern.length > 0 || !estimate) {
|
if (where.pattern.length > 0 || !estimate) {
|
||||||
qs = `SELECT count(*) FROM $1:name ${wherePattern}`;
|
qs = `SELECT count(*) FROM $1:name ${wherePattern}`;
|
||||||
} else {
|
} else {
|
||||||
qs =
|
qs = 'SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = $1';
|
||||||
'SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = $1';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._client
|
return this._client
|
||||||
@@ -2130,12 +1968,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async distinct(
|
async distinct(className: string, schema: SchemaType, query: QueryType, fieldName: string) {
|
||||||
className: string,
|
|
||||||
schema: SchemaType,
|
|
||||||
query: QueryType,
|
|
||||||
fieldName: string
|
|
||||||
) {
|
|
||||||
debug('distinct', className, query);
|
debug('distinct', className, query);
|
||||||
let field = fieldName;
|
let field = fieldName;
|
||||||
let column = fieldName;
|
let column = fieldName;
|
||||||
@@ -2145,13 +1978,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
column = fieldName.split('.')[0];
|
column = fieldName.split('.')[0];
|
||||||
}
|
}
|
||||||
const isArrayField =
|
const isArrayField =
|
||||||
schema.fields &&
|
schema.fields && schema.fields[fieldName] && schema.fields[fieldName].type === 'Array';
|
||||||
schema.fields[fieldName] &&
|
|
||||||
schema.fields[fieldName].type === 'Array';
|
|
||||||
const isPointerField =
|
const isPointerField =
|
||||||
schema.fields &&
|
schema.fields && schema.fields[fieldName] && schema.fields[fieldName].type === 'Pointer';
|
||||||
schema.fields[fieldName] &&
|
|
||||||
schema.fields[fieldName].type === 'Pointer';
|
|
||||||
const values = [field, column, className];
|
const values = [field, column, className];
|
||||||
const where = buildWhereClause({
|
const where = buildWhereClause({
|
||||||
schema,
|
schema,
|
||||||
@@ -2161,8 +1990,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
});
|
});
|
||||||
values.push(...where.values);
|
values.push(...where.values);
|
||||||
|
|
||||||
const wherePattern =
|
const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||||
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
|
||||||
const transformer = isArrayField ? 'jsonb_array_elements' : 'ON';
|
const transformer = isArrayField ? 'jsonb_array_elements' : 'ON';
|
||||||
let qs = `SELECT DISTINCT ${transformer}($1:name) $2:name FROM $3:name ${wherePattern}`;
|
let qs = `SELECT DISTINCT ${transformer}($1:name) $2:name FROM $3:name ${wherePattern}`;
|
||||||
if (isNested) {
|
if (isNested) {
|
||||||
@@ -2195,9 +2023,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
return results.map(object => object[column][child]);
|
return results.map(object => object[column][child]);
|
||||||
})
|
})
|
||||||
.then(results =>
|
.then(results =>
|
||||||
results.map(object =>
|
results.map(object => this.postgresObjectToParseObject(className, object, schema))
|
||||||
this.postgresObjectToParseObject(className, object, schema)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2235,11 +2061,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
index += 1;
|
index += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (
|
if (field === '_id' && typeof value === 'object' && Object.keys(value).length !== 0) {
|
||||||
field === '_id' &&
|
|
||||||
typeof value === 'object' &&
|
|
||||||
Object.keys(value).length !== 0
|
|
||||||
) {
|
|
||||||
groupValues = value;
|
groupValues = value;
|
||||||
const groupByFields = [];
|
const groupByFields = [];
|
||||||
for (const alias in value) {
|
for (const alias in value) {
|
||||||
@@ -2261,9 +2083,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
columns.push(
|
columns.push(
|
||||||
`EXTRACT(${
|
`EXTRACT(${
|
||||||
mongoAggregateToPostgres[operation]
|
mongoAggregateToPostgres[operation]
|
||||||
} FROM $${index}:name AT TIME ZONE 'UTC') AS $${
|
} FROM $${index}:name AT TIME ZONE 'UTC') AS $${index + 1}:name`
|
||||||
index + 1
|
|
||||||
}:name`
|
|
||||||
);
|
);
|
||||||
values.push(source, alias);
|
values.push(source, alias);
|
||||||
index += 2;
|
index += 2;
|
||||||
@@ -2323,10 +2143,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
}
|
}
|
||||||
if (stage.$match) {
|
if (stage.$match) {
|
||||||
const patterns = [];
|
const patterns = [];
|
||||||
const orOrAnd = Object.prototype.hasOwnProperty.call(
|
const orOrAnd = Object.prototype.hasOwnProperty.call(stage.$match, '$or')
|
||||||
stage.$match,
|
|
||||||
'$or'
|
|
||||||
)
|
|
||||||
? ' OR '
|
? ' OR '
|
||||||
: ' AND ';
|
: ' AND ';
|
||||||
|
|
||||||
@@ -2345,9 +2162,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
Object.keys(ParseToPosgresComparator).forEach(cmp => {
|
Object.keys(ParseToPosgresComparator).forEach(cmp => {
|
||||||
if (value[cmp]) {
|
if (value[cmp]) {
|
||||||
const pgComparator = ParseToPosgresComparator[cmp];
|
const pgComparator = ParseToPosgresComparator[cmp];
|
||||||
matchPatterns.push(
|
matchPatterns.push(`$${index}:name ${pgComparator} $${index + 1}`);
|
||||||
`$${index}:name ${pgComparator} $${index + 1}`
|
|
||||||
);
|
|
||||||
values.push(field, toPostgresValue(value[cmp]));
|
values.push(field, toPostgresValue(value[cmp]));
|
||||||
index += 2;
|
index += 2;
|
||||||
}
|
}
|
||||||
@@ -2355,18 +2170,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
if (matchPatterns.length > 0) {
|
if (matchPatterns.length > 0) {
|
||||||
patterns.push(`(${matchPatterns.join(' AND ')})`);
|
patterns.push(`(${matchPatterns.join(' AND ')})`);
|
||||||
}
|
}
|
||||||
if (
|
if (schema.fields[field] && schema.fields[field].type && matchPatterns.length === 0) {
|
||||||
schema.fields[field] &&
|
|
||||||
schema.fields[field].type &&
|
|
||||||
matchPatterns.length === 0
|
|
||||||
) {
|
|
||||||
patterns.push(`$${index}:name = $${index + 1}`);
|
patterns.push(`$${index}:name = $${index + 1}`);
|
||||||
values.push(field, value);
|
values.push(field, value);
|
||||||
index += 2;
|
index += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wherePattern =
|
wherePattern = patterns.length > 0 ? `WHERE ${patterns.join(` ${orOrAnd} `)}` : '';
|
||||||
patterns.length > 0 ? `WHERE ${patterns.join(` ${orOrAnd} `)}` : '';
|
|
||||||
}
|
}
|
||||||
if (stage.$limit) {
|
if (stage.$limit) {
|
||||||
limitPattern = `LIMIT $${index}`;
|
limitPattern = `LIMIT $${index}`;
|
||||||
@@ -2390,8 +2200,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
})
|
})
|
||||||
.join();
|
.join();
|
||||||
values.push(...keys);
|
values.push(...keys);
|
||||||
sortPattern =
|
sortPattern = sort !== undefined && sorting.length > 0 ? `ORDER BY ${sorting}` : '';
|
||||||
sort !== undefined && sorting.length > 0 ? `ORDER BY ${sorting}` : '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2406,17 +2215,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
const originalQuery = `SELECT ${columns
|
const originalQuery = `SELECT ${columns
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join()} FROM $1:name ${wherePattern} ${skipPattern} ${groupPattern} ${sortPattern} ${limitPattern}`;
|
.join()} FROM $1:name ${wherePattern} ${skipPattern} ${groupPattern} ${sortPattern} ${limitPattern}`;
|
||||||
const qs = explain
|
const qs = explain ? this.createExplainableQuery(originalQuery) : originalQuery;
|
||||||
? this.createExplainableQuery(originalQuery)
|
|
||||||
: originalQuery;
|
|
||||||
debug(qs, values);
|
debug(qs, values);
|
||||||
return this._client.any(qs, values).then(a => {
|
return this._client.any(qs, values).then(a => {
|
||||||
if (explain) {
|
if (explain) {
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
const results = a.map(object =>
|
const results = a.map(object => this.postgresObjectToParseObject(className, object, schema));
|
||||||
this.postgresObjectToParseObject(className, object, schema)
|
|
||||||
);
|
|
||||||
results.forEach(result => {
|
results.forEach(result => {
|
||||||
if (!Object.prototype.hasOwnProperty.call(result, 'objectId')) {
|
if (!Object.prototype.hasOwnProperty.call(result, 'objectId')) {
|
||||||
result.objectId = null;
|
result.objectId = null;
|
||||||
@@ -2474,11 +2279,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async createIndexes(
|
async createIndexes(className: string, indexes: any, conn: ?any): Promise<void> {
|
||||||
className: string,
|
|
||||||
indexes: any,
|
|
||||||
conn: ?any
|
|
||||||
): Promise<void> {
|
|
||||||
return (conn || this._client).tx(t =>
|
return (conn || this._client).tx(t =>
|
||||||
t.batch(
|
t.batch(
|
||||||
indexes.map(i => {
|
indexes.map(i => {
|
||||||
@@ -2498,9 +2299,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
type: any,
|
type: any,
|
||||||
conn: ?any
|
conn: ?any
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await (
|
await (conn || this._client).none('CREATE INDEX IF NOT EXISTS $1:name ON $2:name ($3:name)', [
|
||||||
conn || this._client
|
|
||||||
).none('CREATE INDEX IF NOT EXISTS $1:name ON $2:name ($3:name)', [
|
|
||||||
fieldName,
|
fieldName,
|
||||||
className,
|
className,
|
||||||
type,
|
type,
|
||||||
@@ -2512,9 +2311,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
query: 'DROP INDEX $1:name',
|
query: 'DROP INDEX $1:name',
|
||||||
values: i,
|
values: i,
|
||||||
}));
|
}));
|
||||||
await (conn || this._client).tx(t =>
|
await (conn || this._client).tx(t => t.none(this._pgp.helpers.concat(queries)));
|
||||||
t.none(this._pgp.helpers.concat(queries))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getIndexes(className: string) {
|
async getIndexes(className: string) {
|
||||||
@@ -2547,18 +2344,14 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
commitTransactionalSession(transactionalSession: any): Promise<void> {
|
commitTransactionalSession(transactionalSession: any): Promise<void> {
|
||||||
transactionalSession.resolve(
|
transactionalSession.resolve(transactionalSession.t.batch(transactionalSession.batch));
|
||||||
transactionalSession.t.batch(transactionalSession.batch)
|
|
||||||
);
|
|
||||||
return transactionalSession.result;
|
return transactionalSession.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
abortTransactionalSession(transactionalSession: any): Promise<void> {
|
abortTransactionalSession(transactionalSession: any): Promise<void> {
|
||||||
const result = transactionalSession.result.catch();
|
const result = transactionalSession.result.catch();
|
||||||
transactionalSession.batch.push(Promise.reject());
|
transactionalSession.batch.push(Promise.reject());
|
||||||
transactionalSession.resolve(
|
transactionalSession.resolve(transactionalSession.t.batch(transactionalSession.batch));
|
||||||
transactionalSession.t.batch(transactionalSession.batch)
|
|
||||||
);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2575,41 +2368,34 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
const indexNameOptions: Object =
|
const indexNameOptions: Object =
|
||||||
indexName != null ? { name: indexName } : { name: defaultIndexName };
|
indexName != null ? { name: indexName } : { name: defaultIndexName };
|
||||||
const constraintPatterns = caseInsensitive
|
const constraintPatterns = caseInsensitive
|
||||||
? fieldNames.map(
|
? fieldNames.map((fieldName, index) => `lower($${index + 3}:name) varchar_pattern_ops`)
|
||||||
(fieldName, index) => `lower($${index + 3}:name) varchar_pattern_ops`
|
|
||||||
)
|
|
||||||
: fieldNames.map((fieldName, index) => `$${index + 3}:name`);
|
: fieldNames.map((fieldName, index) => `$${index + 3}:name`);
|
||||||
const qs = `CREATE INDEX IF NOT EXISTS $1:name ON $2:name (${constraintPatterns.join()})`;
|
const qs = `CREATE INDEX IF NOT EXISTS $1:name ON $2:name (${constraintPatterns.join()})`;
|
||||||
await conn
|
await conn.none(qs, [indexNameOptions.name, className, ...fieldNames]).catch(error => {
|
||||||
.none(qs, [indexNameOptions.name, className, ...fieldNames])
|
if (
|
||||||
.catch(error => {
|
error.code === PostgresDuplicateRelationError &&
|
||||||
if (
|
error.message.includes(indexNameOptions.name)
|
||||||
error.code === PostgresDuplicateRelationError &&
|
) {
|
||||||
error.message.includes(indexNameOptions.name)
|
// Index already exists. Ignore error.
|
||||||
) {
|
} else if (
|
||||||
// Index already exists. Ignore error.
|
error.code === PostgresUniqueIndexViolationError &&
|
||||||
} else if (
|
error.message.includes(indexNameOptions.name)
|
||||||
error.code === PostgresUniqueIndexViolationError &&
|
) {
|
||||||
error.message.includes(indexNameOptions.name)
|
// Cast the error into the proper parse error
|
||||||
) {
|
throw new Parse.Error(
|
||||||
// Cast the error into the proper parse error
|
Parse.Error.DUPLICATE_VALUE,
|
||||||
throw new Parse.Error(
|
'A duplicate value for a field with unique values was provided'
|
||||||
Parse.Error.DUPLICATE_VALUE,
|
);
|
||||||
'A duplicate value for a field with unique values was provided'
|
} else {
|
||||||
);
|
throw error;
|
||||||
} else {
|
}
|
||||||
throw error;
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertPolygonToSQL(polygon) {
|
function convertPolygonToSQL(polygon) {
|
||||||
if (polygon.length < 3) {
|
if (polygon.length < 3) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_JSON, `Polygon must have at least 3 values`);
|
||||||
Parse.Error.INVALID_JSON,
|
|
||||||
`Polygon must have at least 3 values`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
polygon[0][0] !== polygon[polygon.length - 1][0] ||
|
polygon[0][0] !== polygon[polygon.length - 1][0] ||
|
||||||
@@ -2757,9 +2543,7 @@ function literalizeRegexPart(s: string) {
|
|||||||
|
|
||||||
var GeoPointCoder = {
|
var GeoPointCoder = {
|
||||||
isValidJSON(value) {
|
isValidJSON(value) {
|
||||||
return (
|
return typeof value === 'object' && value !== null && value.__type === 'GeoPoint';
|
||||||
typeof value === 'object' && value !== null && value.__type === 'GeoPoint'
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -34,18 +34,10 @@ export interface StorageAdapter {
|
|||||||
classExists(className: string): Promise<boolean>;
|
classExists(className: string): Promise<boolean>;
|
||||||
setClassLevelPermissions(className: string, clps: any): Promise<void>;
|
setClassLevelPermissions(className: string, clps: any): Promise<void>;
|
||||||
createClass(className: string, schema: SchemaType): Promise<void>;
|
createClass(className: string, schema: SchemaType): Promise<void>;
|
||||||
addFieldIfNotExists(
|
addFieldIfNotExists(className: string, fieldName: string, type: any): Promise<void>;
|
||||||
className: string,
|
|
||||||
fieldName: string,
|
|
||||||
type: any
|
|
||||||
): Promise<void>;
|
|
||||||
deleteClass(className: string): Promise<void>;
|
deleteClass(className: string): Promise<void>;
|
||||||
deleteAllClasses(fast: boolean): Promise<void>;
|
deleteAllClasses(fast: boolean): Promise<void>;
|
||||||
deleteFields(
|
deleteFields(className: string, schema: SchemaType, fieldNames: Array<string>): Promise<void>;
|
||||||
className: string,
|
|
||||||
schema: SchemaType,
|
|
||||||
fieldNames: Array<string>
|
|
||||||
): Promise<void>;
|
|
||||||
getAllClasses(): Promise<StorageClass[]>;
|
getAllClasses(): Promise<StorageClass[]>;
|
||||||
getClass(className: string): Promise<StorageClass>;
|
getClass(className: string): Promise<StorageClass>;
|
||||||
createObject(
|
createObject(
|
||||||
@@ -95,11 +87,7 @@ export interface StorageAdapter {
|
|||||||
caseSensitive?: boolean,
|
caseSensitive?: boolean,
|
||||||
options?: Object
|
options?: Object
|
||||||
): Promise<any>;
|
): Promise<any>;
|
||||||
ensureUniqueness(
|
ensureUniqueness(className: string, schema: SchemaType, fieldNames: Array<string>): Promise<void>;
|
||||||
className: string,
|
|
||||||
schema: SchemaType,
|
|
||||||
fieldNames: Array<string>
|
|
||||||
): Promise<void>;
|
|
||||||
count(
|
count(
|
||||||
className: string,
|
className: string,
|
||||||
schema: SchemaType,
|
schema: SchemaType,
|
||||||
|
|||||||
@@ -5,40 +5,17 @@ const createObject = async (className, fields, config, auth, info) => {
|
|||||||
fields = {};
|
fields = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (await rest.create(config, auth, className, fields, info.clientSDK, info.context))
|
||||||
await rest.create(
|
.response;
|
||||||
config,
|
|
||||||
auth,
|
|
||||||
className,
|
|
||||||
fields,
|
|
||||||
info.clientSDK,
|
|
||||||
info.context
|
|
||||||
)
|
|
||||||
).response;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateObject = async (
|
const updateObject = async (className, objectId, fields, config, auth, info) => {
|
||||||
className,
|
|
||||||
objectId,
|
|
||||||
fields,
|
|
||||||
config,
|
|
||||||
auth,
|
|
||||||
info
|
|
||||||
) => {
|
|
||||||
if (!fields) {
|
if (!fields) {
|
||||||
fields = {};
|
fields = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
await rest.update(
|
await rest.update(config, auth, className, { objectId }, fields, info.clientSDK, info.context)
|
||||||
config,
|
|
||||||
auth,
|
|
||||||
className,
|
|
||||||
{ objectId },
|
|
||||||
fields,
|
|
||||||
info.clientSDK,
|
|
||||||
info.context
|
|
||||||
)
|
|
||||||
).response;
|
).response;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ const load = parseGraphQLSchema => {
|
|||||||
parseGraphQLSchema.addGraphQLQuery(
|
parseGraphQLSchema.addGraphQLQuery(
|
||||||
'health',
|
'health',
|
||||||
{
|
{
|
||||||
description:
|
description: 'The health query can be used to check if the server is up and running.',
|
||||||
'The health query can be used to check if the server is up and running.',
|
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
type: new GraphQLNonNull(GraphQLBoolean),
|
||||||
resolve: () => true,
|
resolve: () => true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class TypeValidationError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseStringValue = (value) => {
|
const parseStringValue = value => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ const parseStringValue = (value) => {
|
|||||||
throw new TypeValidationError(value, 'String');
|
throw new TypeValidationError(value, 'String');
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseIntValue = (value) => {
|
const parseIntValue = value => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
const int = Number(value);
|
const int = Number(value);
|
||||||
if (Number.isInteger(int)) {
|
if (Number.isInteger(int)) {
|
||||||
@@ -42,7 +42,7 @@ const parseIntValue = (value) => {
|
|||||||
throw new TypeValidationError(value, 'Int');
|
throw new TypeValidationError(value, 'Int');
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseFloatValue = (value) => {
|
const parseFloatValue = value => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
const float = Number(value);
|
const float = Number(value);
|
||||||
if (!isNaN(float)) {
|
if (!isNaN(float)) {
|
||||||
@@ -53,7 +53,7 @@ const parseFloatValue = (value) => {
|
|||||||
throw new TypeValidationError(value, 'Float');
|
throw new TypeValidationError(value, 'Float');
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseBooleanValue = (value) => {
|
const parseBooleanValue = value => {
|
||||||
if (typeof value === 'boolean') {
|
if (typeof value === 'boolean') {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ const parseBooleanValue = (value) => {
|
|||||||
throw new TypeValidationError(value, 'Boolean');
|
throw new TypeValidationError(value, 'Boolean');
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseValue = (value) => {
|
const parseValue = value => {
|
||||||
switch (value.kind) {
|
switch (value.kind) {
|
||||||
case Kind.STRING:
|
case Kind.STRING:
|
||||||
return parseStringValue(value.value);
|
return parseStringValue(value.value);
|
||||||
@@ -86,15 +86,15 @@ const parseValue = (value) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseListValues = (values) => {
|
const parseListValues = values => {
|
||||||
if (Array.isArray(values)) {
|
if (Array.isArray(values)) {
|
||||||
return values.map((value) => parseValue(value));
|
return values.map(value => parseValue(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new TypeValidationError(values, 'List');
|
throw new TypeValidationError(values, 'List');
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseObjectFields = (fields) => {
|
const parseObjectFields = fields => {
|
||||||
if (Array.isArray(fields)) {
|
if (Array.isArray(fields)) {
|
||||||
return fields.reduce(
|
return fields.reduce(
|
||||||
(object, field) => ({
|
(object, field) => ({
|
||||||
@@ -112,15 +112,14 @@ const ANY = new GraphQLScalarType({
|
|||||||
name: 'Any',
|
name: 'Any',
|
||||||
description:
|
description:
|
||||||
'The Any scalar type is used in operations and types that involve any type of value.',
|
'The Any scalar type is used in operations and types that involve any type of value.',
|
||||||
parseValue: (value) => value,
|
parseValue: value => value,
|
||||||
serialize: (value) => value,
|
serialize: value => value,
|
||||||
parseLiteral: (ast) => parseValue(ast),
|
parseLiteral: ast => parseValue(ast),
|
||||||
});
|
});
|
||||||
|
|
||||||
const OBJECT = new GraphQLScalarType({
|
const OBJECT = new GraphQLScalarType({
|
||||||
name: 'Object',
|
name: 'Object',
|
||||||
description:
|
description: 'The Object scalar type is used in operations and types that involve objects.',
|
||||||
'The Object scalar type is used in operations and types that involve objects.',
|
|
||||||
parseValue(value) {
|
parseValue(value) {
|
||||||
if (typeof value === 'object') {
|
if (typeof value === 'object') {
|
||||||
return value;
|
return value;
|
||||||
@@ -144,7 +143,7 @@ const OBJECT = new GraphQLScalarType({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const parseDateIsoValue = (value) => {
|
const parseDateIsoValue = value => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
const date = new Date(value);
|
const date = new Date(value);
|
||||||
if (!isNaN(date)) {
|
if (!isNaN(date)) {
|
||||||
@@ -157,7 +156,7 @@ const parseDateIsoValue = (value) => {
|
|||||||
throw new TypeValidationError(value, 'Date');
|
throw new TypeValidationError(value, 'Date');
|
||||||
};
|
};
|
||||||
|
|
||||||
const serializeDateIso = (value) => {
|
const serializeDateIso = value => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -168,7 +167,7 @@ const serializeDateIso = (value) => {
|
|||||||
throw new TypeValidationError(value, 'Date');
|
throw new TypeValidationError(value, 'Date');
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseDateIsoLiteral = (ast) => {
|
const parseDateIsoLiteral = ast => {
|
||||||
if (ast.kind === Kind.STRING) {
|
if (ast.kind === Kind.STRING) {
|
||||||
return parseDateIsoValue(ast.value);
|
return parseDateIsoValue(ast.value);
|
||||||
}
|
}
|
||||||
@@ -178,19 +177,14 @@ const parseDateIsoLiteral = (ast) => {
|
|||||||
|
|
||||||
const DATE = new GraphQLScalarType({
|
const DATE = new GraphQLScalarType({
|
||||||
name: 'Date',
|
name: 'Date',
|
||||||
description:
|
description: 'The Date scalar type is used in operations and types that involve dates.',
|
||||||
'The Date scalar type is used in operations and types that involve dates.',
|
|
||||||
parseValue(value) {
|
parseValue(value) {
|
||||||
if (typeof value === 'string' || value instanceof Date) {
|
if (typeof value === 'string' || value instanceof Date) {
|
||||||
return {
|
return {
|
||||||
__type: 'Date',
|
__type: 'Date',
|
||||||
iso: parseDateIsoValue(value),
|
iso: parseDateIsoValue(value),
|
||||||
};
|
};
|
||||||
} else if (
|
} else if (typeof value === 'object' && value.__type === 'Date' && value.iso) {
|
||||||
typeof value === 'object' &&
|
|
||||||
value.__type === 'Date' &&
|
|
||||||
value.iso
|
|
||||||
) {
|
|
||||||
return {
|
return {
|
||||||
__type: value.__type,
|
__type: value.__type,
|
||||||
iso: parseDateIsoValue(value.iso),
|
iso: parseDateIsoValue(value.iso),
|
||||||
@@ -202,11 +196,7 @@ const DATE = new GraphQLScalarType({
|
|||||||
serialize(value) {
|
serialize(value) {
|
||||||
if (typeof value === 'string' || value instanceof Date) {
|
if (typeof value === 'string' || value instanceof Date) {
|
||||||
return serializeDateIso(value);
|
return serializeDateIso(value);
|
||||||
} else if (
|
} else if (typeof value === 'object' && value.__type === 'Date' && value.iso) {
|
||||||
typeof value === 'object' &&
|
|
||||||
value.__type === 'Date' &&
|
|
||||||
value.iso
|
|
||||||
) {
|
|
||||||
return serializeDateIso(value.iso);
|
return serializeDateIso(value.iso);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,8 +209,8 @@ const DATE = new GraphQLScalarType({
|
|||||||
iso: parseDateIsoLiteral(ast),
|
iso: parseDateIsoLiteral(ast),
|
||||||
};
|
};
|
||||||
} else if (ast.kind === Kind.OBJECT) {
|
} else if (ast.kind === Kind.OBJECT) {
|
||||||
const __type = ast.fields.find((field) => field.name.value === '__type');
|
const __type = ast.fields.find(field => field.name.value === '__type');
|
||||||
const iso = ast.fields.find((field) => field.name.value === 'iso');
|
const iso = ast.fields.find(field => field.name.value === 'iso');
|
||||||
if (__type && __type.value && __type.value.value === 'Date' && iso) {
|
if (__type && __type.value && __type.value.value === 'Date' && iso) {
|
||||||
return {
|
return {
|
||||||
__type: __type.value.value,
|
__type: __type.value.value,
|
||||||
@@ -273,8 +263,8 @@ const BYTES = new GraphQLScalarType({
|
|||||||
base64: ast.value,
|
base64: ast.value,
|
||||||
};
|
};
|
||||||
} else if (ast.kind === Kind.OBJECT) {
|
} else if (ast.kind === Kind.OBJECT) {
|
||||||
const __type = ast.fields.find((field) => field.name.value === '__type');
|
const __type = ast.fields.find(field => field.name.value === '__type');
|
||||||
const base64 = ast.fields.find((field) => field.name.value === 'base64');
|
const base64 = ast.fields.find(field => field.name.value === 'base64');
|
||||||
if (
|
if (
|
||||||
__type &&
|
__type &&
|
||||||
__type.value &&
|
__type.value &&
|
||||||
@@ -294,7 +284,7 @@ const BYTES = new GraphQLScalarType({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const parseFileValue = (value) => {
|
const parseFileValue = value => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
return {
|
return {
|
||||||
__type: 'File',
|
__type: 'File',
|
||||||
@@ -314,10 +304,9 @@ const parseFileValue = (value) => {
|
|||||||
|
|
||||||
const FILE = new GraphQLScalarType({
|
const FILE = new GraphQLScalarType({
|
||||||
name: 'File',
|
name: 'File',
|
||||||
description:
|
description: 'The File scalar type is used in operations and types that involve files.',
|
||||||
'The File scalar type is used in operations and types that involve files.',
|
|
||||||
parseValue: parseFileValue,
|
parseValue: parseFileValue,
|
||||||
serialize: (value) => {
|
serialize: value => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
return value;
|
return value;
|
||||||
} else if (
|
} else if (
|
||||||
@@ -335,9 +324,9 @@ const FILE = new GraphQLScalarType({
|
|||||||
if (ast.kind === Kind.STRING) {
|
if (ast.kind === Kind.STRING) {
|
||||||
return parseFileValue(ast.value);
|
return parseFileValue(ast.value);
|
||||||
} else if (ast.kind === Kind.OBJECT) {
|
} else if (ast.kind === Kind.OBJECT) {
|
||||||
const __type = ast.fields.find((field) => field.name.value === '__type');
|
const __type = ast.fields.find(field => field.name.value === '__type');
|
||||||
const name = ast.fields.find((field) => field.name.value === 'name');
|
const name = ast.fields.find(field => field.name.value === 'name');
|
||||||
const url = ast.fields.find((field) => field.name.value === 'url');
|
const url = ast.fields.find(field => field.name.value === 'url');
|
||||||
if (__type && __type.value && name && name.value) {
|
if (__type && __type.value && name && name.value) {
|
||||||
return parseFileValue({
|
return parseFileValue({
|
||||||
__type: __type.value.value,
|
__type: __type.value.value,
|
||||||
@@ -353,8 +342,7 @@ const FILE = new GraphQLScalarType({
|
|||||||
|
|
||||||
const FILE_INFO = new GraphQLObjectType({
|
const FILE_INFO = new GraphQLObjectType({
|
||||||
name: 'FileInfo',
|
name: 'FileInfo',
|
||||||
description:
|
description: 'The FileInfo object type is used to return the information about files.',
|
||||||
'The FileInfo object type is used to return the information about files.',
|
|
||||||
fields: {
|
fields: {
|
||||||
name: {
|
name: {
|
||||||
description: 'This is the file name.',
|
description: 'This is the file name.',
|
||||||
@@ -407,8 +395,7 @@ const GEO_POINT_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const GEO_POINT = new GraphQLObjectType({
|
const GEO_POINT = new GraphQLObjectType({
|
||||||
name: 'GeoPoint',
|
name: 'GeoPoint',
|
||||||
description:
|
description: 'The GeoPoint object type is used to return the information about geo point fields.',
|
||||||
'The GeoPoint object type is used to return the information about geo point fields.',
|
|
||||||
fields: GEO_POINT_FIELDS,
|
fields: GEO_POINT_FIELDS,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -444,13 +431,11 @@ const ROLE_ACL_INPUT = new GraphQLInputObjectType({
|
|||||||
type: new GraphQLNonNull(GraphQLString),
|
type: new GraphQLNonNull(GraphQLString),
|
||||||
},
|
},
|
||||||
read: {
|
read: {
|
||||||
description:
|
description: 'Allow users who are members of the role to read the current object.',
|
||||||
'Allow users who are members of the role to read the current object.',
|
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
type: new GraphQLNonNull(GraphQLBoolean),
|
||||||
},
|
},
|
||||||
write: {
|
write: {
|
||||||
description:
|
description: 'Allow users who are members of the role to write on the current object.',
|
||||||
'Allow users who are members of the role to write on the current object.',
|
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
type: new GraphQLNonNull(GraphQLBoolean),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -521,13 +506,11 @@ const ROLE_ACL = new GraphQLObjectType({
|
|||||||
type: new GraphQLNonNull(GraphQLID),
|
type: new GraphQLNonNull(GraphQLID),
|
||||||
},
|
},
|
||||||
read: {
|
read: {
|
||||||
description:
|
description: 'Allow users who are members of the role to read the current object.',
|
||||||
'Allow users who are members of the role to read the current object.',
|
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
type: new GraphQLNonNull(GraphQLBoolean),
|
||||||
},
|
},
|
||||||
write: {
|
write: {
|
||||||
description:
|
description: 'Allow users who are members of the role to write on the current object.',
|
||||||
'Allow users who are members of the role to write on the current object.',
|
|
||||||
type: new GraphQLNonNull(GraphQLBoolean),
|
type: new GraphQLNonNull(GraphQLBoolean),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -557,7 +540,7 @@ const ACL = new GraphQLObjectType({
|
|||||||
type: new GraphQLList(new GraphQLNonNull(USER_ACL)),
|
type: new GraphQLList(new GraphQLNonNull(USER_ACL)),
|
||||||
resolve(p) {
|
resolve(p) {
|
||||||
const users = [];
|
const users = [];
|
||||||
Object.keys(p).forEach((rule) => {
|
Object.keys(p).forEach(rule => {
|
||||||
if (rule !== '*' && rule.indexOf('role:') !== 0) {
|
if (rule !== '*' && rule.indexOf('role:') !== 0) {
|
||||||
users.push({
|
users.push({
|
||||||
userId: toGlobalId('_User', rule),
|
userId: toGlobalId('_User', rule),
|
||||||
@@ -574,7 +557,7 @@ const ACL = new GraphQLObjectType({
|
|||||||
type: new GraphQLList(new GraphQLNonNull(ROLE_ACL)),
|
type: new GraphQLList(new GraphQLNonNull(ROLE_ACL)),
|
||||||
resolve(p) {
|
resolve(p) {
|
||||||
const roles = [];
|
const roles = [];
|
||||||
Object.keys(p).forEach((rule) => {
|
Object.keys(p).forEach(rule => {
|
||||||
if (rule.indexOf('role:') === 0) {
|
if (rule.indexOf('role:') === 0) {
|
||||||
roles.push({
|
roles.push({
|
||||||
roleName: rule.replace('role:', ''),
|
roleName: rule.replace('role:', ''),
|
||||||
@@ -610,8 +593,7 @@ const CLASS_NAME_ATT = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const GLOBAL_OR_OBJECT_ID_ATT = {
|
const GLOBAL_OR_OBJECT_ID_ATT = {
|
||||||
description:
|
description: 'This is the object id. You can use either the global or the object id.',
|
||||||
'This is the object id. You can use either the global or the object id.',
|
|
||||||
type: OBJECT_ID,
|
type: OBJECT_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -686,8 +668,7 @@ const READ_PREFERENCE_ATT = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const INCLUDE_READ_PREFERENCE_ATT = {
|
const INCLUDE_READ_PREFERENCE_ATT = {
|
||||||
description:
|
description: 'The read preference for the queries to be executed to include fields.',
|
||||||
'The read preference for the queries to be executed to include fields.',
|
|
||||||
type: READ_PREFERENCE,
|
type: READ_PREFERENCE,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -713,8 +694,7 @@ const READ_OPTIONS_ATT = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const WHERE_ATT = {
|
const WHERE_ATT = {
|
||||||
description:
|
description: 'These are the conditions that the objects need to match in order to be found',
|
||||||
'These are the conditions that the objects need to match in order to be found',
|
|
||||||
type: OBJECT,
|
type: OBJECT,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -736,8 +716,7 @@ const COUNT_ATT = {
|
|||||||
|
|
||||||
const SEARCH_INPUT = new GraphQLInputObjectType({
|
const SEARCH_INPUT = new GraphQLInputObjectType({
|
||||||
name: 'SearchInput',
|
name: 'SearchInput',
|
||||||
description:
|
description: 'The SearchInput type is used to specifiy a search operation on a full text search.',
|
||||||
'The SearchInput type is used to specifiy a search operation on a full text search.',
|
|
||||||
fields: {
|
fields: {
|
||||||
term: {
|
term: {
|
||||||
description: 'This is the term to be searched.',
|
description: 'This is the term to be searched.',
|
||||||
@@ -749,13 +728,11 @@ const SEARCH_INPUT = new GraphQLInputObjectType({
|
|||||||
type: GraphQLString,
|
type: GraphQLString,
|
||||||
},
|
},
|
||||||
caseSensitive: {
|
caseSensitive: {
|
||||||
description:
|
description: 'This is the flag to enable or disable case sensitive search.',
|
||||||
'This is the flag to enable or disable case sensitive search.',
|
|
||||||
type: GraphQLBoolean,
|
type: GraphQLBoolean,
|
||||||
},
|
},
|
||||||
diacriticSensitive: {
|
diacriticSensitive: {
|
||||||
description:
|
description: 'This is the flag to enable or disable diacritic sensitive search.',
|
||||||
'This is the flag to enable or disable diacritic sensitive search.',
|
|
||||||
type: GraphQLBoolean,
|
type: GraphQLBoolean,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -763,8 +740,7 @@ const SEARCH_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const TEXT_INPUT = new GraphQLInputObjectType({
|
const TEXT_INPUT = new GraphQLInputObjectType({
|
||||||
name: 'TextInput',
|
name: 'TextInput',
|
||||||
description:
|
description: 'The TextInput type is used to specify a text operation on a constraint.',
|
||||||
'The TextInput type is used to specify a text operation on a constraint.',
|
|
||||||
fields: {
|
fields: {
|
||||||
search: {
|
search: {
|
||||||
description: 'This is the search to be executed.',
|
description: 'This is the search to be executed.',
|
||||||
@@ -775,8 +751,7 @@ const TEXT_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const BOX_INPUT = new GraphQLInputObjectType({
|
const BOX_INPUT = new GraphQLInputObjectType({
|
||||||
name: 'BoxInput',
|
name: 'BoxInput',
|
||||||
description:
|
description: 'The BoxInput type is used to specifiy a box operation on a within geo query.',
|
||||||
'The BoxInput type is used to specifiy a box operation on a within geo query.',
|
|
||||||
fields: {
|
fields: {
|
||||||
bottomLeft: {
|
bottomLeft: {
|
||||||
description: 'This is the bottom left coordinates of the box.',
|
description: 'This is the bottom left coordinates of the box.',
|
||||||
@@ -791,8 +766,7 @@ const BOX_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const WITHIN_INPUT = new GraphQLInputObjectType({
|
const WITHIN_INPUT = new GraphQLInputObjectType({
|
||||||
name: 'WithinInput',
|
name: 'WithinInput',
|
||||||
description:
|
description: 'The WithinInput type is used to specify a within operation on a constraint.',
|
||||||
'The WithinInput type is used to specify a within operation on a constraint.',
|
|
||||||
fields: {
|
fields: {
|
||||||
box: {
|
box: {
|
||||||
description: 'This is the box to be specified.',
|
description: 'This is the box to be specified.',
|
||||||
@@ -819,8 +793,7 @@ const CENTER_SPHERE_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const GEO_WITHIN_INPUT = new GraphQLInputObjectType({
|
const GEO_WITHIN_INPUT = new GraphQLInputObjectType({
|
||||||
name: 'GeoWithinInput',
|
name: 'GeoWithinInput',
|
||||||
description:
|
description: 'The GeoWithinInput type is used to specify a geoWithin operation on a constraint.',
|
||||||
'The GeoWithinInput type is used to specify a geoWithin operation on a constraint.',
|
|
||||||
fields: {
|
fields: {
|
||||||
polygon: {
|
polygon: {
|
||||||
description: 'This is the polygon to be specified.',
|
description: 'This is the polygon to be specified.',
|
||||||
@@ -845,49 +818,49 @@ const GEO_INTERSECTS_INPUT = new GraphQLInputObjectType({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const equalTo = (type) => ({
|
const equalTo = type => ({
|
||||||
description:
|
description:
|
||||||
'This is the equalTo operator to specify a constraint to select the objects where the value of a field equals to a specified value.',
|
'This is the equalTo operator to specify a constraint to select the objects where the value of a field equals to a specified value.',
|
||||||
type,
|
type,
|
||||||
});
|
});
|
||||||
|
|
||||||
const notEqualTo = (type) => ({
|
const notEqualTo = type => ({
|
||||||
description:
|
description:
|
||||||
'This is the notEqualTo operator to specify a constraint to select the objects where the value of a field do not equal to a specified value.',
|
'This is the notEqualTo operator to specify a constraint to select the objects where the value of a field do not equal to a specified value.',
|
||||||
type,
|
type,
|
||||||
});
|
});
|
||||||
|
|
||||||
const lessThan = (type) => ({
|
const lessThan = type => ({
|
||||||
description:
|
description:
|
||||||
'This is the lessThan operator to specify a constraint to select the objects where the value of a field is less than a specified value.',
|
'This is the lessThan operator to specify a constraint to select the objects where the value of a field is less than a specified value.',
|
||||||
type,
|
type,
|
||||||
});
|
});
|
||||||
|
|
||||||
const lessThanOrEqualTo = (type) => ({
|
const lessThanOrEqualTo = type => ({
|
||||||
description:
|
description:
|
||||||
'This is the lessThanOrEqualTo operator to specify a constraint to select the objects where the value of a field is less than or equal to a specified value.',
|
'This is the lessThanOrEqualTo operator to specify a constraint to select the objects where the value of a field is less than or equal to a specified value.',
|
||||||
type,
|
type,
|
||||||
});
|
});
|
||||||
|
|
||||||
const greaterThan = (type) => ({
|
const greaterThan = type => ({
|
||||||
description:
|
description:
|
||||||
'This is the greaterThan operator to specify a constraint to select the objects where the value of a field is greater than a specified value.',
|
'This is the greaterThan operator to specify a constraint to select the objects where the value of a field is greater than a specified value.',
|
||||||
type,
|
type,
|
||||||
});
|
});
|
||||||
|
|
||||||
const greaterThanOrEqualTo = (type) => ({
|
const greaterThanOrEqualTo = type => ({
|
||||||
description:
|
description:
|
||||||
'This is the greaterThanOrEqualTo operator to specify a constraint to select the objects where the value of a field is greater than or equal to a specified value.',
|
'This is the greaterThanOrEqualTo operator to specify a constraint to select the objects where the value of a field is greater than or equal to a specified value.',
|
||||||
type,
|
type,
|
||||||
});
|
});
|
||||||
|
|
||||||
const inOp = (type) => ({
|
const inOp = type => ({
|
||||||
description:
|
description:
|
||||||
'This is the in operator to specify a constraint to select the objects where the value of a field equals any value in the specified array.',
|
'This is the in operator to specify a constraint to select the objects where the value of a field equals any value in the specified array.',
|
||||||
type: new GraphQLList(type),
|
type: new GraphQLList(type),
|
||||||
});
|
});
|
||||||
|
|
||||||
const notIn = (type) => ({
|
const notIn = type => ({
|
||||||
description:
|
description:
|
||||||
'This is the notIn operator to specify a constraint to select the objects where the value of a field do not equal any value in the specified array.',
|
'This is the notIn operator to specify a constraint to select the objects where the value of a field do not equal any value in the specified array.',
|
||||||
type: new GraphQLList(type),
|
type: new GraphQLList(type),
|
||||||
@@ -913,8 +886,7 @@ const options = {
|
|||||||
|
|
||||||
const SUBQUERY_INPUT = new GraphQLInputObjectType({
|
const SUBQUERY_INPUT = new GraphQLInputObjectType({
|
||||||
name: 'SubqueryInput',
|
name: 'SubqueryInput',
|
||||||
description:
|
description: 'The SubqueryInput type is used to specify a sub query to another class.',
|
||||||
'The SubqueryInput type is used to specify a sub query to another class.',
|
|
||||||
fields: {
|
fields: {
|
||||||
className: CLASS_NAME_ATT,
|
className: CLASS_NAME_ATT,
|
||||||
where: Object.assign({}, WHERE_ATT, {
|
where: Object.assign({}, WHERE_ATT, {
|
||||||
@@ -988,8 +960,7 @@ const STRING_WHERE_INPUT = new GraphQLInputObjectType({
|
|||||||
matchesRegex,
|
matchesRegex,
|
||||||
options,
|
options,
|
||||||
text: {
|
text: {
|
||||||
description:
|
description: 'This is the $text operator to specify a full text search constraint.',
|
||||||
'This is the $text operator to specify a full text search constraint.',
|
|
||||||
type: TEXT_INPUT,
|
type: TEXT_INPUT,
|
||||||
},
|
},
|
||||||
inQueryKey,
|
inQueryKey,
|
||||||
@@ -1225,27 +1196,21 @@ let ARRAY_RESULT;
|
|||||||
|
|
||||||
const loadArrayResult = (parseGraphQLSchema, parseClasses) => {
|
const loadArrayResult = (parseGraphQLSchema, parseClasses) => {
|
||||||
const classTypes = parseClasses
|
const classTypes = parseClasses
|
||||||
.filter((parseClass) =>
|
.filter(parseClass =>
|
||||||
parseGraphQLSchema.parseClassTypes[parseClass.className]
|
parseGraphQLSchema.parseClassTypes[parseClass.className].classGraphQLOutputType ? true : false
|
||||||
.classGraphQLOutputType
|
|
||||||
? true
|
|
||||||
: false
|
|
||||||
)
|
)
|
||||||
.map(
|
.map(
|
||||||
(parseClass) =>
|
parseClass => parseGraphQLSchema.parseClassTypes[parseClass.className].classGraphQLOutputType
|
||||||
parseGraphQLSchema.parseClassTypes[parseClass.className]
|
|
||||||
.classGraphQLOutputType
|
|
||||||
);
|
);
|
||||||
ARRAY_RESULT = new GraphQLUnionType({
|
ARRAY_RESULT = new GraphQLUnionType({
|
||||||
name: 'ArrayResult',
|
name: 'ArrayResult',
|
||||||
description:
|
description:
|
||||||
'Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments',
|
'Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments',
|
||||||
types: () => [ELEMENT, ...classTypes],
|
types: () => [ELEMENT, ...classTypes],
|
||||||
resolveType: (value) => {
|
resolveType: value => {
|
||||||
if (value.__type === 'Object' && value.className && value.objectId) {
|
if (value.__type === 'Object' && value.className && value.objectId) {
|
||||||
if (parseGraphQLSchema.parseClassTypes[value.className]) {
|
if (parseGraphQLSchema.parseClassTypes[value.className]) {
|
||||||
return parseGraphQLSchema.parseClassTypes[value.className]
|
return parseGraphQLSchema.parseClassTypes[value.className].classGraphQLOutputType;
|
||||||
.classGraphQLOutputType;
|
|
||||||
} else {
|
} else {
|
||||||
return ELEMENT;
|
return ELEMENT;
|
||||||
}
|
}
|
||||||
@@ -1257,7 +1222,7 @@ const loadArrayResult = (parseGraphQLSchema, parseClasses) => {
|
|||||||
parseGraphQLSchema.graphQLTypes.push(ARRAY_RESULT);
|
parseGraphQLSchema.graphQLTypes.push(ARRAY_RESULT);
|
||||||
};
|
};
|
||||||
|
|
||||||
const load = (parseGraphQLSchema) => {
|
const load = parseGraphQLSchema => {
|
||||||
parseGraphQLSchema.addGraphQLType(GraphQLUpload, true);
|
parseGraphQLSchema.addGraphQLType(GraphQLUpload, true);
|
||||||
parseGraphQLSchema.addGraphQLType(ANY, true);
|
parseGraphQLSchema.addGraphQLType(ANY, true);
|
||||||
parseGraphQLSchema.addGraphQLType(OBJECT, true);
|
parseGraphQLSchema.addGraphQLType(OBJECT, true);
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ const load = parseGraphQLSchema => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
obj => {
|
obj => {
|
||||||
return parseGraphQLSchema.parseClassTypes[obj.className]
|
return parseGraphQLSchema.parseClassTypes[obj.className].classGraphQLOutputType;
|
||||||
.classGraphQLOutputType;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const handleUpload = async (upload, config) => {
|
|||||||
const chunks = [];
|
const chunks = [];
|
||||||
stream
|
stream
|
||||||
.on('error', reject)
|
.on('error', reject)
|
||||||
.on('data', (chunk) => chunks.push(chunk))
|
.on('data', chunk => chunks.push(chunk))
|
||||||
.on('end', () => resolve(Buffer.concat(chunks)));
|
.on('end', () => resolve(Buffer.concat(chunks)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -28,35 +28,23 @@ const handleUpload = async (upload, config) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!filename.match(/^[_a-zA-Z0-9][a-zA-Z0-9@\.\ ~_-]*$/)) {
|
if (!filename.match(/^[_a-zA-Z0-9][a-zA-Z0-9@\.\ ~_-]*$/)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename contains invalid characters.');
|
||||||
Parse.Error.INVALID_FILE_NAME,
|
|
||||||
'Filename contains invalid characters.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
fileInfo: await config.filesController.createFile(
|
fileInfo: await config.filesController.createFile(config, filename, data, mimetype),
|
||||||
config,
|
|
||||||
filename,
|
|
||||||
data,
|
|
||||||
mimetype
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Error creating a file: ', e);
|
logger.error('Error creating a file: ', e);
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `Could not store file: ${filename}.`);
|
||||||
Parse.Error.FILE_SAVE_ERROR,
|
|
||||||
`Could not store file: ${filename}.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const load = (parseGraphQLSchema) => {
|
const load = parseGraphQLSchema => {
|
||||||
const createMutation = mutationWithClientMutationId({
|
const createMutation = mutationWithClientMutationId({
|
||||||
name: 'CreateFile',
|
name: 'CreateFile',
|
||||||
description:
|
description: 'The createFile mutation can be used to create and upload a new file.',
|
||||||
'The createFile mutation can be used to create and upload a new file.',
|
|
||||||
inputFields: {
|
inputFields: {
|
||||||
upload: {
|
upload: {
|
||||||
description: 'This is the new file to be created and uploaded.',
|
description: 'This is the new file to be created and uploaded.',
|
||||||
@@ -80,18 +68,9 @@ const load = (parseGraphQLSchema) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
parseGraphQLSchema.addGraphQLType(
|
parseGraphQLSchema.addGraphQLType(createMutation.args.input.type.ofType, true, true);
|
||||||
createMutation.args.input.type.ofType,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
parseGraphQLSchema.addGraphQLType(createMutation.type, true, true);
|
parseGraphQLSchema.addGraphQLType(createMutation.type, true, true);
|
||||||
parseGraphQLSchema.addGraphQLMutation(
|
parseGraphQLSchema.addGraphQLMutation('createFile', createMutation, true, true);
|
||||||
'createFile',
|
|
||||||
createMutation,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { load, handleUpload };
|
export { load, handleUpload };
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ const load = parseGraphQLSchema => {
|
|||||||
|
|
||||||
const callCloudCodeMutation = mutationWithClientMutationId({
|
const callCloudCodeMutation = mutationWithClientMutationId({
|
||||||
name: 'CallCloudCode',
|
name: 'CallCloudCode',
|
||||||
description:
|
description: 'The callCloudCode mutation can be used to invoke a cloud code function.',
|
||||||
'The callCloudCode mutation can be used to invoke a cloud code function.',
|
|
||||||
inputFields: {
|
inputFields: {
|
||||||
functionName: {
|
functionName: {
|
||||||
description: 'This is the function to be called.',
|
description: 'This is the function to be called.',
|
||||||
@@ -38,8 +37,7 @@ const load = parseGraphQLSchema => {
|
|||||||
},
|
},
|
||||||
outputFields: {
|
outputFields: {
|
||||||
result: {
|
result: {
|
||||||
description:
|
description: 'This is the result value of the cloud code function execution.',
|
||||||
'This is the result value of the cloud code function execution.',
|
|
||||||
type: defaultGraphQLTypes.ANY,
|
type: defaultGraphQLTypes.ANY,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -49,15 +47,17 @@ const load = parseGraphQLSchema => {
|
|||||||
const { config, auth, info } = context;
|
const { config, auth, info } = context;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result: (await FunctionsRouter.handleCloudFunction({
|
result: (
|
||||||
params: {
|
await FunctionsRouter.handleCloudFunction({
|
||||||
functionName,
|
params: {
|
||||||
},
|
functionName,
|
||||||
config,
|
},
|
||||||
auth,
|
config,
|
||||||
info,
|
auth,
|
||||||
body: params,
|
info,
|
||||||
})).response.result,
|
body: params,
|
||||||
|
})
|
||||||
|
).response.result,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
parseGraphQLSchema.handleError(e);
|
parseGraphQLSchema.handleError(e);
|
||||||
@@ -65,18 +65,9 @@ const load = parseGraphQLSchema => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
parseGraphQLSchema.addGraphQLType(
|
parseGraphQLSchema.addGraphQLType(callCloudCodeMutation.args.input.type.ofType, true, true);
|
||||||
callCloudCodeMutation.args.input.type.ofType,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
parseGraphQLSchema.addGraphQLType(callCloudCodeMutation.type, true, true);
|
parseGraphQLSchema.addGraphQLType(callCloudCodeMutation.type, true, true);
|
||||||
parseGraphQLSchema.addGraphQLMutation(
|
parseGraphQLSchema.addGraphQLMutation('callCloudCode', callCloudCodeMutation, true, true);
|
||||||
'callCloudCode',
|
|
||||||
callCloudCodeMutation,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,7 @@ import { GraphQLNonNull } from 'graphql';
|
|||||||
import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay';
|
import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay';
|
||||||
import getFieldNames from 'graphql-list-fields';
|
import getFieldNames from 'graphql-list-fields';
|
||||||
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
|
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
|
||||||
import {
|
import { extractKeysAndInclude, getParseClassMutationConfig } from '../parseGraphQLUtils';
|
||||||
extractKeysAndInclude,
|
|
||||||
getParseClassMutationConfig,
|
|
||||||
} from '../parseGraphQLUtils';
|
|
||||||
import * as objectsMutations from '../helpers/objectsMutations';
|
import * as objectsMutations from '../helpers/objectsMutations';
|
||||||
import * as objectsQueries from '../helpers/objectsQueries';
|
import * as objectsQueries from '../helpers/objectsQueries';
|
||||||
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
|
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
|
||||||
@@ -18,17 +15,10 @@ const getOnlyRequiredFields = (
|
|||||||
includedFieldsString,
|
includedFieldsString,
|
||||||
nativeObjectFields
|
nativeObjectFields
|
||||||
) => {
|
) => {
|
||||||
const includedFields = includedFieldsString
|
const includedFields = includedFieldsString ? includedFieldsString.split(',') : [];
|
||||||
? includedFieldsString.split(',')
|
const selectedFields = selectedFieldsString ? selectedFieldsString.split(',') : [];
|
||||||
: [];
|
|
||||||
const selectedFields = selectedFieldsString
|
|
||||||
? selectedFieldsString.split(',')
|
|
||||||
: [];
|
|
||||||
const missingFields = selectedFields
|
const missingFields = selectedFields
|
||||||
.filter(
|
.filter(field => !nativeObjectFields.includes(field) || includedFields.includes(field))
|
||||||
field =>
|
|
||||||
!nativeObjectFields.includes(field) || includedFields.includes(field)
|
|
||||||
)
|
|
||||||
.join(',');
|
.join(',');
|
||||||
if (!missingFields.length) {
|
if (!missingFields.length) {
|
||||||
return { needGet: false, keys: '' };
|
return { needGet: false, keys: '' };
|
||||||
@@ -37,15 +27,10 @@ const getOnlyRequiredFields = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const load = function(
|
const load = function (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseGraphQLClassConfig) {
|
||||||
parseGraphQLSchema,
|
|
||||||
parseClass,
|
|
||||||
parseClassConfig: ?ParseGraphQLClassConfig
|
|
||||||
) {
|
|
||||||
const className = parseClass.className;
|
const className = parseClass.className;
|
||||||
const graphQLClassName = transformClassNameToGraphQL(className);
|
const graphQLClassName = transformClassNameToGraphQL(className);
|
||||||
const getGraphQLQueryName =
|
const getGraphQLQueryName = graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
||||||
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
create: isCreateEnabled = true,
|
create: isCreateEnabled = true,
|
||||||
@@ -63,24 +48,20 @@ const load = function(
|
|||||||
} = parseGraphQLSchema.parseClassTypes[className];
|
} = parseGraphQLSchema.parseClassTypes[className];
|
||||||
|
|
||||||
if (isCreateEnabled) {
|
if (isCreateEnabled) {
|
||||||
const createGraphQLMutationName =
|
const createGraphQLMutationName = createAlias || `create${graphQLClassName}`;
|
||||||
createAlias || `create${graphQLClassName}`;
|
|
||||||
const createGraphQLMutation = mutationWithClientMutationId({
|
const createGraphQLMutation = mutationWithClientMutationId({
|
||||||
name: `Create${graphQLClassName}`,
|
name: `Create${graphQLClassName}`,
|
||||||
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${graphQLClassName} class.`,
|
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${graphQLClassName} class.`,
|
||||||
inputFields: {
|
inputFields: {
|
||||||
fields: {
|
fields: {
|
||||||
description:
|
description: 'These are the fields that will be used to create the new object.',
|
||||||
'These are the fields that will be used to create the new object.',
|
|
||||||
type: classGraphQLCreateType || defaultGraphQLTypes.OBJECT,
|
type: classGraphQLCreateType || defaultGraphQLTypes.OBJECT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
outputFields: {
|
outputFields: {
|
||||||
[getGraphQLQueryName]: {
|
[getGraphQLQueryName]: {
|
||||||
description: 'This is the created object.',
|
description: 'This is the created object.',
|
||||||
type: new GraphQLNonNull(
|
type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
|
||||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
||||||
@@ -106,12 +87,12 @@ const load = function(
|
|||||||
.filter(field => field.startsWith(`${getGraphQLQueryName}.`))
|
.filter(field => field.startsWith(`${getGraphQLQueryName}.`))
|
||||||
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
||||||
const { keys, include } = extractKeysAndInclude(selectedFields);
|
const { keys, include } = extractKeysAndInclude(selectedFields);
|
||||||
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(
|
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(fields, keys, include, [
|
||||||
fields,
|
'id',
|
||||||
keys,
|
'objectId',
|
||||||
include,
|
'createdAt',
|
||||||
['id', 'objectId', 'createdAt', 'updatedAt']
|
'updatedAt',
|
||||||
);
|
]);
|
||||||
const needToGetAllKeys = objectsQueries.needToGetAllKeys(
|
const needToGetAllKeys = objectsQueries.needToGetAllKeys(
|
||||||
parseClass.fields,
|
parseClass.fields,
|
||||||
keys,
|
keys,
|
||||||
@@ -160,38 +141,29 @@ const load = function(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
parseGraphQLSchema.addGraphQLType(
|
parseGraphQLSchema.addGraphQLType(createGraphQLMutation.args.input.type.ofType) &&
|
||||||
createGraphQLMutation.args.input.type.ofType
|
|
||||||
) &&
|
|
||||||
parseGraphQLSchema.addGraphQLType(createGraphQLMutation.type)
|
parseGraphQLSchema.addGraphQLType(createGraphQLMutation.type)
|
||||||
) {
|
) {
|
||||||
parseGraphQLSchema.addGraphQLMutation(
|
parseGraphQLSchema.addGraphQLMutation(createGraphQLMutationName, createGraphQLMutation);
|
||||||
createGraphQLMutationName,
|
|
||||||
createGraphQLMutation
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUpdateEnabled) {
|
if (isUpdateEnabled) {
|
||||||
const updateGraphQLMutationName =
|
const updateGraphQLMutationName = updateAlias || `update${graphQLClassName}`;
|
||||||
updateAlias || `update${graphQLClassName}`;
|
|
||||||
const updateGraphQLMutation = mutationWithClientMutationId({
|
const updateGraphQLMutation = mutationWithClientMutationId({
|
||||||
name: `Update${graphQLClassName}`,
|
name: `Update${graphQLClassName}`,
|
||||||
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`,
|
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`,
|
||||||
inputFields: {
|
inputFields: {
|
||||||
id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT,
|
id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT,
|
||||||
fields: {
|
fields: {
|
||||||
description:
|
description: 'These are the fields that will be used to update the object.',
|
||||||
'These are the fields that will be used to update the object.',
|
|
||||||
type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT,
|
type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
outputFields: {
|
outputFields: {
|
||||||
[getGraphQLQueryName]: {
|
[getGraphQLQueryName]: {
|
||||||
description: 'This is the updated object.',
|
description: 'This is the updated object.',
|
||||||
type: new GraphQLNonNull(
|
type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
|
||||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
||||||
@@ -225,12 +197,11 @@ const load = function(
|
|||||||
.filter(field => field.startsWith(`${getGraphQLQueryName}.`))
|
.filter(field => field.startsWith(`${getGraphQLQueryName}.`))
|
||||||
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
||||||
const { keys, include } = extractKeysAndInclude(selectedFields);
|
const { keys, include } = extractKeysAndInclude(selectedFields);
|
||||||
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(
|
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(fields, keys, include, [
|
||||||
fields,
|
'id',
|
||||||
keys,
|
'objectId',
|
||||||
include,
|
'updatedAt',
|
||||||
['id', 'objectId', 'updatedAt']
|
]);
|
||||||
);
|
|
||||||
const needToGetAllKeys = objectsQueries.needToGetAllKeys(
|
const needToGetAllKeys = objectsQueries.needToGetAllKeys(
|
||||||
parseClass.fields,
|
parseClass.fields,
|
||||||
keys,
|
keys,
|
||||||
@@ -279,21 +250,15 @@ const load = function(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
parseGraphQLSchema.addGraphQLType(
|
parseGraphQLSchema.addGraphQLType(updateGraphQLMutation.args.input.type.ofType) &&
|
||||||
updateGraphQLMutation.args.input.type.ofType
|
|
||||||
) &&
|
|
||||||
parseGraphQLSchema.addGraphQLType(updateGraphQLMutation.type)
|
parseGraphQLSchema.addGraphQLType(updateGraphQLMutation.type)
|
||||||
) {
|
) {
|
||||||
parseGraphQLSchema.addGraphQLMutation(
|
parseGraphQLSchema.addGraphQLMutation(updateGraphQLMutationName, updateGraphQLMutation);
|
||||||
updateGraphQLMutationName,
|
|
||||||
updateGraphQLMutation
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDestroyEnabled) {
|
if (isDestroyEnabled) {
|
||||||
const deleteGraphQLMutationName =
|
const deleteGraphQLMutationName = destroyAlias || `delete${graphQLClassName}`;
|
||||||
destroyAlias || `delete${graphQLClassName}`;
|
|
||||||
const deleteGraphQLMutation = mutationWithClientMutationId({
|
const deleteGraphQLMutation = mutationWithClientMutationId({
|
||||||
name: `Delete${graphQLClassName}`,
|
name: `Delete${graphQLClassName}`,
|
||||||
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`,
|
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`,
|
||||||
@@ -303,9 +268,7 @@ const load = function(
|
|||||||
outputFields: {
|
outputFields: {
|
||||||
[getGraphQLQueryName]: {
|
[getGraphQLQueryName]: {
|
||||||
description: 'This is the deleted object.',
|
description: 'This is the deleted object.',
|
||||||
type: new GraphQLNonNull(
|
type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
|
||||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
||||||
@@ -324,11 +287,7 @@ const load = function(
|
|||||||
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
||||||
const { keys, include } = extractKeysAndInclude(selectedFields);
|
const { keys, include } = extractKeysAndInclude(selectedFields);
|
||||||
let optimizedObject = {};
|
let optimizedObject = {};
|
||||||
if (
|
if (keys && keys.split(',').filter(key => !['id', 'objectId'].includes(key)).length > 0) {
|
||||||
keys &&
|
|
||||||
keys.split(',').filter(key => !['id', 'objectId'].includes(key))
|
|
||||||
.length > 0
|
|
||||||
) {
|
|
||||||
optimizedObject = await objectsQueries.getObject(
|
optimizedObject = await objectsQueries.getObject(
|
||||||
className,
|
className,
|
||||||
id,
|
id,
|
||||||
@@ -342,13 +301,7 @@ const load = function(
|
|||||||
parseGraphQLSchema.parseClasses
|
parseGraphQLSchema.parseClasses
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await objectsMutations.deleteObject(
|
await objectsMutations.deleteObject(className, id, config, auth, info);
|
||||||
className,
|
|
||||||
id,
|
|
||||||
config,
|
|
||||||
auth,
|
|
||||||
info
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
[getGraphQLQueryName]: {
|
[getGraphQLQueryName]: {
|
||||||
objectId: id,
|
objectId: id,
|
||||||
@@ -362,15 +315,10 @@ const load = function(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
parseGraphQLSchema.addGraphQLType(
|
parseGraphQLSchema.addGraphQLType(deleteGraphQLMutation.args.input.type.ofType) &&
|
||||||
deleteGraphQLMutation.args.input.type.ofType
|
|
||||||
) &&
|
|
||||||
parseGraphQLSchema.addGraphQLType(deleteGraphQLMutation.type)
|
parseGraphQLSchema.addGraphQLType(deleteGraphQLMutation.type)
|
||||||
) {
|
) {
|
||||||
parseGraphQLSchema.addGraphQLMutation(
|
parseGraphQLSchema.addGraphQLMutation(deleteGraphQLMutationName, deleteGraphQLMutation);
|
||||||
deleteGraphQLMutationName,
|
|
||||||
deleteGraphQLMutation
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,20 +8,11 @@ import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLControlle
|
|||||||
import { transformClassNameToGraphQL } from '../transformers/className';
|
import { transformClassNameToGraphQL } from '../transformers/className';
|
||||||
import { extractKeysAndInclude } from '../parseGraphQLUtils';
|
import { extractKeysAndInclude } from '../parseGraphQLUtils';
|
||||||
|
|
||||||
const getParseClassQueryConfig = function (
|
const getParseClassQueryConfig = function (parseClassConfig: ?ParseGraphQLClassConfig) {
|
||||||
parseClassConfig: ?ParseGraphQLClassConfig
|
|
||||||
) {
|
|
||||||
return (parseClassConfig && parseClassConfig.query) || {};
|
return (parseClassConfig && parseClassConfig.query) || {};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getQuery = async (
|
const getQuery = async (parseClass, _source, args, context, queryInfo, parseClasses) => {
|
||||||
parseClass,
|
|
||||||
_source,
|
|
||||||
args,
|
|
||||||
context,
|
|
||||||
queryInfo,
|
|
||||||
parseClasses
|
|
||||||
) => {
|
|
||||||
let { id } = args;
|
let { id } = args;
|
||||||
const { options } = args;
|
const { options } = args;
|
||||||
const { readPreference, includeReadPreference } = options || {};
|
const { readPreference, includeReadPreference } = options || {};
|
||||||
@@ -50,11 +41,7 @@ const getQuery = async (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const load = function (
|
const load = function (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseGraphQLClassConfig) {
|
||||||
parseGraphQLSchema,
|
|
||||||
parseClass,
|
|
||||||
parseClassConfig: ?ParseGraphQLClassConfig
|
|
||||||
) {
|
|
||||||
const className = parseClass.className;
|
const className = parseClass.className;
|
||||||
const graphQLClassName = transformClassNameToGraphQL(className);
|
const graphQLClassName = transformClassNameToGraphQL(className);
|
||||||
const {
|
const {
|
||||||
@@ -71,8 +58,7 @@ const load = function (
|
|||||||
} = parseGraphQLSchema.parseClassTypes[className];
|
} = parseGraphQLSchema.parseClassTypes[className];
|
||||||
|
|
||||||
if (isGetEnabled) {
|
if (isGetEnabled) {
|
||||||
const lowerCaseClassName =
|
const lowerCaseClassName = graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
||||||
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
|
||||||
|
|
||||||
const getGraphQLQueryName = getAlias || lowerCaseClassName;
|
const getGraphQLQueryName = getAlias || lowerCaseClassName;
|
||||||
|
|
||||||
@@ -82,9 +68,7 @@ const load = function (
|
|||||||
id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT,
|
id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT,
|
||||||
options: defaultGraphQLTypes.READ_OPTIONS_ATT,
|
options: defaultGraphQLTypes.READ_OPTIONS_ATT,
|
||||||
},
|
},
|
||||||
type: new GraphQLNonNull(
|
type: new GraphQLNonNull(classGraphQLOutputType || defaultGraphQLTypes.OBJECT),
|
||||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
|
||||||
),
|
|
||||||
async resolve(_source, args, context, queryInfo) {
|
async resolve(_source, args, context, queryInfo) {
|
||||||
try {
|
try {
|
||||||
return await getQuery(
|
return await getQuery(
|
||||||
@@ -103,41 +87,25 @@ const load = function (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isFindEnabled) {
|
if (isFindEnabled) {
|
||||||
const lowerCaseClassName =
|
const lowerCaseClassName = graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
||||||
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
|
||||||
|
|
||||||
const findGraphQLQueryName = findAlias || pluralize(lowerCaseClassName);
|
const findGraphQLQueryName = findAlias || pluralize(lowerCaseClassName);
|
||||||
|
|
||||||
parseGraphQLSchema.addGraphQLQuery(findGraphQLQueryName, {
|
parseGraphQLSchema.addGraphQLQuery(findGraphQLQueryName, {
|
||||||
description: `The ${findGraphQLQueryName} query can be used to find objects of the ${graphQLClassName} class.`,
|
description: `The ${findGraphQLQueryName} query can be used to find objects of the ${graphQLClassName} class.`,
|
||||||
args: classGraphQLFindArgs,
|
args: classGraphQLFindArgs,
|
||||||
type: new GraphQLNonNull(
|
type: new GraphQLNonNull(classGraphQLFindResultType || defaultGraphQLTypes.OBJECT),
|
||||||
classGraphQLFindResultType || defaultGraphQLTypes.OBJECT
|
|
||||||
),
|
|
||||||
async resolve(_source, args, context, queryInfo) {
|
async resolve(_source, args, context, queryInfo) {
|
||||||
try {
|
try {
|
||||||
const {
|
const { where, order, skip, first, after, last, before, options } = args;
|
||||||
where,
|
const { readPreference, includeReadPreference, subqueryReadPreference } = options || {};
|
||||||
order,
|
|
||||||
skip,
|
|
||||||
first,
|
|
||||||
after,
|
|
||||||
last,
|
|
||||||
before,
|
|
||||||
options,
|
|
||||||
} = args;
|
|
||||||
const {
|
|
||||||
readPreference,
|
|
||||||
includeReadPreference,
|
|
||||||
subqueryReadPreference,
|
|
||||||
} = options || {};
|
|
||||||
const { config, auth, info } = context;
|
const { config, auth, info } = context;
|
||||||
const selectedFields = getFieldNames(queryInfo);
|
const selectedFields = getFieldNames(queryInfo);
|
||||||
|
|
||||||
const { keys, include } = extractKeysAndInclude(
|
const { keys, include } = extractKeysAndInclude(
|
||||||
selectedFields
|
selectedFields
|
||||||
.filter((field) => field.startsWith('edges.node.'))
|
.filter(field => field.startsWith('edges.node.'))
|
||||||
.map((field) => field.replace('edges.node.', ''))
|
.map(field => field.replace('edges.node.', ''))
|
||||||
);
|
);
|
||||||
const parseOrder = order && order.join(',');
|
const parseOrder = order && order.join(',');
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,7 @@ import {
|
|||||||
GraphQLBoolean,
|
GraphQLBoolean,
|
||||||
GraphQLEnumType,
|
GraphQLEnumType,
|
||||||
} from 'graphql';
|
} from 'graphql';
|
||||||
import {
|
import { globalIdField, connectionArgs, connectionDefinitions } from 'graphql-relay';
|
||||||
globalIdField,
|
|
||||||
connectionArgs,
|
|
||||||
connectionDefinitions,
|
|
||||||
} from 'graphql-relay';
|
|
||||||
import getFieldNames from 'graphql-list-fields';
|
import getFieldNames from 'graphql-list-fields';
|
||||||
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
|
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
|
||||||
import * as objectsQueries from '../helpers/objectsQueries';
|
import * as objectsQueries from '../helpers/objectsQueries';
|
||||||
@@ -21,14 +17,9 @@ import { transformClassNameToGraphQL } from '../transformers/className';
|
|||||||
import { transformInputTypeToGraphQL } from '../transformers/inputType';
|
import { transformInputTypeToGraphQL } from '../transformers/inputType';
|
||||||
import { transformOutputTypeToGraphQL } from '../transformers/outputType';
|
import { transformOutputTypeToGraphQL } from '../transformers/outputType';
|
||||||
import { transformConstraintTypeToGraphQL } from '../transformers/constraintType';
|
import { transformConstraintTypeToGraphQL } from '../transformers/constraintType';
|
||||||
import {
|
import { extractKeysAndInclude, getParseClassMutationConfig } from '../parseGraphQLUtils';
|
||||||
extractKeysAndInclude,
|
|
||||||
getParseClassMutationConfig,
|
|
||||||
} from '../parseGraphQLUtils';
|
|
||||||
|
|
||||||
const getParseClassTypeConfig = function (
|
const getParseClassTypeConfig = function (parseClassConfig: ?ParseGraphQLClassConfig) {
|
||||||
parseClassConfig: ?ParseGraphQLClassConfig
|
|
||||||
) {
|
|
||||||
return (parseClassConfig && parseClassConfig.type) || {};
|
return (parseClassConfig && parseClassConfig.type) || {};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,22 +42,19 @@ const getInputFieldsAndConstraints = function (
|
|||||||
let classSortFields;
|
let classSortFields;
|
||||||
|
|
||||||
// All allowed customs fields
|
// All allowed customs fields
|
||||||
const classCustomFields = classFields.filter((field) => {
|
const classCustomFields = classFields.filter(field => {
|
||||||
return (
|
return !Object.keys(defaultGraphQLTypes.PARSE_OBJECT_FIELDS).includes(field) && field !== 'id';
|
||||||
!Object.keys(defaultGraphQLTypes.PARSE_OBJECT_FIELDS).includes(field) &&
|
|
||||||
field !== 'id'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (allowedInputFields && allowedInputFields.create) {
|
if (allowedInputFields && allowedInputFields.create) {
|
||||||
classCreateFields = classCustomFields.filter((field) => {
|
classCreateFields = classCustomFields.filter(field => {
|
||||||
return allowedInputFields.create.includes(field);
|
return allowedInputFields.create.includes(field);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
classCreateFields = classCustomFields;
|
classCreateFields = classCustomFields;
|
||||||
}
|
}
|
||||||
if (allowedInputFields && allowedInputFields.update) {
|
if (allowedInputFields && allowedInputFields.update) {
|
||||||
classUpdateFields = classCustomFields.filter((field) => {
|
classUpdateFields = classCustomFields.filter(field => {
|
||||||
return allowedInputFields.update.includes(field);
|
return allowedInputFields.update.includes(field);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -74,7 +62,7 @@ const getInputFieldsAndConstraints = function (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (allowedOutputFields) {
|
if (allowedOutputFields) {
|
||||||
classOutputFields = classCustomFields.filter((field) => {
|
classOutputFields = classCustomFields.filter(field => {
|
||||||
return allowedOutputFields.includes(field);
|
return allowedOutputFields.includes(field);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -82,13 +70,11 @@ const getInputFieldsAndConstraints = function (
|
|||||||
}
|
}
|
||||||
// Filters the "password" field from class _User
|
// Filters the "password" field from class _User
|
||||||
if (parseClass.className === '_User') {
|
if (parseClass.className === '_User') {
|
||||||
classOutputFields = classOutputFields.filter(
|
classOutputFields = classOutputFields.filter(outputField => outputField !== 'password');
|
||||||
(outputField) => outputField !== 'password'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowedConstraintFields) {
|
if (allowedConstraintFields) {
|
||||||
classConstraintFields = classCustomFields.filter((field) => {
|
classConstraintFields = classCustomFields.filter(field => {
|
||||||
return allowedConstraintFields.includes(field);
|
return allowedConstraintFields.includes(field);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -107,7 +93,7 @@ const getInputFieldsAndConstraints = function (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
classSortFields = classFields.map((field) => {
|
classSortFields = classFields.map(field => {
|
||||||
return { field, asc: true, desc: true };
|
return { field, asc: true, desc: true };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -121,11 +107,7 @@ const getInputFieldsAndConstraints = function (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const load = (
|
const load = (parseGraphQLSchema, parseClass, parseClassConfig: ?ParseGraphQLClassConfig) => {
|
||||||
parseGraphQLSchema,
|
|
||||||
parseClass,
|
|
||||||
parseClassConfig: ?ParseGraphQLClassConfig
|
|
||||||
) => {
|
|
||||||
const className = parseClass.className;
|
const className = parseClass.className;
|
||||||
const graphQLClassName = transformClassNameToGraphQL(className);
|
const graphQLClassName = transformClassNameToGraphQL(className);
|
||||||
const {
|
const {
|
||||||
@@ -159,8 +141,7 @@ const load = (
|
|||||||
[field]: {
|
[field]: {
|
||||||
description: `This is the object ${field}.`,
|
description: `This is the object ${field}.`,
|
||||||
type:
|
type:
|
||||||
(className === '_User' &&
|
(className === '_User' && (field === 'username' || field === 'password')) ||
|
||||||
(field === 'username' || field === 'password')) ||
|
|
||||||
parseClass.fields[field].required
|
parseClass.fields[field].required
|
||||||
? new GraphQLNonNull(type)
|
? new GraphQLNonNull(type)
|
||||||
: type,
|
: type,
|
||||||
@@ -175,9 +156,7 @@ const load = (
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
classGraphQLCreateType = parseGraphQLSchema.addGraphQLType(
|
classGraphQLCreateType = parseGraphQLSchema.addGraphQLType(classGraphQLCreateType);
|
||||||
classGraphQLCreateType
|
|
||||||
);
|
|
||||||
|
|
||||||
const classGraphQLUpdateTypeName = `Update${graphQLClassName}FieldsInput`;
|
const classGraphQLUpdateTypeName = `Update${graphQLClassName}FieldsInput`;
|
||||||
let classGraphQLUpdateType = new GraphQLInputObjectType({
|
let classGraphQLUpdateType = new GraphQLInputObjectType({
|
||||||
@@ -208,9 +187,7 @@ const load = (
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
classGraphQLUpdateType = parseGraphQLSchema.addGraphQLType(
|
classGraphQLUpdateType = parseGraphQLSchema.addGraphQLType(classGraphQLUpdateType);
|
||||||
classGraphQLUpdateType
|
|
||||||
);
|
|
||||||
|
|
||||||
const classGraphQLPointerTypeName = `${graphQLClassName}PointerInput`;
|
const classGraphQLPointerTypeName = `${graphQLClassName}PointerInput`;
|
||||||
let classGraphQLPointerType = new GraphQLInputObjectType({
|
let classGraphQLPointerType = new GraphQLInputObjectType({
|
||||||
@@ -233,8 +210,7 @@ const load = (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
classGraphQLPointerType =
|
classGraphQLPointerType =
|
||||||
parseGraphQLSchema.addGraphQLType(classGraphQLPointerType) ||
|
parseGraphQLSchema.addGraphQLType(classGraphQLPointerType) || defaultGraphQLTypes.OBJECT;
|
||||||
defaultGraphQLTypes.OBJECT;
|
|
||||||
|
|
||||||
const classGraphQLRelationTypeName = `${graphQLClassName}RelationInput`;
|
const classGraphQLRelationTypeName = `${graphQLClassName}RelationInput`;
|
||||||
let classGraphQLRelationType = new GraphQLInputObjectType({
|
let classGraphQLRelationType = new GraphQLInputObjectType({
|
||||||
@@ -261,8 +237,7 @@ const load = (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
classGraphQLRelationType =
|
classGraphQLRelationType =
|
||||||
parseGraphQLSchema.addGraphQLType(classGraphQLRelationType) ||
|
parseGraphQLSchema.addGraphQLType(classGraphQLRelationType) || defaultGraphQLTypes.OBJECT;
|
||||||
defaultGraphQLTypes.OBJECT;
|
|
||||||
|
|
||||||
const classGraphQLConstraintsTypeName = `${graphQLClassName}WhereInput`;
|
const classGraphQLConstraintsTypeName = `${graphQLClassName}WhereInput`;
|
||||||
let classGraphQLConstraintsType = new GraphQLInputObjectType({
|
let classGraphQLConstraintsType = new GraphQLInputObjectType({
|
||||||
@@ -310,8 +285,7 @@ const load = (
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
classGraphQLConstraintsType =
|
classGraphQLConstraintsType =
|
||||||
parseGraphQLSchema.addGraphQLType(classGraphQLConstraintsType) ||
|
parseGraphQLSchema.addGraphQLType(classGraphQLConstraintsType) || defaultGraphQLTypes.OBJECT;
|
||||||
defaultGraphQLTypes.OBJECT;
|
|
||||||
|
|
||||||
const classGraphQLRelationConstraintsTypeName = `${graphQLClassName}RelationWhereInput`;
|
const classGraphQLRelationConstraintsTypeName = `${graphQLClassName}RelationWhereInput`;
|
||||||
let classGraphQLRelationConstraintsType = new GraphQLInputObjectType({
|
let classGraphQLRelationConstraintsType = new GraphQLInputObjectType({
|
||||||
@@ -319,8 +293,7 @@ const load = (
|
|||||||
description: `The ${classGraphQLRelationConstraintsTypeName} input type is used in operations that involve filtering objects of ${graphQLClassName} class.`,
|
description: `The ${classGraphQLRelationConstraintsTypeName} input type is used in operations that involve filtering objects of ${graphQLClassName} class.`,
|
||||||
fields: () => ({
|
fields: () => ({
|
||||||
have: {
|
have: {
|
||||||
description:
|
description: 'Run a relational/pointer query where at least one child object can match.',
|
||||||
'Run a relational/pointer query where at least one child object can match.',
|
|
||||||
type: classGraphQLConstraintsType,
|
type: classGraphQLConstraintsType,
|
||||||
},
|
},
|
||||||
haveNot: {
|
haveNot: {
|
||||||
@@ -357,14 +330,11 @@ const load = (
|
|||||||
return updatedSortFields;
|
return updatedSortFields;
|
||||||
}, {}),
|
}, {}),
|
||||||
});
|
});
|
||||||
classGraphQLOrderType = parseGraphQLSchema.addGraphQLType(
|
classGraphQLOrderType = parseGraphQLSchema.addGraphQLType(classGraphQLOrderType);
|
||||||
classGraphQLOrderType
|
|
||||||
);
|
|
||||||
|
|
||||||
const classGraphQLFindArgs = {
|
const classGraphQLFindArgs = {
|
||||||
where: {
|
where: {
|
||||||
description:
|
description: 'These are the conditions that the objects need to match in order to be found.',
|
||||||
'These are the conditions that the objects need to match in order to be found.',
|
|
||||||
type: classGraphQLConstraintsType,
|
type: classGraphQLConstraintsType,
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
@@ -378,12 +348,9 @@ const load = (
|
|||||||
options: defaultGraphQLTypes.READ_OPTIONS_ATT,
|
options: defaultGraphQLTypes.READ_OPTIONS_ATT,
|
||||||
};
|
};
|
||||||
const classGraphQLOutputTypeName = `${graphQLClassName}`;
|
const classGraphQLOutputTypeName = `${graphQLClassName}`;
|
||||||
const interfaces = [
|
const interfaces = [defaultGraphQLTypes.PARSE_OBJECT, parseGraphQLSchema.relayNodeInterface];
|
||||||
defaultGraphQLTypes.PARSE_OBJECT,
|
|
||||||
parseGraphQLSchema.relayNodeInterface,
|
|
||||||
];
|
|
||||||
const parseObjectFields = {
|
const parseObjectFields = {
|
||||||
id: globalIdField(className, (obj) => obj.objectId),
|
id: globalIdField(className, obj => obj.objectId),
|
||||||
...defaultGraphQLTypes.PARSE_OBJECT_FIELDS,
|
...defaultGraphQLTypes.PARSE_OBJECT_FIELDS,
|
||||||
};
|
};
|
||||||
const outputFields = () => {
|
const outputFields = () => {
|
||||||
@@ -395,44 +362,26 @@ const load = (
|
|||||||
);
|
);
|
||||||
if (parseClass.fields[field].type === 'Relation') {
|
if (parseClass.fields[field].type === 'Relation') {
|
||||||
const targetParseClassTypes =
|
const targetParseClassTypes =
|
||||||
parseGraphQLSchema.parseClassTypes[
|
parseGraphQLSchema.parseClassTypes[parseClass.fields[field].targetClass];
|
||||||
parseClass.fields[field].targetClass
|
const args = targetParseClassTypes ? targetParseClassTypes.classGraphQLFindArgs : undefined;
|
||||||
];
|
|
||||||
const args = targetParseClassTypes
|
|
||||||
? targetParseClassTypes.classGraphQLFindArgs
|
|
||||||
: undefined;
|
|
||||||
return {
|
return {
|
||||||
...fields,
|
...fields,
|
||||||
[field]: {
|
[field]: {
|
||||||
description: `This is the object ${field}.`,
|
description: `This is the object ${field}.`,
|
||||||
args,
|
args,
|
||||||
type: parseClass.fields[field].required
|
type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
|
||||||
? new GraphQLNonNull(type)
|
|
||||||
: type,
|
|
||||||
async resolve(source, args, context, queryInfo) {
|
async resolve(source, args, context, queryInfo) {
|
||||||
try {
|
try {
|
||||||
const {
|
const { where, order, skip, first, after, last, before, options } = args;
|
||||||
where,
|
const { readPreference, includeReadPreference, subqueryReadPreference } =
|
||||||
order,
|
options || {};
|
||||||
skip,
|
|
||||||
first,
|
|
||||||
after,
|
|
||||||
last,
|
|
||||||
before,
|
|
||||||
options,
|
|
||||||
} = args;
|
|
||||||
const {
|
|
||||||
readPreference,
|
|
||||||
includeReadPreference,
|
|
||||||
subqueryReadPreference,
|
|
||||||
} = options || {};
|
|
||||||
const { config, auth, info } = context;
|
const { config, auth, info } = context;
|
||||||
const selectedFields = getFieldNames(queryInfo);
|
const selectedFields = getFieldNames(queryInfo);
|
||||||
|
|
||||||
const { keys, include } = extractKeysAndInclude(
|
const { keys, include } = extractKeysAndInclude(
|
||||||
selectedFields
|
selectedFields
|
||||||
.filter((field) => field.startsWith('edges.node.'))
|
.filter(field => field.startsWith('edges.node.'))
|
||||||
.map((field) => field.replace('edges.node.', ''))
|
.map(field => field.replace('edges.node.', ''))
|
||||||
);
|
);
|
||||||
const parseOrder = order && order.join(',');
|
const parseOrder = order && order.join(',');
|
||||||
|
|
||||||
@@ -478,12 +427,10 @@ const load = (
|
|||||||
...fields,
|
...fields,
|
||||||
[field]: {
|
[field]: {
|
||||||
description: `This is the object ${field}.`,
|
description: `This is the object ${field}.`,
|
||||||
type: parseClass.fields[field].required
|
type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
|
||||||
? new GraphQLNonNull(type)
|
|
||||||
: type,
|
|
||||||
async resolve(source) {
|
async resolve(source) {
|
||||||
if (source[field] && source[field].coordinates) {
|
if (source[field] && source[field].coordinates) {
|
||||||
return source[field].coordinates.map((coordinate) => ({
|
return source[field].coordinates.map(coordinate => ({
|
||||||
latitude: coordinate[0],
|
latitude: coordinate[0],
|
||||||
longitude: coordinate[1],
|
longitude: coordinate[1],
|
||||||
}));
|
}));
|
||||||
@@ -498,17 +445,11 @@ const load = (
|
|||||||
...fields,
|
...fields,
|
||||||
[field]: {
|
[field]: {
|
||||||
description: `Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments`,
|
description: `Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments`,
|
||||||
type: parseClass.fields[field].required
|
type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
|
||||||
? new GraphQLNonNull(type)
|
|
||||||
: type,
|
|
||||||
async resolve(source) {
|
async resolve(source) {
|
||||||
if (!source[field]) return null;
|
if (!source[field]) return null;
|
||||||
return source[field].map(async (elem) => {
|
return source[field].map(async elem => {
|
||||||
if (
|
if (elem.className && elem.objectId && elem.__type === 'Object') {
|
||||||
elem.className &&
|
|
||||||
elem.objectId &&
|
|
||||||
elem.__type === 'Object'
|
|
||||||
) {
|
|
||||||
return elem;
|
return elem;
|
||||||
} else {
|
} else {
|
||||||
return { value: elem };
|
return { value: elem };
|
||||||
@@ -522,9 +463,7 @@ const load = (
|
|||||||
...fields,
|
...fields,
|
||||||
[field]: {
|
[field]: {
|
||||||
description: `This is the object ${field}.`,
|
description: `This is the object ${field}.`,
|
||||||
type: parseClass.fields[field].required
|
type: parseClass.fields[field].required ? new GraphQLNonNull(type) : type,
|
||||||
? new GraphQLNonNull(type)
|
|
||||||
: type,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@@ -538,9 +477,7 @@ const load = (
|
|||||||
interfaces,
|
interfaces,
|
||||||
fields: outputFields,
|
fields: outputFields,
|
||||||
});
|
});
|
||||||
classGraphQLOutputType = parseGraphQLSchema.addGraphQLType(
|
classGraphQLOutputType = parseGraphQLSchema.addGraphQLType(classGraphQLOutputType);
|
||||||
classGraphQLOutputType
|
|
||||||
);
|
|
||||||
|
|
||||||
const { connectionType, edgeType } = connectionDefinitions({
|
const { connectionType, edgeType } = connectionDefinitions({
|
||||||
name: graphQLClassName,
|
name: graphQLClassName,
|
||||||
|
|||||||
@@ -21,15 +21,17 @@ const load = parseGraphQLSchema => {
|
|||||||
functionName = this.args.to;
|
functionName = this.args.to;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await FunctionsRouter.handleCloudFunction({
|
return (
|
||||||
params: {
|
await FunctionsRouter.handleCloudFunction({
|
||||||
functionName,
|
params: {
|
||||||
},
|
functionName,
|
||||||
config,
|
},
|
||||||
auth,
|
config,
|
||||||
info,
|
auth,
|
||||||
body: args,
|
info,
|
||||||
})).response.result;
|
body: args,
|
||||||
|
})
|
||||||
|
).response.result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
parseGraphQLSchema.handleError(e);
|
parseGraphQLSchema.handleError(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,7 @@ import Parse from 'parse/node';
|
|||||||
import { GraphQLNonNull } from 'graphql';
|
import { GraphQLNonNull } from 'graphql';
|
||||||
import { mutationWithClientMutationId } from 'graphql-relay';
|
import { mutationWithClientMutationId } from 'graphql-relay';
|
||||||
import * as schemaTypes from './schemaTypes';
|
import * as schemaTypes from './schemaTypes';
|
||||||
import {
|
import { transformToParse, transformToGraphQL } from '../transformers/schemaFields';
|
||||||
transformToParse,
|
|
||||||
transformToGraphQL,
|
|
||||||
} from '../transformers/schemaFields';
|
|
||||||
import { enforceMasterKeyAccess } from '../parseGraphQLUtils';
|
import { enforceMasterKeyAccess } from '../parseGraphQLUtils';
|
||||||
import { getClass } from './schemaQueries';
|
import { getClass } from './schemaQueries';
|
||||||
|
|
||||||
@@ -42,10 +39,7 @@ const load = parseGraphQLSchema => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const schema = await config.database.loadSchema({ clearCache: true });
|
const schema = await config.database.loadSchema({ clearCache: true });
|
||||||
const parseClass = await schema.addClassIfNotExists(
|
const parseClass = await schema.addClassIfNotExists(name, transformToParse(schemaFields));
|
||||||
name,
|
|
||||||
transformToParse(schemaFields)
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
class: {
|
class: {
|
||||||
name: parseClass.className,
|
name: parseClass.className,
|
||||||
@@ -58,18 +52,9 @@ const load = parseGraphQLSchema => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
parseGraphQLSchema.addGraphQLType(
|
parseGraphQLSchema.addGraphQLType(createClassMutation.args.input.type.ofType, true, true);
|
||||||
createClassMutation.args.input.type.ofType,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
parseGraphQLSchema.addGraphQLType(createClassMutation.type, true, true);
|
parseGraphQLSchema.addGraphQLType(createClassMutation.type, true, true);
|
||||||
parseGraphQLSchema.addGraphQLMutation(
|
parseGraphQLSchema.addGraphQLMutation('createClass', createClassMutation, true, true);
|
||||||
'createClass',
|
|
||||||
createClassMutation,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateClassMutation = mutationWithClientMutationId({
|
const updateClassMutation = mutationWithClientMutationId({
|
||||||
name: 'UpdateClass',
|
name: 'UpdateClass',
|
||||||
@@ -123,23 +108,13 @@ const load = parseGraphQLSchema => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
parseGraphQLSchema.addGraphQLType(
|
parseGraphQLSchema.addGraphQLType(updateClassMutation.args.input.type.ofType, true, true);
|
||||||
updateClassMutation.args.input.type.ofType,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
parseGraphQLSchema.addGraphQLType(updateClassMutation.type, true, true);
|
parseGraphQLSchema.addGraphQLType(updateClassMutation.type, true, true);
|
||||||
parseGraphQLSchema.addGraphQLMutation(
|
parseGraphQLSchema.addGraphQLMutation('updateClass', updateClassMutation, true, true);
|
||||||
'updateClass',
|
|
||||||
updateClassMutation,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
const deleteClassMutation = mutationWithClientMutationId({
|
const deleteClassMutation = mutationWithClientMutationId({
|
||||||
name: 'DeleteClass',
|
name: 'DeleteClass',
|
||||||
description:
|
description: 'The deleteClass mutation can be used to delete an existing object class.',
|
||||||
'The deleteClass mutation can be used to delete an existing object class.',
|
|
||||||
inputFields: {
|
inputFields: {
|
||||||
name: schemaTypes.CLASS_NAME_ATT,
|
name: schemaTypes.CLASS_NAME_ATT,
|
||||||
},
|
},
|
||||||
@@ -178,18 +153,9 @@ const load = parseGraphQLSchema => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
parseGraphQLSchema.addGraphQLType(
|
parseGraphQLSchema.addGraphQLType(deleteClassMutation.args.input.type.ofType, true, true);
|
||||||
deleteClassMutation.args.input.type.ofType,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
parseGraphQLSchema.addGraphQLType(deleteClassMutation.type, true, true);
|
parseGraphQLSchema.addGraphQLType(deleteClassMutation.type, true, true);
|
||||||
parseGraphQLSchema.addGraphQLMutation(
|
parseGraphQLSchema.addGraphQLMutation('deleteClass', deleteClassMutation, true, true);
|
||||||
'deleteClass',
|
|
||||||
deleteClassMutation,
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { load };
|
export { load };
|
||||||
|
|||||||
@@ -9,15 +9,9 @@ const getClass = async (name, schema) => {
|
|||||||
return await schema.getOneSchema(name, true);
|
return await schema.getOneSchema(name, true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e === undefined) {
|
if (e === undefined) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${name} does not exist.`);
|
||||||
Parse.Error.INVALID_CLASS_NAME,
|
|
||||||
`Class ${name} does not exist.`
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error.');
|
||||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
|
||||||
'Database adapter error.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -26,8 +20,7 @@ const load = parseGraphQLSchema => {
|
|||||||
parseGraphQLSchema.addGraphQLQuery(
|
parseGraphQLSchema.addGraphQLQuery(
|
||||||
'class',
|
'class',
|
||||||
{
|
{
|
||||||
description:
|
description: 'The class query can be used to retrieve an existing object class.',
|
||||||
'The class query can be used to retrieve an existing object class.',
|
|
||||||
args: {
|
args: {
|
||||||
name: schemaTypes.CLASS_NAME_ATT,
|
name: schemaTypes.CLASS_NAME_ATT,
|
||||||
},
|
},
|
||||||
@@ -57,11 +50,8 @@ const load = parseGraphQLSchema => {
|
|||||||
parseGraphQLSchema.addGraphQLQuery(
|
parseGraphQLSchema.addGraphQLQuery(
|
||||||
'classes',
|
'classes',
|
||||||
{
|
{
|
||||||
description:
|
description: 'The classes query can be used to retrieve the existing object classes.',
|
||||||
'The classes query can be used to retrieve the existing object classes.',
|
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(schemaTypes.CLASS))),
|
||||||
type: new GraphQLNonNull(
|
|
||||||
new GraphQLList(new GraphQLNonNull(schemaTypes.CLASS))
|
|
||||||
),
|
|
||||||
resolve: async (_source, _args, context) => {
|
resolve: async (_source, _args, context) => {
|
||||||
try {
|
try {
|
||||||
const { config, auth } = context;
|
const { config, auth } = context;
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ const SCHEMA_FIELD_NAME_ATT = {
|
|||||||
|
|
||||||
const SCHEMA_FIELD_INPUT = new GraphQLInputObjectType({
|
const SCHEMA_FIELD_INPUT = new GraphQLInputObjectType({
|
||||||
name: 'SchemaFieldInput',
|
name: 'SchemaFieldInput',
|
||||||
description:
|
description: 'The SchemaFieldInput is used to specify a field of an object class schema.',
|
||||||
'The SchemaFieldInput is used to specify a field of an object class schema.',
|
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
},
|
},
|
||||||
@@ -57,8 +56,7 @@ const SCHEMA_STRING_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_STRING_FIELD = new GraphQLObjectType({
|
const SCHEMA_STRING_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaStringField',
|
name: 'SchemaStringField',
|
||||||
description:
|
description: 'The SchemaStringField is used to return information of a String field.',
|
||||||
'The SchemaStringField is used to return information of a String field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -76,8 +74,7 @@ const SCHEMA_NUMBER_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_NUMBER_FIELD = new GraphQLObjectType({
|
const SCHEMA_NUMBER_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaNumberField',
|
name: 'SchemaNumberField',
|
||||||
description:
|
description: 'The SchemaNumberField is used to return information of a Number field.',
|
||||||
'The SchemaNumberField is used to return information of a Number field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -95,8 +92,7 @@ const SCHEMA_BOOLEAN_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_BOOLEAN_FIELD = new GraphQLObjectType({
|
const SCHEMA_BOOLEAN_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaBooleanField',
|
name: 'SchemaBooleanField',
|
||||||
description:
|
description: 'The SchemaBooleanField is used to return information of a Boolean field.',
|
||||||
'The SchemaBooleanField is used to return information of a Boolean field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -114,8 +110,7 @@ const SCHEMA_ARRAY_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_ARRAY_FIELD = new GraphQLObjectType({
|
const SCHEMA_ARRAY_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaArrayField',
|
name: 'SchemaArrayField',
|
||||||
description:
|
description: 'The SchemaArrayField is used to return information of an Array field.',
|
||||||
'The SchemaArrayField is used to return information of an Array field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -133,8 +128,7 @@ const SCHEMA_OBJECT_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_OBJECT_FIELD = new GraphQLObjectType({
|
const SCHEMA_OBJECT_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaObjectField',
|
name: 'SchemaObjectField',
|
||||||
description:
|
description: 'The SchemaObjectField is used to return information of an Object field.',
|
||||||
'The SchemaObjectField is used to return information of an Object field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -152,8 +146,7 @@ const SCHEMA_DATE_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_DATE_FIELD = new GraphQLObjectType({
|
const SCHEMA_DATE_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaDateField',
|
name: 'SchemaDateField',
|
||||||
description:
|
description: 'The SchemaDateField is used to return information of a Date field.',
|
||||||
'The SchemaDateField is used to return information of a Date field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -171,8 +164,7 @@ const SCHEMA_FILE_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_FILE_FIELD = new GraphQLObjectType({
|
const SCHEMA_FILE_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaFileField',
|
name: 'SchemaFileField',
|
||||||
description:
|
description: 'The SchemaFileField is used to return information of a File field.',
|
||||||
'The SchemaFileField is used to return information of a File field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -190,8 +182,7 @@ const SCHEMA_GEO_POINT_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_GEO_POINT_FIELD = new GraphQLObjectType({
|
const SCHEMA_GEO_POINT_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaGeoPointField',
|
name: 'SchemaGeoPointField',
|
||||||
description:
|
description: 'The SchemaGeoPointField is used to return information of a Geo Point field.',
|
||||||
'The SchemaGeoPointField is used to return information of a Geo Point field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -209,8 +200,7 @@ const SCHEMA_POLYGON_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_POLYGON_FIELD = new GraphQLObjectType({
|
const SCHEMA_POLYGON_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaPolygonField',
|
name: 'SchemaPolygonField',
|
||||||
description:
|
description: 'The SchemaPolygonField is used to return information of a Polygon field.',
|
||||||
'The SchemaPolygonField is used to return information of a Polygon field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -228,8 +218,7 @@ const SCHEMA_BYTES_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_BYTES_FIELD = new GraphQLObjectType({
|
const SCHEMA_BYTES_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaBytesField',
|
name: 'SchemaBytesField',
|
||||||
description:
|
description: 'The SchemaBytesField is used to return information of a Bytes field.',
|
||||||
'The SchemaBytesField is used to return information of a Bytes field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -253,8 +242,7 @@ const SCHEMA_POINTER_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_POINTER_FIELD = new GraphQLObjectType({
|
const SCHEMA_POINTER_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaPointerField',
|
name: 'SchemaPointerField',
|
||||||
description:
|
description: 'The SchemaPointerField is used to return information of a Pointer field.',
|
||||||
'The SchemaPointerField is used to return information of a Pointer field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -274,8 +262,7 @@ const SCHEMA_RELATION_FIELD_INPUT = new GraphQLInputObjectType({
|
|||||||
|
|
||||||
const SCHEMA_RELATION_FIELD = new GraphQLObjectType({
|
const SCHEMA_RELATION_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaRelationField',
|
name: 'SchemaRelationField',
|
||||||
description:
|
description: 'The SchemaRelationField is used to return information of a Relation field.',
|
||||||
'The SchemaRelationField is used to return information of a Relation field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -285,8 +272,7 @@ const SCHEMA_RELATION_FIELD = new GraphQLObjectType({
|
|||||||
|
|
||||||
const SCHEMA_ACL_FIELD = new GraphQLObjectType({
|
const SCHEMA_ACL_FIELD = new GraphQLObjectType({
|
||||||
name: 'SchemaACLField',
|
name: 'SchemaACLField',
|
||||||
description:
|
description: 'The SchemaACLField is used to return information of an ACL field.',
|
||||||
'The SchemaACLField is used to return information of an ACL field.',
|
|
||||||
interfaces: [SCHEMA_FIELD],
|
interfaces: [SCHEMA_FIELD],
|
||||||
fields: {
|
fields: {
|
||||||
name: SCHEMA_FIELD_NAME_ATT,
|
name: SCHEMA_FIELD_NAME_ATT,
|
||||||
@@ -298,28 +284,23 @@ const SCHEMA_FIELDS_INPUT = new GraphQLInputObjectType({
|
|||||||
description: `The CreateClassSchemaInput type is used to specify the schema for a new object class to be created.`,
|
description: `The CreateClassSchemaInput type is used to specify the schema for a new object class to be created.`,
|
||||||
fields: {
|
fields: {
|
||||||
addStrings: {
|
addStrings: {
|
||||||
description:
|
description: 'These are the String fields to be added to the class schema.',
|
||||||
'These are the String fields to be added to the class schema.',
|
|
||||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_STRING_FIELD_INPUT)),
|
type: new GraphQLList(new GraphQLNonNull(SCHEMA_STRING_FIELD_INPUT)),
|
||||||
},
|
},
|
||||||
addNumbers: {
|
addNumbers: {
|
||||||
description:
|
description: 'These are the Number fields to be added to the class schema.',
|
||||||
'These are the Number fields to be added to the class schema.',
|
|
||||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_NUMBER_FIELD_INPUT)),
|
type: new GraphQLList(new GraphQLNonNull(SCHEMA_NUMBER_FIELD_INPUT)),
|
||||||
},
|
},
|
||||||
addBooleans: {
|
addBooleans: {
|
||||||
description:
|
description: 'These are the Boolean fields to be added to the class schema.',
|
||||||
'These are the Boolean fields to be added to the class schema.',
|
|
||||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_BOOLEAN_FIELD_INPUT)),
|
type: new GraphQLList(new GraphQLNonNull(SCHEMA_BOOLEAN_FIELD_INPUT)),
|
||||||
},
|
},
|
||||||
addArrays: {
|
addArrays: {
|
||||||
description:
|
description: 'These are the Array fields to be added to the class schema.',
|
||||||
'These are the Array fields to be added to the class schema.',
|
|
||||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_ARRAY_FIELD_INPUT)),
|
type: new GraphQLList(new GraphQLNonNull(SCHEMA_ARRAY_FIELD_INPUT)),
|
||||||
},
|
},
|
||||||
addObjects: {
|
addObjects: {
|
||||||
description:
|
description: 'These are the Object fields to be added to the class schema.',
|
||||||
'These are the Object fields to be added to the class schema.',
|
|
||||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_OBJECT_FIELD_INPUT)),
|
type: new GraphQLList(new GraphQLNonNull(SCHEMA_OBJECT_FIELD_INPUT)),
|
||||||
},
|
},
|
||||||
addDates: {
|
addDates: {
|
||||||
@@ -336,23 +317,19 @@ const SCHEMA_FIELDS_INPUT = new GraphQLInputObjectType({
|
|||||||
type: SCHEMA_GEO_POINT_FIELD_INPUT,
|
type: SCHEMA_GEO_POINT_FIELD_INPUT,
|
||||||
},
|
},
|
||||||
addPolygons: {
|
addPolygons: {
|
||||||
description:
|
description: 'These are the Polygon fields to be added to the class schema.',
|
||||||
'These are the Polygon fields to be added to the class schema.',
|
|
||||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_POLYGON_FIELD_INPUT)),
|
type: new GraphQLList(new GraphQLNonNull(SCHEMA_POLYGON_FIELD_INPUT)),
|
||||||
},
|
},
|
||||||
addBytes: {
|
addBytes: {
|
||||||
description:
|
description: 'These are the Bytes fields to be added to the class schema.',
|
||||||
'These are the Bytes fields to be added to the class schema.',
|
|
||||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_BYTES_FIELD_INPUT)),
|
type: new GraphQLList(new GraphQLNonNull(SCHEMA_BYTES_FIELD_INPUT)),
|
||||||
},
|
},
|
||||||
addPointers: {
|
addPointers: {
|
||||||
description:
|
description: 'These are the Pointer fields to be added to the class schema.',
|
||||||
'These are the Pointer fields to be added to the class schema.',
|
|
||||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_POINTER_FIELD_INPUT)),
|
type: new GraphQLList(new GraphQLNonNull(SCHEMA_POINTER_FIELD_INPUT)),
|
||||||
},
|
},
|
||||||
addRelations: {
|
addRelations: {
|
||||||
description:
|
description: 'These are the Relation fields to be added to the class schema.',
|
||||||
'These are the Relation fields to be added to the class schema.',
|
|
||||||
type: new GraphQLList(new GraphQLNonNull(SCHEMA_RELATION_FIELD_INPUT)),
|
type: new GraphQLList(new GraphQLNonNull(SCHEMA_RELATION_FIELD_INPUT)),
|
||||||
},
|
},
|
||||||
remove: {
|
remove: {
|
||||||
@@ -374,9 +351,7 @@ const CLASS = new GraphQLObjectType({
|
|||||||
name: CLASS_NAME_ATT,
|
name: CLASS_NAME_ATT,
|
||||||
schemaFields: {
|
schemaFields: {
|
||||||
description: "These are the schema's fields of the object class.",
|
description: "These are the schema's fields of the object class.",
|
||||||
type: new GraphQLNonNull(
|
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(SCHEMA_FIELD))),
|
||||||
new GraphQLList(new GraphQLNonNull(SCHEMA_FIELD))
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,18 +5,10 @@ import rest from '../../rest';
|
|||||||
import { extractKeysAndInclude } from './parseClassTypes';
|
import { extractKeysAndInclude } from './parseClassTypes';
|
||||||
import { Auth } from '../../Auth';
|
import { Auth } from '../../Auth';
|
||||||
|
|
||||||
const getUserFromSessionToken = async (
|
const getUserFromSessionToken = async (context, queryInfo, keysPrefix, userId) => {
|
||||||
context,
|
|
||||||
queryInfo,
|
|
||||||
keysPrefix,
|
|
||||||
userId
|
|
||||||
) => {
|
|
||||||
const { info, config } = context;
|
const { info, config } = context;
|
||||||
if (!info || !info.sessionToken) {
|
if (!info || !info.sessionToken) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
|
||||||
Parse.Error.INVALID_SESSION_TOKEN,
|
|
||||||
'Invalid session token'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const sessionToken = info.sessionToken;
|
const sessionToken = info.sessionToken;
|
||||||
const selectedFields = getFieldNames(queryInfo)
|
const selectedFields = getFieldNames(queryInfo)
|
||||||
@@ -70,10 +62,7 @@ const getUserFromSessionToken = async (
|
|||||||
info.context
|
info.context
|
||||||
);
|
);
|
||||||
if (!response.results || response.results.length == 0) {
|
if (!response.results || response.results.length == 0) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
|
||||||
Parse.Error.INVALID_SESSION_TOKEN,
|
|
||||||
'Invalid session token'
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
const user = response.results[0];
|
const user = response.results[0];
|
||||||
return {
|
return {
|
||||||
@@ -91,17 +80,11 @@ const load = parseGraphQLSchema => {
|
|||||||
parseGraphQLSchema.addGraphQLQuery(
|
parseGraphQLSchema.addGraphQLQuery(
|
||||||
'viewer',
|
'viewer',
|
||||||
{
|
{
|
||||||
description:
|
description: 'The viewer query can be used to return the current user data.',
|
||||||
'The viewer query can be used to return the current user data.',
|
|
||||||
type: new GraphQLNonNull(parseGraphQLSchema.viewerType),
|
type: new GraphQLNonNull(parseGraphQLSchema.viewerType),
|
||||||
async resolve(_source, _args, context, queryInfo) {
|
async resolve(_source, _args, context, queryInfo) {
|
||||||
try {
|
try {
|
||||||
return await getUserFromSessionToken(
|
return await getUserFromSessionToken(context, queryInfo, 'user.', false);
|
||||||
context,
|
|
||||||
queryInfo,
|
|
||||||
'user.',
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
parseGraphQLSchema.handleError(e);
|
parseGraphQLSchema.handleError(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
|
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
|
||||||
|
|
||||||
const transformConstraintTypeToGraphQL = (
|
const transformConstraintTypeToGraphQL = (parseType, targetClass, parseClassTypes, fieldName) => {
|
||||||
parseType,
|
|
||||||
targetClass,
|
|
||||||
parseClassTypes,
|
|
||||||
fieldName
|
|
||||||
) => {
|
|
||||||
if (fieldName === 'id' || fieldName === 'objectId') {
|
if (fieldName === 'id' || fieldName === 'objectId') {
|
||||||
return defaultGraphQLTypes.ID_WHERE_INPUT;
|
return defaultGraphQLTypes.ID_WHERE_INPUT;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
import {
|
import { GraphQLString, GraphQLFloat, GraphQLBoolean, GraphQLList } from 'graphql';
|
||||||
GraphQLString,
|
|
||||||
GraphQLFloat,
|
|
||||||
GraphQLBoolean,
|
|
||||||
GraphQLList,
|
|
||||||
} from 'graphql';
|
|
||||||
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
|
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
|
||||||
|
|
||||||
const transformInputTypeToGraphQL = (
|
const transformInputTypeToGraphQL = (parseType, targetClass, parseClassTypes) => {
|
||||||
parseType,
|
|
||||||
targetClass,
|
|
||||||
parseClassTypes
|
|
||||||
) => {
|
|
||||||
switch (parseType) {
|
switch (parseType) {
|
||||||
case 'String':
|
case 'String':
|
||||||
return GraphQLString;
|
return GraphQLString;
|
||||||
|
|||||||
@@ -14,19 +14,13 @@ const transformTypes = async (
|
|||||||
classGraphQLUpdateType,
|
classGraphQLUpdateType,
|
||||||
config: { isCreateEnabled, isUpdateEnabled },
|
config: { isCreateEnabled, isUpdateEnabled },
|
||||||
} = parseGraphQLSchema.parseClassTypes[className];
|
} = parseGraphQLSchema.parseClassTypes[className];
|
||||||
const parseClass = parseGraphQLSchema.parseClasses.find(
|
const parseClass = parseGraphQLSchema.parseClasses.find(clazz => clazz.className === className);
|
||||||
(clazz) => clazz.className === className
|
|
||||||
);
|
|
||||||
if (fields) {
|
if (fields) {
|
||||||
const classGraphQLCreateTypeFields =
|
const classGraphQLCreateTypeFields =
|
||||||
isCreateEnabled && classGraphQLCreateType
|
isCreateEnabled && classGraphQLCreateType ? classGraphQLCreateType.getFields() : null;
|
||||||
? classGraphQLCreateType.getFields()
|
|
||||||
: null;
|
|
||||||
const classGraphQLUpdateTypeFields =
|
const classGraphQLUpdateTypeFields =
|
||||||
isUpdateEnabled && classGraphQLUpdateType
|
isUpdateEnabled && classGraphQLUpdateType ? classGraphQLUpdateType.getFields() : null;
|
||||||
? classGraphQLUpdateType.getFields()
|
const promises = Object.keys(fields).map(async field => {
|
||||||
: null;
|
|
||||||
const promises = Object.keys(fields).map(async (field) => {
|
|
||||||
let inputTypeField;
|
let inputTypeField;
|
||||||
if (inputType === 'create' && classGraphQLCreateTypeFields) {
|
if (inputType === 'create' && classGraphQLCreateTypeFields) {
|
||||||
inputTypeField = classGraphQLCreateTypeFields[field];
|
inputTypeField = classGraphQLCreateTypeFields[field];
|
||||||
@@ -84,18 +78,15 @@ const transformers = {
|
|||||||
}
|
}
|
||||||
throw new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.');
|
throw new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.');
|
||||||
},
|
},
|
||||||
polygon: (value) => ({
|
polygon: value => ({
|
||||||
__type: 'Polygon',
|
__type: 'Polygon',
|
||||||
coordinates: value.map((geoPoint) => [
|
coordinates: value.map(geoPoint => [geoPoint.latitude, geoPoint.longitude]),
|
||||||
geoPoint.latitude,
|
|
||||||
geoPoint.longitude,
|
|
||||||
]),
|
|
||||||
}),
|
}),
|
||||||
geoPoint: (value) => ({
|
geoPoint: value => ({
|
||||||
...value,
|
...value,
|
||||||
__type: 'GeoPoint',
|
__type: 'GeoPoint',
|
||||||
}),
|
}),
|
||||||
ACL: (value) => {
|
ACL: value => {
|
||||||
const parseACL = {};
|
const parseACL = {};
|
||||||
if (value.public) {
|
if (value.public) {
|
||||||
parseACL['*'] = {
|
parseACL['*'] = {
|
||||||
@@ -104,7 +95,7 @@ const transformers = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (value.users) {
|
if (value.users) {
|
||||||
value.users.forEach((rule) => {
|
value.users.forEach(rule => {
|
||||||
const globalIdObject = fromGlobalId(rule.userId);
|
const globalIdObject = fromGlobalId(rule.userId);
|
||||||
if (globalIdObject.type === '_User') {
|
if (globalIdObject.type === '_User') {
|
||||||
rule.userId = globalIdObject.id;
|
rule.userId = globalIdObject.id;
|
||||||
@@ -116,7 +107,7 @@ const transformers = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (value.roles) {
|
if (value.roles) {
|
||||||
value.roles.forEach((rule) => {
|
value.roles.forEach(rule => {
|
||||||
parseACL[`role:${rule.roleName}`] = {
|
parseACL[`role:${rule.roleName}`] = {
|
||||||
read: rule.read,
|
read: rule.read,
|
||||||
write: rule.write,
|
write: rule.write,
|
||||||
@@ -125,13 +116,7 @@ const transformers = {
|
|||||||
}
|
}
|
||||||
return parseACL;
|
return parseACL;
|
||||||
},
|
},
|
||||||
relation: async (
|
relation: async (targetClass, field, value, parseGraphQLSchema, { config, auth, info }) => {
|
||||||
targetClass,
|
|
||||||
field,
|
|
||||||
value,
|
|
||||||
parseGraphQLSchema,
|
|
||||||
{ config, auth, info }
|
|
||||||
) => {
|
|
||||||
if (Object.keys(value).length === 0)
|
if (Object.keys(value).length === 0)
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.INVALID_POINTER,
|
Parse.Error.INVALID_POINTER,
|
||||||
@@ -147,22 +132,16 @@ const transformers = {
|
|||||||
if (value.createAndAdd) {
|
if (value.createAndAdd) {
|
||||||
nestedObjectsToAdd = (
|
nestedObjectsToAdd = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
value.createAndAdd.map(async (input) => {
|
value.createAndAdd.map(async input => {
|
||||||
const parseFields = await transformTypes('create', input, {
|
const parseFields = await transformTypes('create', input, {
|
||||||
className: targetClass,
|
className: targetClass,
|
||||||
parseGraphQLSchema,
|
parseGraphQLSchema,
|
||||||
req: { config, auth, info },
|
req: { config, auth, info },
|
||||||
});
|
});
|
||||||
return objectsMutations.createObject(
|
return objectsMutations.createObject(targetClass, parseFields, config, auth, info);
|
||||||
targetClass,
|
|
||||||
parseFields,
|
|
||||||
config,
|
|
||||||
auth,
|
|
||||||
info
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
).map((object) => ({
|
).map(object => ({
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
className: targetClass,
|
className: targetClass,
|
||||||
objectId: object.objectId,
|
objectId: object.objectId,
|
||||||
@@ -171,7 +150,7 @@ const transformers = {
|
|||||||
|
|
||||||
if (value.add || nestedObjectsToAdd.length > 0) {
|
if (value.add || nestedObjectsToAdd.length > 0) {
|
||||||
if (!value.add) value.add = [];
|
if (!value.add) value.add = [];
|
||||||
value.add = value.add.map((input) => {
|
value.add = value.add.map(input => {
|
||||||
const globalIdObject = fromGlobalId(input);
|
const globalIdObject = fromGlobalId(input);
|
||||||
if (globalIdObject.type === targetClass) {
|
if (globalIdObject.type === targetClass) {
|
||||||
input = globalIdObject.id;
|
input = globalIdObject.id;
|
||||||
@@ -191,7 +170,7 @@ const transformers = {
|
|||||||
if (value.remove) {
|
if (value.remove) {
|
||||||
op.ops.push({
|
op.ops.push({
|
||||||
__op: 'RemoveRelation',
|
__op: 'RemoveRelation',
|
||||||
objects: value.remove.map((input) => {
|
objects: value.remove.map(input => {
|
||||||
const globalIdObject = fromGlobalId(input);
|
const globalIdObject = fromGlobalId(input);
|
||||||
if (globalIdObject.type === targetClass) {
|
if (globalIdObject.type === targetClass) {
|
||||||
input = globalIdObject.id;
|
input = globalIdObject.id;
|
||||||
@@ -206,13 +185,7 @@ const transformers = {
|
|||||||
}
|
}
|
||||||
return op;
|
return op;
|
||||||
},
|
},
|
||||||
pointer: async (
|
pointer: async (targetClass, field, value, parseGraphQLSchema, { config, auth, info }) => {
|
||||||
targetClass,
|
|
||||||
field,
|
|
||||||
value,
|
|
||||||
parseGraphQLSchema,
|
|
||||||
{ config, auth, info }
|
|
||||||
) => {
|
|
||||||
if (Object.keys(value).length > 1 || Object.keys(value).length === 0)
|
if (Object.keys(value).length > 1 || Object.keys(value).length === 0)
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.INVALID_POINTER,
|
Parse.Error.INVALID_POINTER,
|
||||||
|
|||||||
@@ -1,17 +1,7 @@
|
|||||||
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
|
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
|
||||||
import {
|
import { GraphQLString, GraphQLFloat, GraphQLBoolean, GraphQLList, GraphQLNonNull } from 'graphql';
|
||||||
GraphQLString,
|
|
||||||
GraphQLFloat,
|
|
||||||
GraphQLBoolean,
|
|
||||||
GraphQLList,
|
|
||||||
GraphQLNonNull,
|
|
||||||
} from 'graphql';
|
|
||||||
|
|
||||||
const transformOutputTypeToGraphQL = (
|
const transformOutputTypeToGraphQL = (parseType, targetClass, parseClassTypes) => {
|
||||||
parseType,
|
|
||||||
targetClass,
|
|
||||||
parseClassTypes
|
|
||||||
) => {
|
|
||||||
switch (parseType) {
|
switch (parseType) {
|
||||||
case 'String':
|
case 'String':
|
||||||
return GraphQLString;
|
return GraphQLString;
|
||||||
@@ -41,9 +31,7 @@ const transformOutputTypeToGraphQL = (
|
|||||||
parseClassTypes[targetClass] &&
|
parseClassTypes[targetClass] &&
|
||||||
parseClassTypes[targetClass].classGraphQLFindResultType
|
parseClassTypes[targetClass].classGraphQLFindResultType
|
||||||
) {
|
) {
|
||||||
return new GraphQLNonNull(
|
return new GraphQLNonNull(parseClassTypes[targetClass].classGraphQLFindResultType);
|
||||||
parseClassTypes[targetClass].classGraphQLFindResultType
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return new GraphQLNonNull(defaultGraphQLTypes.OBJECT);
|
return new GraphQLNonNull(defaultGraphQLTypes.OBJECT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,7 @@ const transformQueryConstraintInputToParse = (
|
|||||||
parentConstraints,
|
parentConstraints,
|
||||||
parseClasses
|
parseClasses
|
||||||
) => {
|
) => {
|
||||||
const fields = parseClasses.find(
|
const fields = parseClasses.find(parseClass => parseClass.className === className).fields;
|
||||||
parseClass => parseClass.className === className
|
|
||||||
).fields;
|
|
||||||
if (parentFieldName === 'id' && className) {
|
if (parentFieldName === 'id' && className) {
|
||||||
Object.keys(constraints).forEach(constraintName => {
|
Object.keys(constraints).forEach(constraintName => {
|
||||||
const constraintValue = constraints[constraintName];
|
const constraintValue = constraints[constraintName];
|
||||||
@@ -110,12 +108,7 @@ const transformQueryConstraintInputToParse = (
|
|||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
if (
|
if (fieldValue.key && fieldValue.value && parentConstraints && parentFieldName) {
|
||||||
fieldValue.key &&
|
|
||||||
fieldValue.value &&
|
|
||||||
parentConstraints &&
|
|
||||||
parentFieldName
|
|
||||||
) {
|
|
||||||
delete parentConstraints[parentFieldName];
|
delete parentConstraints[parentFieldName];
|
||||||
parentConstraints[`${parentFieldName}.${fieldValue.key}`] = {
|
parentConstraints[`${parentFieldName}.${fieldValue.key}`] = {
|
||||||
...parentConstraints[`${parentFieldName}.${fieldValue.key}`],
|
...parentConstraints[`${parentFieldName}.${fieldValue.key}`],
|
||||||
@@ -123,8 +116,7 @@ const transformQueryConstraintInputToParse = (
|
|||||||
};
|
};
|
||||||
} else if (
|
} else if (
|
||||||
fields[parentFieldName] &&
|
fields[parentFieldName] &&
|
||||||
(fields[parentFieldName].type === 'Pointer' ||
|
(fields[parentFieldName].type === 'Pointer' || fields[parentFieldName].type === 'Relation')
|
||||||
fields[parentFieldName].type === 'Relation')
|
|
||||||
) {
|
) {
|
||||||
const { targetClass } = fields[parentFieldName];
|
const { targetClass } = fields[parentFieldName];
|
||||||
if (fieldName === 'exists') {
|
if (fieldName === 'exists') {
|
||||||
@@ -193,11 +185,7 @@ const transformQueryConstraintInputToParse = (
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'box':
|
case 'box':
|
||||||
if (
|
if (typeof fieldValue === 'object' && fieldValue.bottomLeft && fieldValue.upperRight) {
|
||||||
typeof fieldValue === 'object' &&
|
|
||||||
fieldValue.bottomLeft &&
|
|
||||||
fieldValue.upperRight
|
|
||||||
) {
|
|
||||||
fieldValue = [
|
fieldValue = [
|
||||||
{
|
{
|
||||||
__type: 'GeoPoint',
|
__type: 'GeoPoint',
|
||||||
@@ -221,11 +209,7 @@ const transformQueryConstraintInputToParse = (
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'centerSphere':
|
case 'centerSphere':
|
||||||
if (
|
if (typeof fieldValue === 'object' && fieldValue.center && fieldValue.distance) {
|
||||||
typeof fieldValue === 'object' &&
|
|
||||||
fieldValue.center &&
|
|
||||||
fieldValue.distance
|
|
||||||
) {
|
|
||||||
fieldValue = [
|
fieldValue = [
|
||||||
{
|
{
|
||||||
__type: 'GeoPoint',
|
__type: 'GeoPoint',
|
||||||
|
|||||||
@@ -22,20 +22,12 @@ const transformToParse = (graphQLSchemaFields, existingFields) => {
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
graphQLSchemaFields.remove &&
|
graphQLSchemaFields.remove &&
|
||||||
graphQLSchemaFields.remove.find(
|
graphQLSchemaFields.remove.find(removeField => removeField.name === field.name)
|
||||||
removeField => removeField.name === field.name
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
return parseSchemaFields;
|
return parseSchemaFields;
|
||||||
}
|
}
|
||||||
if (
|
if (parseSchemaFields[field.name] || (existingFields && existingFields[field.name])) {
|
||||||
parseSchemaFields[field.name] ||
|
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Duplicated field name: ${field.name}`);
|
||||||
(existingFields && existingFields[field.name])
|
|
||||||
) {
|
|
||||||
throw new Parse.Error(
|
|
||||||
Parse.Error.INVALID_KEY_NAME,
|
|
||||||
`Duplicated field name: ${field.name}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (type === 'Relation' || type === 'Pointer') {
|
if (type === 'Relation' || type === 'Pointer') {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -127,7 +127,8 @@ module.exports.ParseServerOptions = {
|
|||||||
},
|
},
|
||||||
emailVerifyTokenReuseIfValid: {
|
emailVerifyTokenReuseIfValid: {
|
||||||
env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_REUSE_IF_VALID',
|
env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_REUSE_IF_VALID',
|
||||||
help: 'an existing email verify token should be reused when resend verification email is requested',
|
help:
|
||||||
|
'an existing email verify token should be reused when resend verification email is requested',
|
||||||
action: parsers.booleanParser,
|
action: parsers.booleanParser,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
const LiveQueryServerOptions = require('../../Options/Definitions')
|
const LiveQueryServerOptions = require('../../Options/Definitions').LiveQueryServerOptions;
|
||||||
.LiveQueryServerOptions;
|
|
||||||
export default LiveQueryServerOptions;
|
export default LiveQueryServerOptions;
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
const ParseServerDefinitions = require('../../Options/Definitions')
|
const ParseServerDefinitions = require('../../Options/Definitions').ParseServerOptions;
|
||||||
.ParseServerOptions;
|
|
||||||
export default ParseServerDefinitions;
|
export default ParseServerDefinitions;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ let _definitions;
|
|||||||
let _reverseDefinitions;
|
let _reverseDefinitions;
|
||||||
let _defaults;
|
let _defaults;
|
||||||
|
|
||||||
Command.prototype.loadDefinitions = function(definitions) {
|
Command.prototype.loadDefinitions = function (definitions) {
|
||||||
_definitions = definitions;
|
_definitions = definitions;
|
||||||
|
|
||||||
Object.keys(definitions).reduce((program, opt) => {
|
Object.keys(definitions).reduce((program, opt) => {
|
||||||
@@ -47,7 +47,7 @@ Command.prototype.loadDefinitions = function(definitions) {
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
this.on('--help', function() {
|
this.on('--help', function () {
|
||||||
console.log(' Configure From Environment:');
|
console.log(' Configure From Environment:');
|
||||||
console.log('');
|
console.log('');
|
||||||
Object.keys(_reverseDefinitions).forEach(key => {
|
Object.keys(_reverseDefinitions).forEach(key => {
|
||||||
@@ -100,7 +100,7 @@ function parseConfigFile(program) {
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
Command.prototype.setValuesIfNeeded = function(options) {
|
Command.prototype.setValuesIfNeeded = function (options) {
|
||||||
Object.keys(options).forEach(key => {
|
Object.keys(options).forEach(key => {
|
||||||
if (!Object.prototype.hasOwnProperty.call(this, key)) {
|
if (!Object.prototype.hasOwnProperty.call(this, key)) {
|
||||||
this[key] = options[key];
|
this[key] = options[key];
|
||||||
@@ -110,7 +110,7 @@ Command.prototype.setValuesIfNeeded = function(options) {
|
|||||||
|
|
||||||
Command.prototype._parse = Command.prototype.parse;
|
Command.prototype._parse = Command.prototype.parse;
|
||||||
|
|
||||||
Command.prototype.parse = function(args, env) {
|
Command.prototype.parse = function (args, env) {
|
||||||
this._parse(args);
|
this._parse(args);
|
||||||
// Parse the environment first
|
// Parse the environment first
|
||||||
const envOptions = parseEnvironment(env);
|
const envOptions = parseEnvironment(env);
|
||||||
@@ -123,7 +123,7 @@ Command.prototype.parse = function(args, env) {
|
|||||||
this.setValuesIfNeeded(_defaults);
|
this.setValuesIfNeeded(_defaults);
|
||||||
};
|
};
|
||||||
|
|
||||||
Command.prototype.getOptions = function() {
|
Command.prototype.getOptions = function () {
|
||||||
return Object.keys(_definitions).reduce((options, key) => {
|
return Object.keys(_definitions).reduce((options, key) => {
|
||||||
if (typeof this[key] !== 'undefined') {
|
if (typeof this[key] !== 'undefined') {
|
||||||
options[key] = this[key];
|
options[key] = this[key];
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function logStartupOptions(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function({ definitions, help, usage, start }) {
|
export default function ({ definitions, help, usage, start }) {
|
||||||
program.loadDefinitions(definitions);
|
program.loadDefinitions(definitions);
|
||||||
if (usage) {
|
if (usage) {
|
||||||
program.usage(usage);
|
program.usage(usage);
|
||||||
@@ -35,7 +35,7 @@ export default function({ definitions, help, usage, start }) {
|
|||||||
program.parse(process.argv, process.env);
|
program.parse(process.argv, process.env);
|
||||||
|
|
||||||
const options = program.getOptions();
|
const options = program.getOptions();
|
||||||
start(program, options, function() {
|
start(program, options, function () {
|
||||||
logStartupOptions(options);
|
logStartupOptions(options);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user