Adds liniting into the workflow (#3082)
* initial linting of src * fix indent to 2 spaces * Removes unnecessary rules * ignore spec folder for now * Spec linting * Fix spec indent * nits * nits * no no-empty rule
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
// This class handles the Account Lockout Policy settings.
|
||||
|
||||
import Config from './Config';
|
||||
import Parse from 'parse/node';
|
||||
|
||||
export class AccountLockout {
|
||||
constructor(user, config) {
|
||||
@@ -13,7 +12,7 @@ export class AccountLockout {
|
||||
*/
|
||||
_setFailedLoginCount(value) {
|
||||
let query = {
|
||||
username: this._user.username,
|
||||
username: this._user.username
|
||||
};
|
||||
|
||||
const updateFields = {
|
||||
@@ -76,7 +75,7 @@ export class AccountLockout {
|
||||
*/
|
||||
_incrementFailedLoginCount() {
|
||||
const query = {
|
||||
username: this._user.username,
|
||||
username: this._user.username
|
||||
};
|
||||
|
||||
const updateFields = {_failed_login_count: {__op: 'Increment', amount: 1}};
|
||||
@@ -93,7 +92,7 @@ export class AccountLockout {
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = {
|
||||
username: this._user.username,
|
||||
_failed_login_count: { $gte: this._config.accountLockout.threshold },
|
||||
_failed_login_count: { $gte: this._config.accountLockout.threshold }
|
||||
};
|
||||
|
||||
const now = new Date();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*eslint no-unused-vars: "off"*/
|
||||
export class AnalyticsAdapter {
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*eslint no-unused-vars: "off"*/
|
||||
export class CacheAdapter {
|
||||
/**
|
||||
* Get a value in the cache
|
||||
|
||||
@@ -7,7 +7,7 @@ export class InMemoryCacheAdapter {
|
||||
}
|
||||
|
||||
get(key) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
let record = this.cache.get(key);
|
||||
if (record == null) {
|
||||
return resolve(null);
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
export class NullCacheAdapter {
|
||||
|
||||
constructor(ctx) {
|
||||
}
|
||||
constructor() {}
|
||||
|
||||
get(key) {
|
||||
return new Promise((resolve, _) => {
|
||||
get() {
|
||||
return new Promise((resolve) => {
|
||||
return resolve(null);
|
||||
})
|
||||
}
|
||||
|
||||
put(key, value, ttl) {
|
||||
put() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
del(key) {
|
||||
del() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import logger from '../../logger';
|
||||
|
||||
const DEFAULT_REDIS_TTL = 30 * 1000; // 30 seconds in milliseconds
|
||||
|
||||
function debug() {
|
||||
function debug() {
|
||||
logger.debug.apply(logger, ['RedisCacheAdapter', ...arguments]);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ export class RedisCacheAdapter {
|
||||
|
||||
get(key) {
|
||||
debug('get', key);
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve, _) => {
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve) => {
|
||||
this.client.get(key, function(err, res) {
|
||||
debug('-> get', key, res);
|
||||
if(!res) {
|
||||
@@ -39,14 +39,14 @@ export class RedisCacheAdapter {
|
||||
if (ttl < 0 || isNaN(ttl)) {
|
||||
ttl = DEFAULT_REDIS_TTL;
|
||||
}
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve, _) => {
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve) => {
|
||||
if (ttl === Infinity) {
|
||||
this.client.set(key, value, function(err, res) {
|
||||
this.client.set(key, value, function() {
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
this.client.psetex(key, ttl, value, function(err, res) {
|
||||
this.client.psetex(key, ttl, value, function() {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
@@ -57,9 +57,9 @@ export class RedisCacheAdapter {
|
||||
|
||||
del(key) {
|
||||
debug('del', key);
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve, _) => {
|
||||
this.client.del(key, function(err, res) {
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve) => {
|
||||
this.client.del(key, function() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
@@ -69,9 +69,9 @@ export class RedisCacheAdapter {
|
||||
|
||||
clear() {
|
||||
debug('clear');
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve, _) => {
|
||||
this.client.flushall(function(err, res) {
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve) => {
|
||||
this.client.flushall(function() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
/*eslint no-unused-vars: "off"*/
|
||||
/*
|
||||
Mail Adapter prototype
|
||||
A MailAdapter should implement at least sendMail()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*eslint no-unused-vars: "off"*/
|
||||
// Files Adapter
|
||||
//
|
||||
// Allows you to change the file storage mechanism.
|
||||
|
||||
@@ -28,7 +28,7 @@ export class GridStoreAdapter extends FilesAdapter {
|
||||
|
||||
// For a given config object, filename, and data, store a file
|
||||
// Returns a promise
|
||||
createFile(filename: string, data, contentType) {
|
||||
createFile(filename: string, data) {
|
||||
return this._connect().then(database => {
|
||||
let gridStore = new GridStore(database, filename, 'w');
|
||||
return gridStore.open();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*eslint no-unused-vars: "off"*/
|
||||
// Logger Adapter
|
||||
//
|
||||
// Allows you to change the logger mechanism
|
||||
|
||||
@@ -37,7 +37,7 @@ function updateTransports(options) {
|
||||
}, options));
|
||||
}
|
||||
// Mount the additional transports
|
||||
additionalTransports.forEach((transport) => {
|
||||
additionalTransports.forEach((transport) => {
|
||||
transports[transport.name] = transport;
|
||||
});
|
||||
logger.configure({
|
||||
@@ -65,7 +65,7 @@ export function configureLogger({
|
||||
}
|
||||
try {
|
||||
fs.mkdirSync(logsFolder);
|
||||
} catch (exception) {}
|
||||
} catch (e) { /* */ }
|
||||
}
|
||||
options.dirname = logsFolder;
|
||||
options.level = logLevel;
|
||||
@@ -90,7 +90,7 @@ export function removeTransport(transport) {
|
||||
logger.configure({
|
||||
transports: _.values(transports)
|
||||
});
|
||||
_.remove(additionalTransports, (transport) => {
|
||||
_.remove(additionalTransports, (transport) => {
|
||||
return transport.name === transportName;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,11 +3,6 @@ import { logger, addTransport, configureLogger } from './WinstonLogger';
|
||||
|
||||
const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000;
|
||||
|
||||
// returns Date object rounded to nearest day
|
||||
let _getNearestDay = (date) => {
|
||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
}
|
||||
|
||||
export class WinstonLoggerAdapter extends LoggerAdapter {
|
||||
constructor(options) {
|
||||
super();
|
||||
@@ -38,18 +33,16 @@ export class WinstonLoggerAdapter extends LoggerAdapter {
|
||||
let limit = options.size || 10;
|
||||
let order = options.order || 'desc';
|
||||
let level = options.level || 'info';
|
||||
let roundedUntil = _getNearestDay(until);
|
||||
let roundedFrom = _getNearestDay(from);
|
||||
|
||||
var options = {
|
||||
const queryOptions = {
|
||||
from,
|
||||
until,
|
||||
limit,
|
||||
order
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.query(options, (err, res) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.query(queryOptions, (err, res) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return reject(err);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*eslint no-unused-vars: "off"*/
|
||||
// Push Adapter
|
||||
//
|
||||
// Allows you to change the push notification mechanism.
|
||||
|
||||
@@ -84,7 +84,7 @@ export default class MongoCollection {
|
||||
|
||||
_ensureSparseUniqueIndexInBackground(indexRequest) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._mongoCollection.ensureIndex(indexRequest, { unique: true, background: true, sparse: true }, (error, indexName) => {
|
||||
this._mongoCollection.ensureIndex(indexRequest, { unique: true, background: true, sparse: true }, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import MongoCollection from './MongoCollection';
|
||||
import Parse from 'parse/node';
|
||||
|
||||
function mongoFieldToParseSchemaField(type) {
|
||||
if (type[0] === '*') {
|
||||
@@ -14,16 +15,16 @@ function mongoFieldToParseSchemaField(type) {
|
||||
};
|
||||
}
|
||||
switch (type) {
|
||||
case 'number': return {type: 'Number'};
|
||||
case 'string': return {type: 'String'};
|
||||
case 'boolean': return {type: 'Boolean'};
|
||||
case 'date': return {type: 'Date'};
|
||||
case 'map':
|
||||
case 'object': return {type: 'Object'};
|
||||
case 'array': return {type: 'Array'};
|
||||
case 'geopoint': return {type: 'GeoPoint'};
|
||||
case 'file': return {type: 'File'};
|
||||
case 'bytes': return {type: 'Bytes'};
|
||||
case 'number': return {type: 'Number'};
|
||||
case 'string': return {type: 'String'};
|
||||
case 'boolean': return {type: 'Boolean'};
|
||||
case 'date': return {type: 'Date'};
|
||||
case 'map':
|
||||
case 'object': return {type: 'Object'};
|
||||
case 'array': return {type: 'Array'};
|
||||
case 'geopoint': return {type: 'GeoPoint'};
|
||||
case 'file': return {type: 'File'};
|
||||
case 'bytes': return {type: 'Bytes'};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,16 +88,16 @@ function _mongoSchemaQueryFromNameQuery(name: string, query) {
|
||||
// Does no validation. That is expected to be done in Parse Server.
|
||||
function parseFieldTypeToMongoFieldType({ type, targetClass }) {
|
||||
switch (type) {
|
||||
case 'Pointer': return `*${targetClass}`;
|
||||
case 'Relation': return `relation<${targetClass}>`;
|
||||
case 'Number': return 'number';
|
||||
case 'String': return 'string';
|
||||
case 'Boolean': return 'boolean';
|
||||
case 'Date': return 'date';
|
||||
case 'Object': return 'object';
|
||||
case 'Array': return 'array';
|
||||
case 'GeoPoint': return 'geopoint';
|
||||
case 'File': return 'file';
|
||||
case 'Pointer': return `*${targetClass}`;
|
||||
case 'Relation': return `relation<${targetClass}>`;
|
||||
case 'Number': return 'number';
|
||||
case 'String': return 'string';
|
||||
case 'Boolean': return 'boolean';
|
||||
case 'Date': return 'date';
|
||||
case 'Object': return 'object';
|
||||
case 'Array': return 'array';
|
||||
case 'GeoPoint': return 'geopoint';
|
||||
case 'File': return 'file';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ import {
|
||||
transformWhere,
|
||||
transformUpdate,
|
||||
} from './MongoTransform';
|
||||
import Parse from 'parse/node';
|
||||
import _ from 'lodash';
|
||||
import defaults from '../../../defaults';
|
||||
|
||||
let mongodb = require('mongodb');
|
||||
let MongoClient = mongodb.MongoClient;
|
||||
@@ -63,7 +65,7 @@ const mongoSchemaFromFieldsAndClassNameAndCLP = (fields, className, classLevelPe
|
||||
}
|
||||
|
||||
if (typeof classLevelPermissions !== 'undefined') {
|
||||
mongoObject._metadata = mongoObject._metadata || {};
|
||||
mongoObject._metadata = mongoObject._metadata || {};
|
||||
if (!classLevelPermissions) {
|
||||
delete mongoObject._metadata.class_permissions;
|
||||
} else {
|
||||
@@ -111,14 +113,14 @@ export class MongoStorageAdapter {
|
||||
delete this.connectionPromise;
|
||||
return;
|
||||
}
|
||||
database.on('error', (error) => {
|
||||
database.on('error', () => {
|
||||
delete this.connectionPromise;
|
||||
});
|
||||
database.on('close', (error) => {
|
||||
database.on('close', () => {
|
||||
delete this.connectionPromise;
|
||||
});
|
||||
this.database = database;
|
||||
}).catch((err) => {
|
||||
}).catch((err) => {
|
||||
delete this.connectionPromise;
|
||||
return Promise.reject(err);
|
||||
});
|
||||
@@ -288,7 +290,7 @@ export class MongoStorageAdapter {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
|
||||
}
|
||||
return Promise.resolve();
|
||||
}, error => {
|
||||
}, () => {
|
||||
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error');
|
||||
});
|
||||
}
|
||||
@@ -327,7 +329,7 @@ export class MongoStorageAdapter {
|
||||
schema = convertParseSchemaToMongoSchema(schema);
|
||||
let mongoWhere = transformWhere(className, query, schema);
|
||||
let mongoSort = _.mapKeys(sort, (value, fieldName) => transformKey(className, fieldName, schema));
|
||||
let mongoKeys = _.reduce(keys, (memo, key) => {
|
||||
let mongoKeys = _.reduce(keys, (memo, key) => {
|
||||
memo[transformKey(className, key, schema)] = 1;
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
@@ -6,10 +6,10 @@ var Parse = require('parse/node').Parse;
|
||||
const transformKey = (className, fieldName, schema) => {
|
||||
// Check if the schema is known since it's a built-in field.
|
||||
switch(fieldName) {
|
||||
case 'objectId': return '_id';
|
||||
case 'createdAt': return '_created_at';
|
||||
case 'updatedAt': return '_updated_at';
|
||||
case 'sessionToken': return '_session_token';
|
||||
case 'objectId': return '_id';
|
||||
case 'createdAt': return '_created_at';
|
||||
case 'updatedAt': return '_updated_at';
|
||||
case 'sessionToken': return '_session_token';
|
||||
}
|
||||
|
||||
if (schema.fields[fieldName] && schema.fields[fieldName].__type == 'Pointer') {
|
||||
@@ -197,7 +197,7 @@ function transformQueryKeyValue(className, key, value, schema) {
|
||||
return {key: '$or', value: value.map(subQuery => transformWhere(className, subQuery, schema))};
|
||||
case '$and':
|
||||
return {key: '$and', value: value.map(subQuery => transformWhere(className, subQuery, schema))};
|
||||
default:
|
||||
default: {
|
||||
// Other auth data
|
||||
const authDataMatch = key.match(/^authData\.([a-zA-Z0-9_]+)\.id$/);
|
||||
if (authDataMatch) {
|
||||
@@ -206,6 +206,7 @@ function transformQueryKeyValue(className, key, value, schema) {
|
||||
return {key: `_auth_data_${provider}.id`, value};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const expectedTypeIsArray =
|
||||
schema &&
|
||||
@@ -530,8 +531,8 @@ function transformConstraint(constraint, inArray) {
|
||||
break;
|
||||
|
||||
case '$in':
|
||||
case '$nin':
|
||||
var arr = constraint[key];
|
||||
case '$nin': {
|
||||
let arr = constraint[key];
|
||||
if (!(arr instanceof Array)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value');
|
||||
}
|
||||
@@ -543,16 +544,16 @@ function transformConstraint(constraint, inArray) {
|
||||
return result;
|
||||
});
|
||||
break;
|
||||
|
||||
case '$all':
|
||||
var arr = constraint[key];
|
||||
}
|
||||
case '$all': {
|
||||
let arr = constraint[key];
|
||||
if (!(arr instanceof Array)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON,
|
||||
'bad ' + key + ' value');
|
||||
}
|
||||
answer[key] = arr.map(transformInteriorAtom);
|
||||
break;
|
||||
|
||||
}
|
||||
case '$regex':
|
||||
var s = constraint[key];
|
||||
if (typeof s !== 'string') {
|
||||
@@ -736,7 +737,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
|
||||
case 'symbol':
|
||||
case 'function':
|
||||
throw 'bad value in mongoObjectToParseObject';
|
||||
case 'object':
|
||||
case 'object': {
|
||||
if (mongoObject === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -868,6 +869,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
|
||||
});
|
||||
|
||||
return { ...restObject, ...relationFields };
|
||||
}
|
||||
default:
|
||||
throw 'unknown js type';
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createClient } from './PostgresClient';
|
||||
import Parse from 'parse/node';
|
||||
|
||||
const PostgresRelationDoesNotExistError = '42P01';
|
||||
const PostgresDuplicateRelationError = '42P07';
|
||||
@@ -16,21 +17,21 @@ const debug = function(){
|
||||
|
||||
const parseTypeToPostgresType = type => {
|
||||
switch (type.type) {
|
||||
case 'String': return 'text';
|
||||
case 'Date': return 'timestamp with time zone';
|
||||
case 'Object': return 'jsonb';
|
||||
case 'File': return 'text';
|
||||
case 'Boolean': return 'boolean';
|
||||
case 'Pointer': return 'char(10)';
|
||||
case 'Number': return 'double precision';
|
||||
case 'GeoPoint': return 'point';
|
||||
case 'Array':
|
||||
if (type.contents && type.contents.type === 'String') {
|
||||
return 'text[]';
|
||||
} else {
|
||||
return 'jsonb';
|
||||
}
|
||||
default: throw `no type for ${JSON.stringify(type)} yet`;
|
||||
case 'String': return 'text';
|
||||
case 'Date': return 'timestamp with time zone';
|
||||
case 'Object': return 'jsonb';
|
||||
case 'File': return 'text';
|
||||
case 'Boolean': return 'boolean';
|
||||
case 'Pointer': return 'char(10)';
|
||||
case 'Number': return 'double precision';
|
||||
case 'GeoPoint': return 'point';
|
||||
case 'Array':
|
||||
if (type.contents && type.contents.type === 'String') {
|
||||
return 'text[]';
|
||||
} else {
|
||||
return 'jsonb';
|
||||
}
|
||||
default: throw `no type for ${JSON.stringify(type)} yet`;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,7 +42,7 @@ const ParseToPosgresComparator = {
|
||||
'$lte': '<='
|
||||
}
|
||||
|
||||
const toPostgresValue = value => {
|
||||
const toPostgresValue = value => {
|
||||
if (typeof value === 'object') {
|
||||
if (value.__type === 'Date') {
|
||||
return value.iso;
|
||||
@@ -53,7 +54,7 @@ const toPostgresValue = value => {
|
||||
return value;
|
||||
}
|
||||
|
||||
const transformValue = value => {
|
||||
const transformValue = value => {
|
||||
if (value.__type === 'Pointer') {
|
||||
return value.objectId;
|
||||
}
|
||||
@@ -79,7 +80,7 @@ const defaultCLPS = Object.freeze({
|
||||
addField: {'*': true},
|
||||
});
|
||||
|
||||
const toParseSchema = (schema) => {
|
||||
const toParseSchema = (schema) => {
|
||||
if (schema.className === '_User') {
|
||||
delete schema.fields._hashed_password;
|
||||
}
|
||||
@@ -98,7 +99,7 @@ const toParseSchema = (schema) => {
|
||||
};
|
||||
}
|
||||
|
||||
const toPostgresSchema = (schema) => {
|
||||
const toPostgresSchema = (schema) => {
|
||||
if (!schema) {
|
||||
return schema;
|
||||
}
|
||||
@@ -111,8 +112,8 @@ const toPostgresSchema = (schema) => {
|
||||
return schema;
|
||||
}
|
||||
|
||||
const handleDotFields = (object) => {
|
||||
Object.keys(object).forEach(fieldName => {
|
||||
const handleDotFields = (object) => {
|
||||
Object.keys(object).forEach(fieldName => {
|
||||
if (fieldName.indexOf('.') > -1) {
|
||||
let components = fieldName.split('.');
|
||||
let first = components.shift();
|
||||
@@ -123,7 +124,9 @@ const handleDotFields = (object) => {
|
||||
if (value && value.__op === 'Delete') {
|
||||
value = undefined;
|
||||
}
|
||||
while(next = components.shift()) {
|
||||
/* eslint-disable no-cond-assign */
|
||||
while(next = components.shift()) {
|
||||
/* eslint-enable no-cond-assign */
|
||||
currentObj[next] = currentObj[next] || {};
|
||||
if (components.length === 0) {
|
||||
currentObj[next] = value;
|
||||
@@ -151,7 +154,7 @@ const validateKeys = (object) => {
|
||||
}
|
||||
|
||||
// Returns the list of join tables on a schema
|
||||
const joinTablesForSchema = (schema) => {
|
||||
const joinTablesForSchema = (schema) => {
|
||||
let list = [];
|
||||
if (schema) {
|
||||
Object.keys(schema.fields).forEach((field) => {
|
||||
@@ -185,11 +188,11 @@ const buildWhereClause = ({ schema, query, index }) => {
|
||||
}
|
||||
|
||||
if (fieldName.indexOf('.') >= 0) {
|
||||
let components = fieldName.split('.').map((cmpt, index) => {
|
||||
let components = fieldName.split('.').map((cmpt, index) => {
|
||||
if (index === 0) {
|
||||
return `"${cmpt}"`;
|
||||
}
|
||||
return `'${cmpt}'`;
|
||||
return `'${cmpt}'`;
|
||||
});
|
||||
let name = components.slice(0, components.length-1).join('->');
|
||||
name+='->>'+components[components.length-1];
|
||||
@@ -209,7 +212,7 @@ const buildWhereClause = ({ schema, query, index }) => {
|
||||
} else if (fieldName === '$or' || fieldName === '$and') {
|
||||
let clauses = [];
|
||||
let clauseValues = [];
|
||||
fieldValue.forEach((subQuery, idx) => {
|
||||
fieldValue.forEach((subQuery) => {
|
||||
let clause = buildWhereClause({ schema, query: subQuery, index });
|
||||
if (clause.pattern.length > 0) {
|
||||
clauses.push(clause.pattern);
|
||||
@@ -268,7 +271,7 @@ const buildWhereClause = ({ schema, query, index }) => {
|
||||
}
|
||||
index = index + 1 + inPatterns.length;
|
||||
} else if (isInOrNin) {
|
||||
var createConstraint = (baseArray, notIn) => {
|
||||
var createConstraint = (baseArray, notIn) => {
|
||||
if (baseArray.length > 0) {
|
||||
let not = notIn ? ' NOT ' : '';
|
||||
if (isArrayField) {
|
||||
@@ -375,7 +378,7 @@ const buildWhereClause = ({ schema, query, index }) => {
|
||||
index += 2;
|
||||
}
|
||||
|
||||
Object.keys(ParseToPosgresComparator).forEach(cmp => {
|
||||
Object.keys(ParseToPosgresComparator).forEach(cmp => {
|
||||
if (fieldValue[cmp]) {
|
||||
let pgComparator = ParseToPosgresComparator[cmp];
|
||||
patterns.push(`$${index}:name ${pgComparator} $${index + 1}`);
|
||||
@@ -416,10 +419,10 @@ export class PostgresStorageAdapter {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
classExists(name) {
|
||||
return this._client.one(`SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = $1)`, [name]).then((res) => {
|
||||
return this._client.one(`SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = $1)`, [name]).then((res) => {
|
||||
return res.exists;
|
||||
});
|
||||
}
|
||||
@@ -433,15 +436,15 @@ export class PostgresStorageAdapter {
|
||||
|
||||
createClass(className, schema) {
|
||||
return this._client.tx(t => {
|
||||
const q1 = this.createTable(className, schema, t);
|
||||
const q2 = t.none('INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)', { className, schema });
|
||||
const q1 = this.createTable(className, schema, t);
|
||||
const q2 = t.none('INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)', { className, schema });
|
||||
|
||||
return t.batch([q1, q2]);
|
||||
return t.batch([q1, q2]);
|
||||
})
|
||||
.then(() => {
|
||||
return toParseSchema(schema)
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch((err) => {
|
||||
if (Array.isArray(err.data) && err.data.length > 1 && err.data[0].result.code === PostgresTransactionAbortedError) {
|
||||
err = err.data[1].result;
|
||||
}
|
||||
@@ -500,9 +503,9 @@ export class PostgresStorageAdapter {
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}).then(() => {
|
||||
}).then(() => {
|
||||
// Create the relation tables
|
||||
return Promise.all(relations.map((fieldName) => {
|
||||
return Promise.all(relations.map((fieldName) => {
|
||||
return conn.none('CREATE TABLE IF NOT EXISTS $<joinTable:name> ("relatedId" varChar(120), "owningId" varChar(120), PRIMARY KEY("relatedId", "owningId") )', {joinTable: `_Join:${fieldName}:${className}`});
|
||||
}));
|
||||
});
|
||||
@@ -551,11 +554,11 @@ export class PostgresStorageAdapter {
|
||||
// Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.)
|
||||
// and resolves with false if it wasn't (eg. a join table). Rejects if deletion was impossible.
|
||||
deleteClass(className) {
|
||||
return Promise.resolve().then(() => {
|
||||
return Promise.resolve().then(() => {
|
||||
let operations = [[`DROP TABLE IF EXISTS $1:name`, [className]],
|
||||
[`DELETE FROM "_SCHEMA" WHERE "className"=$1`, [className]]];
|
||||
return this._client.tx(t=>t.batch(operations.map(statement=>t.none(statement[0], statement[1]))));
|
||||
}).then(() => {
|
||||
}).then(() => {
|
||||
// resolves with false when _Join table
|
||||
return className.indexOf('_Join:') != 0;
|
||||
});
|
||||
@@ -567,7 +570,7 @@ export class PostgresStorageAdapter {
|
||||
debug('deleteAllClasses');
|
||||
return this._client.any('SELECT * FROM "_SCHEMA"')
|
||||
.then(results => {
|
||||
let joins = results.reduce((list, schema) => {
|
||||
let joins = results.reduce((list, schema) => {
|
||||
return list.concat(joinTablesForSchema(schema.schema));
|
||||
}, []);
|
||||
const classes = ['_SCHEMA','_PushStatus','_JobStatus','_Hooks','_GlobalConfig', ...results.map(result => result.className), ...joins];
|
||||
@@ -579,7 +582,7 @@ export class PostgresStorageAdapter {
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}).then(() => {
|
||||
}).then(() => {
|
||||
debug(`deleteAllClasses done in ${new Date().getTime() - now}`);
|
||||
});
|
||||
}
|
||||
@@ -600,8 +603,8 @@ export class PostgresStorageAdapter {
|
||||
deleteFields(className, schema, fieldNames) {
|
||||
debug('deleteFields', className, fieldNames);
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
fieldNames = fieldNames.reduce((list, fieldName) => {
|
||||
.then(() => {
|
||||
fieldNames = fieldNames.reduce((list, fieldName) => {
|
||||
let field = schema.fields[fieldName]
|
||||
if (field.type !== 'Relation') {
|
||||
list.push(fieldName);
|
||||
@@ -611,11 +614,11 @@ export class PostgresStorageAdapter {
|
||||
}, []);
|
||||
|
||||
let values = [className, ...fieldNames];
|
||||
let columns = fieldNames.map((name, idx) => {
|
||||
let columns = fieldNames.map((name, idx) => {
|
||||
return `$${idx+2}:name`;
|
||||
}).join(',');
|
||||
|
||||
let doBatch = (t) => {
|
||||
let doBatch = (t) => {
|
||||
let batch = [
|
||||
t.none('UPDATE "_SCHEMA" SET "schema"=$<schema> WHERE "className"=$<className>', {schema, className})
|
||||
];
|
||||
@@ -624,7 +627,7 @@ export class PostgresStorageAdapter {
|
||||
}
|
||||
return batch;
|
||||
}
|
||||
return this._client.tx((t) => {
|
||||
return this._client.tx((t) => {
|
||||
return t.batch(doBatch(t));
|
||||
});
|
||||
});
|
||||
@@ -658,7 +661,6 @@ export class PostgresStorageAdapter {
|
||||
createObject(className, schema, object) {
|
||||
debug('createObject', className, object);
|
||||
let columnsArray = [];
|
||||
let newFieldsArray = [];
|
||||
let valuesArray = [];
|
||||
schema = toPostgresSchema(schema);
|
||||
let geoPoints = {};
|
||||
@@ -705,39 +707,39 @@ export class PostgresStorageAdapter {
|
||||
return;
|
||||
}
|
||||
switch (schema.fields[fieldName].type) {
|
||||
case 'Date':
|
||||
if (object[fieldName]) {
|
||||
valuesArray.push(object[fieldName].iso);
|
||||
} else {
|
||||
valuesArray.push(null);
|
||||
}
|
||||
break;
|
||||
case 'Pointer':
|
||||
valuesArray.push(object[fieldName].objectId);
|
||||
break;
|
||||
case 'Array':
|
||||
if (['_rperm', '_wperm'].indexOf(fieldName) >= 0) {
|
||||
valuesArray.push(object[fieldName]);
|
||||
} else {
|
||||
valuesArray.push(JSON.stringify(object[fieldName]));
|
||||
}
|
||||
break;
|
||||
case 'Object':
|
||||
case 'String':
|
||||
case 'Number':
|
||||
case 'Boolean':
|
||||
case 'Date':
|
||||
if (object[fieldName]) {
|
||||
valuesArray.push(object[fieldName].iso);
|
||||
} else {
|
||||
valuesArray.push(null);
|
||||
}
|
||||
break;
|
||||
case 'Pointer':
|
||||
valuesArray.push(object[fieldName].objectId);
|
||||
break;
|
||||
case 'Array':
|
||||
if (['_rperm', '_wperm'].indexOf(fieldName) >= 0) {
|
||||
valuesArray.push(object[fieldName]);
|
||||
break;
|
||||
case 'File':
|
||||
valuesArray.push(object[fieldName].name);
|
||||
break;
|
||||
case 'GeoPoint':
|
||||
} else {
|
||||
valuesArray.push(JSON.stringify(object[fieldName]));
|
||||
}
|
||||
break;
|
||||
case 'Object':
|
||||
case 'String':
|
||||
case 'Number':
|
||||
case 'Boolean':
|
||||
valuesArray.push(object[fieldName]);
|
||||
break;
|
||||
case 'File':
|
||||
valuesArray.push(object[fieldName].name);
|
||||
break;
|
||||
case 'GeoPoint':
|
||||
// pop the point and process later
|
||||
geoPoints[fieldName] = object[fieldName];
|
||||
columnsArray.pop();
|
||||
break;
|
||||
default:
|
||||
throw `Type ${schema.fields[fieldName].type} not supported yet`;
|
||||
geoPoints[fieldName] = object[fieldName];
|
||||
columnsArray.pop();
|
||||
break;
|
||||
default:
|
||||
throw `Type ${schema.fields[fieldName].type} not supported yet`;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -752,7 +754,7 @@ export class PostgresStorageAdapter {
|
||||
}
|
||||
return `$${index + 2 + columnsArray.length}${termination}`;
|
||||
});
|
||||
let geoPointsInjects = Object.keys(geoPoints).map((key, idx) => {
|
||||
let geoPointsInjects = Object.keys(geoPoints).map((key) => {
|
||||
let value = geoPoints[key];
|
||||
valuesArray.push(value.longitude, value.latitude);
|
||||
let l = valuesArray.length + columnsArray.length;
|
||||
@@ -802,13 +804,12 @@ export class PostgresStorageAdapter {
|
||||
// Return value not currently well specified.
|
||||
findOneAndUpdate(className, schema, query, update) {
|
||||
debug('findOneAndUpdate', className, query, update);
|
||||
return this.updateObjectsByQuery(className, schema, query, update).then((val) => val[0]);
|
||||
return this.updateObjectsByQuery(className, schema, query, update).then((val) => val[0]);
|
||||
}
|
||||
|
||||
// Apply the update to all objects that match the given Parse Query.
|
||||
updateObjectsByQuery(className, schema, query, update) {
|
||||
debug('updateObjectsByQuery', className, query, update);
|
||||
let conditionPatterns = [];
|
||||
let updatePatterns = [];
|
||||
let values = [className]
|
||||
let index = 2;
|
||||
@@ -838,8 +839,8 @@ export class PostgresStorageAdapter {
|
||||
} else if (fieldName == 'authData') {
|
||||
// This recursively sets the json_object
|
||||
// Only 1 level deep
|
||||
let generate = (jsonb, key, value) => {
|
||||
return `json_object_set_key(COALESCE(${jsonb}, '{}'::jsonb), ${key}, ${value})::jsonb`;
|
||||
let generate = (jsonb, key, value) => {
|
||||
return `json_object_set_key(COALESCE(${jsonb}, '{}'::jsonb), ${key}, ${value})::jsonb`;
|
||||
}
|
||||
let lastKey = `$${index}:name`;
|
||||
let fieldNameIndex = index;
|
||||
@@ -930,10 +931,10 @@ export class PostgresStorageAdapter {
|
||||
return p + ` - '$${index + 1 + i}:value'`;
|
||||
}, '');
|
||||
|
||||
updatePatterns.push(`$${index}:name = ( COALESCE($${index}:name, '{}'::jsonb) ${deletePatterns} || $${index + 1 + keysToDelete.length}::jsonb )`);
|
||||
updatePatterns.push(`$${index}:name = ( COALESCE($${index}:name, '{}'::jsonb) ${deletePatterns} || $${index + 1 + keysToDelete.length}::jsonb )`);
|
||||
|
||||
values.push(fieldName, ...keysToDelete, JSON.stringify(fieldValue));
|
||||
index += 2 + keysToDelete.length;
|
||||
values.push(fieldName, ...keysToDelete, JSON.stringify(fieldValue));
|
||||
index += 2 + keysToDelete.length;
|
||||
} else if (Array.isArray(fieldValue)
|
||||
&& schema.fields[fieldName]
|
||||
&& schema.fields[fieldName].type === 'Array') {
|
||||
@@ -970,7 +971,7 @@ export class PostgresStorageAdapter {
|
||||
upsertOneObject(className, schema, query, update) {
|
||||
debug('upsertOneObject', {className, query, update});
|
||||
let createValue = Object.assign({}, query, update);
|
||||
return this.createObject(className, schema, createValue).catch((err) => {
|
||||
return this.createObject(className, schema, createValue).catch((err) => {
|
||||
// ignore duplicate value errors as it's upsert
|
||||
if (err.code === Parse.Error.DUPLICATE_VALUE) {
|
||||
return this.findOneAndUpdate(className, schema, query, update);
|
||||
@@ -999,26 +1000,26 @@ export class PostgresStorageAdapter {
|
||||
|
||||
let sortPattern = '';
|
||||
if (sort) {
|
||||
let sorting = Object.keys(sort).map((key) => {
|
||||
let sorting = Object.keys(sort).map((key) => {
|
||||
// Using $idx pattern gives: non-integer constant in ORDER BY
|
||||
if (sort[key] === 1) {
|
||||
return `"${key}" ASC`;
|
||||
}
|
||||
return `"${key}" DESC`;
|
||||
}).join(',');
|
||||
sortPattern = sort !== undefined && Object.keys(sort).length > 0 ? `ORDER BY ${sorting}` : '';
|
||||
sortPattern = sort !== undefined && Object.keys(sort).length > 0 ? `ORDER BY ${sorting}` : '';
|
||||
}
|
||||
if (where.sorts && Object.keys(where.sorts).length > 0) {
|
||||
if (where.sorts && Object.keys(where.sorts).length > 0) {
|
||||
sortPattern = `ORDER BY ${where.sorts.join(',')}`;
|
||||
}
|
||||
|
||||
let columns = '*';
|
||||
if (keys) {
|
||||
// Exclude empty keys
|
||||
keys = keys.filter((key) => {
|
||||
keys = keys.filter((key) => {
|
||||
return key.length > 0;
|
||||
});
|
||||
columns = keys.map((key, index) => {
|
||||
columns = keys.map((key, index) => {
|
||||
return `$${index+values.length+1}:name`;
|
||||
}).join(',');
|
||||
values = values.concat(keys);
|
||||
@@ -1027,7 +1028,7 @@ export class PostgresStorageAdapter {
|
||||
const qs = `SELECT ${columns} FROM $1:name ${wherePattern} ${sortPattern} ${limitPattern} ${skipPattern}`;
|
||||
debug(qs, values);
|
||||
return this._client.any(qs, values)
|
||||
.catch((err) => {
|
||||
.catch((err) => {
|
||||
// Query on non existing table, don't crash
|
||||
if (err.code === PostgresRelationDoesNotExistError) {
|
||||
return [];
|
||||
@@ -1127,7 +1128,7 @@ export class PostgresStorageAdapter {
|
||||
|
||||
const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||
const qs = `SELECT count(*) FROM $1:name ${wherePattern}`;
|
||||
return this._client.one(qs, values, a => +a.count).catch((err) => {
|
||||
return this._client.one(qs, values, a => +a.count).catch((err) => {
|
||||
if (err.code === PostgresRelationDoesNotExistError) {
|
||||
return 0;
|
||||
}
|
||||
@@ -1138,7 +1139,7 @@ export class PostgresStorageAdapter {
|
||||
performInitialization({ VolatileClassesSchemas }) {
|
||||
let now = new Date().getTime();
|
||||
debug('performInitialization');
|
||||
let promises = VolatileClassesSchemas.map((schema) => {
|
||||
let promises = VolatileClassesSchemas.map((schema) => {
|
||||
return this.createTable(schema.className, schema).catch((err) =>{
|
||||
if (err.code === PostgresDuplicateRelationError || err.code === Parse.Error.INVALID_CLASS_NAME) {
|
||||
return Promise.resolve();
|
||||
@@ -1146,36 +1147,34 @@ export class PostgresStorageAdapter {
|
||||
throw err;
|
||||
});
|
||||
});
|
||||
/* eslint-disable no-console */
|
||||
promises = promises.concat([
|
||||
this._client.any(json_object_set_key).catch((err) => {
|
||||
console.error(err);
|
||||
}),
|
||||
this._client.any(array_add).catch((err) => {
|
||||
console.error(err);
|
||||
}),
|
||||
this._client.any(array_add_unique).catch((err) => {
|
||||
console.error(err);
|
||||
}),
|
||||
this._client.any(array_remove).catch((err) => {
|
||||
console.error(err);
|
||||
}),
|
||||
this._client.any(array_contains_all).catch((err) => {
|
||||
console.error(err);
|
||||
}),
|
||||
this._client.any(array_contains).catch((err) => {
|
||||
console.error(err);
|
||||
})
|
||||
]);
|
||||
return Promise.all(promises).then(() => {
|
||||
this._client.any(json_object_set_key).catch((err) => {
|
||||
console.error(err);
|
||||
}),
|
||||
this._client.any(array_add).catch((err) => {
|
||||
console.error(err);
|
||||
}),
|
||||
this._client.any(array_add_unique).catch((err) => {
|
||||
console.error(err);
|
||||
}),
|
||||
this._client.any(array_remove).catch((err) => {
|
||||
console.error(err);
|
||||
}),
|
||||
this._client.any(array_contains_all).catch((err) => {
|
||||
console.error(err);
|
||||
}),
|
||||
this._client.any(array_contains).catch((err) => {
|
||||
console.error(err);
|
||||
})
|
||||
]);
|
||||
/* eslint-enable no-console */
|
||||
return Promise.all(promises).then(() => {
|
||||
debug(`initialzationDone in ${new Date().getTime() - now}`);
|
||||
}, (err) => {});
|
||||
}, () => {});
|
||||
}
|
||||
}
|
||||
|
||||
function notImplemented() {
|
||||
return Promise.reject(new Error('Not implemented yet.'));
|
||||
}
|
||||
|
||||
function removeWhiteSpace(regex) {
|
||||
if (!regex.endsWith('\n')){
|
||||
regex += '\n';
|
||||
|
||||
11
src/Auth.js
11
src/Auth.js
@@ -1,4 +1,3 @@
|
||||
var deepcopy = require('deepcopy');
|
||||
var Parse = require('parse/node').Parse;
|
||||
var RestQuery = require('./RestQuery');
|
||||
|
||||
@@ -80,13 +79,13 @@ var getAuthForSessionToken = function({ config, sessionToken, installationId } =
|
||||
|
||||
var getAuthForLegacySessionToken = function({config, sessionToken, installationId } = {}) {
|
||||
var restOptions = {
|
||||
limit: 1
|
||||
limit: 1
|
||||
};
|
||||
var query = new RestQuery(config, master(config), '_User', { sessionToken: sessionToken}, restOptions);
|
||||
return query.execute().then((response) => {
|
||||
return query.execute().then((response) => {
|
||||
var results = response.results;
|
||||
if (results.length !== 1) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'invalid legacy session token');
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'invalid legacy session token');
|
||||
}
|
||||
let obj = results[0];
|
||||
obj.className = '_User';
|
||||
@@ -163,7 +162,7 @@ Auth.prototype._loadRoles = function() {
|
||||
|
||||
// Given a list of roleIds, find all the parent roles, returns a promise with all names
|
||||
Auth.prototype._getAllRolesNamesForRoleIds = function(roleIDs, names = [], queriedRoles = {}) {
|
||||
let ins = roleIDs.filter((roleID) => {
|
||||
let ins = roleIDs.filter((roleID) => {
|
||||
return queriedRoles[roleID] !== true;
|
||||
}).map((roleID) => {
|
||||
// mark as queried
|
||||
@@ -194,7 +193,7 @@ Auth.prototype._getAllRolesNamesForRoleIds = function(roleIDs, names = [], queri
|
||||
return Promise.resolve(names);
|
||||
}
|
||||
// Map the results with all Ids and names
|
||||
let resultMap = results.reduce((memo, role) => {
|
||||
let resultMap = results.reduce((memo, role) => {
|
||||
memo.names.push(role.name);
|
||||
memo.ids.push(role.objectId);
|
||||
return memo;
|
||||
|
||||
@@ -50,15 +50,15 @@ export class AdaptableController {
|
||||
|
||||
// Makes sure the prototype matches
|
||||
let mismatches = Object.getOwnPropertyNames(Type.prototype).reduce( (obj, key) => {
|
||||
const adapterType = typeof adapter[key];
|
||||
const expectedType = typeof Type.prototype[key];
|
||||
if (adapterType !== expectedType) {
|
||||
obj[key] = {
|
||||
expected: expectedType,
|
||||
actual: adapterType
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
const adapterType = typeof adapter[key];
|
||||
const expectedType = typeof Type.prototype[key];
|
||||
if (adapterType !== expectedType) {
|
||||
obj[key] = {
|
||||
expected: expectedType,
|
||||
actual: adapterType
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
if (Object.keys(mismatches).length > 0) {
|
||||
|
||||
@@ -7,7 +7,7 @@ export class AnalyticsController extends AdaptableController {
|
||||
return this.adapter.appOpened(req.body, req);
|
||||
}).then((response) => {
|
||||
return { response: response || {} };
|
||||
}).catch((err) => {
|
||||
}).catch(() => {
|
||||
return { response: {} };
|
||||
});
|
||||
}
|
||||
@@ -17,7 +17,7 @@ export class AnalyticsController extends AdaptableController {
|
||||
return this.adapter.trackEvent(req.params.eventName, req.body, req);
|
||||
}).then((response) => {
|
||||
return { response: response || {} };
|
||||
}).catch((err) => {
|
||||
}).catch(() => {
|
||||
return { response: {} };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import { Parse } from 'parse/node';
|
||||
import _ from 'lodash';
|
||||
import mongdb from 'mongodb';
|
||||
import intersect from 'intersect';
|
||||
import deepcopy from 'deepcopy';
|
||||
import logger from '../logger';
|
||||
@@ -45,7 +44,7 @@ const transformObjectACL = ({ ACL, ...result }) => {
|
||||
|
||||
const specialQuerykeys = ['$and', '$or', '_rperm', '_wperm', '_perishable_token', '_email_verify_token', '_email_verify_token_expires_at', '_account_lockout_expires_at', '_failed_login_count'];
|
||||
|
||||
const isSpecialQueryKey = key => {
|
||||
const isSpecialQueryKey = key => {
|
||||
return specialQuerykeys.indexOf(key) >= 0;
|
||||
}
|
||||
|
||||
@@ -148,7 +147,7 @@ DatabaseController.prototype.validateObject = function(className, object, query,
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this.canAddField(schema, className, object, aclGroup);
|
||||
}).then(() => {
|
||||
}).then(() => {
|
||||
return schema.validateObject(className, object, query);
|
||||
});
|
||||
};
|
||||
@@ -193,7 +192,7 @@ const filterSensitiveData = (isMaster, aclGroup, className, object) => {
|
||||
// write permissions.
|
||||
const specialKeysForUpdate = ['_hashed_password', '_perishable_token', '_email_verify_token', '_email_verify_token_expires_at', '_account_lockout_expires_at', '_failed_login_count', '_perishable_token_expires_at', '_password_changed_at'];
|
||||
|
||||
const isSpecialUpdateKey = key => {
|
||||
const isSpecialUpdateKey = key => {
|
||||
return specialKeysForUpdate.indexOf(key) >= 0;
|
||||
}
|
||||
|
||||
@@ -208,7 +207,6 @@ DatabaseController.prototype.update = function(className, query, update, {
|
||||
|
||||
var isMaster = acl === undefined;
|
||||
var aclGroup = acl || [];
|
||||
var mongoUpdate;
|
||||
return this.loadSchema()
|
||||
.then(schemaController => {
|
||||
return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, 'update'))
|
||||
@@ -280,7 +278,7 @@ function sanitizeDatabaseResult(originalObject, result) {
|
||||
let keyUpdate = originalObject[key];
|
||||
// determine if that was an op
|
||||
if (keyUpdate && typeof keyUpdate === 'object' && keyUpdate.__op
|
||||
&& ['Add', 'AddUnique', 'Remove', 'Increment'].indexOf(keyUpdate.__op) > -1) {
|
||||
&& ['Add', 'AddUnique', 'Remove', 'Increment'].indexOf(keyUpdate.__op) > -1) {
|
||||
// only valid ops that produce an actionable result
|
||||
response[key] = result[key];
|
||||
}
|
||||
@@ -302,7 +300,7 @@ DatabaseController.prototype.handleRelationUpdates = function(className, objectI
|
||||
return;
|
||||
}
|
||||
if (op.__op == 'AddRelation') {
|
||||
for (var object of op.objects) {
|
||||
for (let object of op.objects) {
|
||||
pending.push(this.addRelation(key, className,
|
||||
objectId,
|
||||
object.objectId));
|
||||
@@ -311,7 +309,7 @@ DatabaseController.prototype.handleRelationUpdates = function(className, objectI
|
||||
}
|
||||
|
||||
if (op.__op == 'RemoveRelation') {
|
||||
for (var object of op.objects) {
|
||||
for (let object of op.objects) {
|
||||
pending.push(this.removeRelation(key, className,
|
||||
objectId,
|
||||
object.objectId));
|
||||
@@ -326,10 +324,10 @@ DatabaseController.prototype.handleRelationUpdates = function(className, objectI
|
||||
}
|
||||
};
|
||||
|
||||
for (var key in update) {
|
||||
for (let key in update) {
|
||||
process(update[key], key);
|
||||
}
|
||||
for (var key of deleteMe) {
|
||||
for (let key of deleteMe) {
|
||||
delete update[key];
|
||||
}
|
||||
return Promise.all(pending);
|
||||
@@ -415,35 +413,35 @@ const flattenUpdateOperatorsForCreate = object => {
|
||||
for (let key in object) {
|
||||
if (object[key] && object[key].__op) {
|
||||
switch (object[key].__op) {
|
||||
case 'Increment':
|
||||
if (typeof object[key].amount !== 'number') {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
|
||||
}
|
||||
object[key] = object[key].amount;
|
||||
break;
|
||||
case 'Add':
|
||||
if (!(object[key].objects instanceof Array)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
|
||||
}
|
||||
object[key] = object[key].objects;
|
||||
break;
|
||||
case 'AddUnique':
|
||||
if (!(object[key].objects instanceof Array)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
|
||||
}
|
||||
object[key] = object[key].objects;
|
||||
break;
|
||||
case 'Remove':
|
||||
if (!(object[key].objects instanceof Array)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
|
||||
}
|
||||
object[key] = []
|
||||
break;
|
||||
case 'Delete':
|
||||
delete object[key];
|
||||
break;
|
||||
default:
|
||||
throw new Parse.Error(Parse.Error.COMMAND_UNAVAILABLE, `The ${object[key].__op} operator is not supported yet.`);
|
||||
case 'Increment':
|
||||
if (typeof object[key].amount !== 'number') {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
|
||||
}
|
||||
object[key] = object[key].amount;
|
||||
break;
|
||||
case 'Add':
|
||||
if (!(object[key].objects instanceof Array)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
|
||||
}
|
||||
object[key] = object[key].objects;
|
||||
break;
|
||||
case 'AddUnique':
|
||||
if (!(object[key].objects instanceof Array)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
|
||||
}
|
||||
object[key] = object[key].objects;
|
||||
break;
|
||||
case 'Remove':
|
||||
if (!(object[key].objects instanceof Array)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
|
||||
}
|
||||
object[key] = []
|
||||
break;
|
||||
case 'Delete':
|
||||
delete object[key];
|
||||
break;
|
||||
default:
|
||||
throw new Parse.Error(Parse.Error.COMMAND_UNAVAILABLE, `The ${object[key].__op} operator is not supported yet.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -451,7 +449,7 @@ const flattenUpdateOperatorsForCreate = object => {
|
||||
|
||||
const transformAuthData = (className, object, schema) => {
|
||||
if (object.authData && className === '_User') {
|
||||
Object.keys(object.authData).forEach(provider => {
|
||||
Object.keys(object.authData).forEach(provider => {
|
||||
const providerData = object.authData[provider];
|
||||
const fieldName = `_auth_data_${provider}`;
|
||||
if (providerData == null) {
|
||||
@@ -504,7 +502,7 @@ DatabaseController.prototype.canAddField = function(schema, className, object, a
|
||||
}
|
||||
let fields = Object.keys(object);
|
||||
let schemaFields = Object.keys(classSchema);
|
||||
let newKeys = fields.filter((field) => {
|
||||
let newKeys = fields.filter((field) => {
|
||||
return schemaFields.indexOf(field) < 0;
|
||||
})
|
||||
if (newKeys.length > 0) {
|
||||
@@ -523,20 +521,6 @@ DatabaseController.prototype.deleteEverything = function() {
|
||||
]);
|
||||
};
|
||||
|
||||
// Finds the keys in a query. Returns a Set. REST format only
|
||||
function keysForQuery(query) {
|
||||
var sublist = query['$and'] || query['$or'];
|
||||
if (sublist) {
|
||||
let answer = sublist.reduce((memo, subquery) => {
|
||||
return memo.concat(keysForQuery(subquery));
|
||||
}, []);
|
||||
|
||||
return new Set(answer);
|
||||
}
|
||||
|
||||
return new Set(Object.keys(query));
|
||||
}
|
||||
|
||||
// Returns a promise for a list of related ids given an owning id.
|
||||
// className here is the owning className.
|
||||
DatabaseController.prototype.relatedIds = function(className, key, owningId) {
|
||||
@@ -561,10 +545,10 @@ DatabaseController.prototype.reduceInRelation = function(className, query, schem
|
||||
if (query['$or']) {
|
||||
let ors = query['$or'];
|
||||
return Promise.all(ors.map((aQuery, index) => {
|
||||
return this.reduceInRelation(className, aQuery, schema).then((aQuery) => {
|
||||
return this.reduceInRelation(className, aQuery, schema).then((aQuery) => {
|
||||
query['$or'][index] = aQuery;
|
||||
});
|
||||
})).then(() => {
|
||||
})).then(() => {
|
||||
return Promise.resolve(query);
|
||||
});
|
||||
}
|
||||
@@ -575,7 +559,6 @@ DatabaseController.prototype.reduceInRelation = function(className, query, schem
|
||||
if (!t || t.type !== 'Relation') {
|
||||
return Promise.resolve(query);
|
||||
}
|
||||
let relatedClassName = t.targetClass;
|
||||
// Build the list of queries
|
||||
let queries = Object.keys(query[key]).map((constraintKey) => {
|
||||
let relatedIds;
|
||||
@@ -625,7 +608,7 @@ DatabaseController.prototype.reduceInRelation = function(className, query, schem
|
||||
return Promise.resolve();
|
||||
})
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
return Promise.all(promises).then(() => {
|
||||
return Promise.resolve(query);
|
||||
})
|
||||
};
|
||||
@@ -839,7 +822,7 @@ DatabaseController.prototype.deleteSchema = function(className) {
|
||||
})
|
||||
.then(schema => {
|
||||
return this.collectionExists(className)
|
||||
.then(exist => this.adapter.count(className, { fields: {} }))
|
||||
.then(() => this.adapter.count(className, { fields: {} }))
|
||||
.then(count => {
|
||||
if (count > 0) {
|
||||
throw new Parse.Error(255, `Class ${className} is not empty, contains ${count} objects, cannot drop schema.`);
|
||||
@@ -866,10 +849,10 @@ DatabaseController.prototype.addPointerPermissions = function(schema, className,
|
||||
let perms = schema.perms[className];
|
||||
let field = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
|
||||
let userACL = aclGroup.filter((acl) => {
|
||||
return acl.indexOf('role:') != 0 && acl != '*';
|
||||
return acl.indexOf('role:') != 0 && acl != '*';
|
||||
});
|
||||
// the ACL should have exactly 1 user
|
||||
if (perms && perms[field] && perms[field].length > 0) {
|
||||
if (perms && perms[field] && perms[field].length > 0) {
|
||||
// No user set return undefined
|
||||
// If the length is > 1, that means we didn't dedup users correctly
|
||||
if (userACL.length != 1) {
|
||||
@@ -877,20 +860,19 @@ DatabaseController.prototype.addPointerPermissions = function(schema, className,
|
||||
}
|
||||
let userId = userACL[0];
|
||||
let userPointer = {
|
||||
"__type": "Pointer",
|
||||
"className": "_User",
|
||||
"objectId": userId
|
||||
};
|
||||
"__type": "Pointer",
|
||||
"className": "_User",
|
||||
"objectId": userId
|
||||
};
|
||||
|
||||
let constraints = {};
|
||||
let permFields = perms[field];
|
||||
let ors = permFields.map((key) => {
|
||||
let ors = permFields.map((key) => {
|
||||
let q = {
|
||||
[key]: userPointer
|
||||
};
|
||||
return {'$and': [q, query]};
|
||||
});
|
||||
if (ors.length > 1) {
|
||||
if (ors.length > 1) {
|
||||
return {'$or': ors};
|
||||
}
|
||||
return ors[0];
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// FilesController.js
|
||||
import { Parse } from 'parse/node';
|
||||
import { randomHexString } from '../cryptoUtils';
|
||||
import AdaptableController from './AdaptableController';
|
||||
import { FilesAdapter } from '../Adapters/Files/FilesAdapter';
|
||||
@@ -85,7 +84,7 @@ export class FilesController extends AdaptableController {
|
||||
|
||||
getFileStream(config, filename) {
|
||||
return this.adapter.getFileStream(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default FilesController;
|
||||
|
||||
@@ -51,8 +51,7 @@ export class HooksController {
|
||||
return this._removeHooks({ className: className, triggerName: triggerName });
|
||||
}
|
||||
|
||||
_getHooks(query = {}, limit) {
|
||||
let options = limit ? { limit: limit } : undefined;
|
||||
_getHooks(query = {}) {
|
||||
return this.database.find(DefaultHooksCollectionName, query).then((results) => {
|
||||
return results.map((result) => {
|
||||
delete result.objectId;
|
||||
@@ -113,7 +112,7 @@ export class HooksController {
|
||||
}
|
||||
|
||||
return this.addHook(hook);
|
||||
};
|
||||
}
|
||||
|
||||
createHook(aHook) {
|
||||
if (aHook.functionName) {
|
||||
@@ -134,7 +133,7 @@ export class HooksController {
|
||||
}
|
||||
|
||||
throw new Parse.Error(143, "invalid hook declaration");
|
||||
};
|
||||
}
|
||||
|
||||
updateHook(aHook) {
|
||||
if (aHook.functionName) {
|
||||
@@ -153,7 +152,7 @@ export class HooksController {
|
||||
});
|
||||
}
|
||||
throw new Parse.Error(143, "invalid hook declaration");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function wrapToHTTPRequest(hook, key) {
|
||||
@@ -170,7 +169,7 @@ function wrapToHTTPRequest(hook, key) {
|
||||
jsonBody.original = req.original.toJSON();
|
||||
jsonBody.original.className = req.original.className;
|
||||
}
|
||||
let jsonRequest = {
|
||||
let jsonRequest: any = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
|
||||
@@ -5,7 +5,6 @@ export class LiveQueryController {
|
||||
liveQueryPublisher: any;
|
||||
|
||||
constructor(config: any) {
|
||||
let classNames;
|
||||
// If config is empty, we just assume no classs needs to be registered as LiveQuery
|
||||
if (!config || !config.classNames) {
|
||||
this.classNames = new Set();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Parse } from 'parse/node';
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
import AdaptableController from './AdaptableController';
|
||||
import { LoggerAdapter } from '../Adapters/Logger/LoggerAdapter';
|
||||
import url from 'url';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Parse } from 'parse/node';
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
import rest from '../rest';
|
||||
import AdaptableController from './AdaptableController';
|
||||
import { PushAdapter } from '../Adapters/Push/PushAdapter';
|
||||
@@ -9,7 +8,6 @@ import RestWrite from '../RestWrite';
|
||||
import { master } from '../Auth';
|
||||
import { pushStatusHandler } from '../StatusHandler';
|
||||
|
||||
const FEATURE_NAME = 'push';
|
||||
const UNSUPPORTED_BADGE_KEY = "unsupported";
|
||||
|
||||
export class PushController extends AdaptableController {
|
||||
@@ -24,7 +22,7 @@ export class PushController extends AdaptableController {
|
||||
var deviceTypes = [];
|
||||
if (typeof deviceTypeField === 'string') {
|
||||
deviceTypes.push(deviceTypeField);
|
||||
} else if (typeof deviceTypeField['$in'] === 'array') {
|
||||
} else if (Array.isArray(deviceTypeField['$in'])) {
|
||||
deviceTypes.concat(deviceTypeField['$in']);
|
||||
}
|
||||
for (var i = 0; i < deviceTypes.length; i++) {
|
||||
@@ -98,13 +96,13 @@ export class PushController extends AdaptableController {
|
||||
}).then((results) => {
|
||||
return pushStatus.complete(results);
|
||||
}).catch((err) => {
|
||||
return pushStatus.fail(err).then(() => {
|
||||
return pushStatus.fail(err).then(() => {
|
||||
throw err;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
sendToAdapter(body, installations, pushStatus, config) {
|
||||
sendToAdapter(body, installations, pushStatus) {
|
||||
if (body.data && body.data.badge && typeof body.data.badge == 'string' && body.data.badge.toLowerCase() == "increment") {
|
||||
// Collect the badges to reduce the # of calls
|
||||
let badgeInstallationsMap = installations.reduce((map, installation) => {
|
||||
|
||||
@@ -21,7 +21,7 @@ export default class SchemaCache {
|
||||
}
|
||||
|
||||
put(key, value) {
|
||||
return this.cache.get(this.prefix+ALL_KEYS).then((allKeys) => {
|
||||
return this.cache.get(this.prefix+ALL_KEYS).then((allKeys) => {
|
||||
allKeys = allKeys || {};
|
||||
allKeys[key] = true;
|
||||
return Promise.all([this.cache.put(this.prefix+ALL_KEYS, allKeys, this.ttl), this.cache.put(key, value, this.ttl)]);
|
||||
@@ -72,8 +72,7 @@ export default class SchemaCache {
|
||||
|
||||
clear() {
|
||||
// That clears all caches...
|
||||
let promise = Promise.resolve();
|
||||
return this.cache.get(this.prefix+ALL_KEYS).then((allKeys) => {
|
||||
return this.cache.get(this.prefix+ALL_KEYS).then((allKeys) => {
|
||||
if (!allKeys) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
// TODO: hide all schema logic inside the database adapter.
|
||||
|
||||
const Parse = require('parse/node').Parse;
|
||||
import _ from 'lodash';
|
||||
|
||||
const defaultColumns = Object.freeze({
|
||||
// Contain the default columns for every parse object type (except _Join collection)
|
||||
@@ -152,7 +151,7 @@ function validateCLP(perms, fields) {
|
||||
} else {
|
||||
perms[operation].forEach((key) => {
|
||||
if (!fields[key] || fields[key].type != 'Pointer' || fields[key].targetClass != '_User') {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `'${key}' is not a valid column for class level pointer permissions ${operation}`);
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `'${key}' is not a valid column for class level pointer permissions ${operation}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -222,19 +221,19 @@ const fieldTypeIsInvalid = ({ type, targetClass }) => {
|
||||
if (!targetClass) {
|
||||
return new Parse.Error(135, `type ${type} needs a class name`);
|
||||
} else if (typeof targetClass !== 'string') {
|
||||
return invalidJsonError;
|
||||
return invalidJsonError;
|
||||
} else if (!classNameIsValid(targetClass)) {
|
||||
return new Parse.Error(Parse.Error.INVALID_CLASS_NAME, invalidClassNameMessage(targetClass));
|
||||
} else {
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
if (typeof type !== 'string') {
|
||||
}
|
||||
}
|
||||
if (typeof type !== 'string') {
|
||||
return invalidJsonError;
|
||||
}
|
||||
}
|
||||
if (validNonRelationOrPointerTypes.indexOf(type) < 0) {
|
||||
return new Parse.Error(Parse.Error.INCORRECT_TYPE, `invalid field type: ${type}`);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -280,14 +279,14 @@ const injectDefaultSchema = ({className, fields, classLevelPermissions}) => ({
|
||||
const _HooksSchema = {className: "_Hooks", fields: defaultColumns._Hooks};
|
||||
const _GlobalConfigSchema = { className: "_GlobalConfig", fields: defaultColumns._GlobalConfig }
|
||||
const _PushStatusSchema = convertSchemaToAdapterSchema(injectDefaultSchema({
|
||||
className: "_PushStatus",
|
||||
fields: {},
|
||||
classLevelPermissions: {}
|
||||
className: "_PushStatus",
|
||||
fields: {},
|
||||
classLevelPermissions: {}
|
||||
}));
|
||||
const _JobStatusSchema = convertSchemaToAdapterSchema(injectDefaultSchema({
|
||||
className: "_JobStatus",
|
||||
fields: {},
|
||||
classLevelPermissions: {}
|
||||
className: "_JobStatus",
|
||||
fields: {},
|
||||
classLevelPermissions: {}
|
||||
}));
|
||||
const VolatileClassesSchemas = [_HooksSchema, _JobStatusSchema, _PushStatusSchema, _GlobalConfigSchema];
|
||||
|
||||
@@ -325,7 +324,7 @@ export default class SchemaController {
|
||||
reloadData(options = {clearCache: false}) {
|
||||
let promise = Promise.resolve();
|
||||
if (options.clearCache) {
|
||||
promise = promise.then(() => {
|
||||
promise = promise.then(() => {
|
||||
return this._cache.clear();
|
||||
});
|
||||
}
|
||||
@@ -334,7 +333,7 @@ export default class SchemaController {
|
||||
}
|
||||
this.data = {};
|
||||
this.perms = {};
|
||||
this.reloadDataPromise = promise.then(() => {
|
||||
this.reloadDataPromise = promise.then(() => {
|
||||
return this.getAllClasses(options);
|
||||
})
|
||||
.then(allSchemas => {
|
||||
@@ -352,7 +351,7 @@ export default class SchemaController {
|
||||
});
|
||||
});
|
||||
delete this.reloadDataPromise;
|
||||
}, (err) => {
|
||||
}, (err) => {
|
||||
delete this.reloadDataPromise;
|
||||
throw err;
|
||||
});
|
||||
@@ -364,16 +363,16 @@ export default class SchemaController {
|
||||
if (options.clearCache) {
|
||||
promise = this._cache.clear();
|
||||
}
|
||||
return promise.then(() => {
|
||||
return this._cache.getAllClasses()
|
||||
}).then((allClasses) => {
|
||||
return promise.then(() => {
|
||||
return this._cache.getAllClasses()
|
||||
}).then((allClasses) => {
|
||||
if (allClasses && allClasses.length && !options.clearCache) {
|
||||
return Promise.resolve(allClasses);
|
||||
}
|
||||
return this._dbAdapter.getAllClasses()
|
||||
.then(allSchemas => allSchemas.map(injectDefaultSchema))
|
||||
.then(allSchemas => {
|
||||
return this._cache.setAllClasses(allSchemas).then(() => {
|
||||
return this._cache.setAllClasses(allSchemas).then(() => {
|
||||
return allSchemas;
|
||||
});
|
||||
})
|
||||
@@ -385,18 +384,18 @@ export default class SchemaController {
|
||||
if (options.clearCache) {
|
||||
promise = this._cache.clear();
|
||||
}
|
||||
return promise.then(() => {
|
||||
return promise.then(() => {
|
||||
if (allowVolatileClasses && volatileClasses.indexOf(className) > -1) {
|
||||
return Promise.resolve(this.data[className]);
|
||||
}
|
||||
return this._cache.getOneSchema(className).then((cached) => {
|
||||
return this._cache.getOneSchema(className).then((cached) => {
|
||||
if (cached && !options.clearCache) {
|
||||
return Promise.resolve(cached);
|
||||
}
|
||||
return this._dbAdapter.getClass(className)
|
||||
.then(injectDefaultSchema)
|
||||
.then((result) => {
|
||||
return this._cache.setOneSchema(className, result).then(() => {
|
||||
.then((result) => {
|
||||
return this._cache.setOneSchema(className, result).then(() => {
|
||||
return result;
|
||||
})
|
||||
});
|
||||
@@ -419,9 +418,9 @@ export default class SchemaController {
|
||||
|
||||
return this._dbAdapter.createClass(className, convertSchemaToAdapterSchema({ fields, classLevelPermissions, className }))
|
||||
.then(convertAdapterSchemaToParseSchema)
|
||||
.then((res) => {
|
||||
return this._cache.clear().then(() => {
|
||||
return Promise.resolve(res);
|
||||
.then((res) => {
|
||||
return this._cache.clear().then(() => {
|
||||
return Promise.resolve(res);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -504,7 +503,7 @@ export default class SchemaController {
|
||||
return this.addClassIfNotExists(className)
|
||||
// The schema update succeeded. Reload the schema
|
||||
.then(() => this.reloadData({ clearCache: true }))
|
||||
.catch(error => {
|
||||
.catch(() => {
|
||||
// The schema update failed. This can be okay - it might
|
||||
// have failed because there's a race condition and a different
|
||||
// client is making the exact same schema update that we want.
|
||||
@@ -519,7 +518,7 @@ export default class SchemaController {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `Failed to add ${className}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
.catch(() => {
|
||||
// The schema still doesn't validate. Give up
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'schema class name does not revalidate');
|
||||
});
|
||||
@@ -586,7 +585,7 @@ export default class SchemaController {
|
||||
// object if the provided className-fieldName-type tuple is valid.
|
||||
// The className must already be validated.
|
||||
// If 'freeze' is true, refuse to update the schema for this field.
|
||||
enforceFieldExists(className, fieldName, type, freeze) {
|
||||
enforceFieldExists(className, fieldName, type) {
|
||||
if (fieldName.indexOf(".") > 0) {
|
||||
// subdocument key (x.y) => ok if x is of type 'object'
|
||||
fieldName = fieldName.split(".")[ 0 ];
|
||||
@@ -620,14 +619,14 @@ export default class SchemaController {
|
||||
return this._dbAdapter.addFieldIfNotExists(className, fieldName, type).then(() => {
|
||||
// The update succeeded. Reload the schema
|
||||
return this.reloadData({ clearCache: true });
|
||||
}, error => {
|
||||
}, () => {
|
||||
//TODO: introspect the error and only reload if the error is one for which is makes sense to reload
|
||||
|
||||
// The update failed. This can be okay - it might have been a race
|
||||
// condition where another client updated the schema in the same
|
||||
// way that we wanted to. So, just reload the schema
|
||||
return this.reloadData({ clearCache: true });
|
||||
}).then(error => {
|
||||
}).then(() => {
|
||||
// Ensure that the schema now validates
|
||||
if (!dbTypeMatchesObjectType(this.getExpectedType(className, fieldName), type)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, `Could not add field ${fieldName}`);
|
||||
@@ -676,7 +675,7 @@ export default class SchemaController {
|
||||
.then(() => database.adapter.deleteClass(`_Join:${fieldName}:${className}`));
|
||||
}
|
||||
return database.adapter.deleteFields(className, schema, [fieldName]);
|
||||
}).then(() => {
|
||||
}).then(() => {
|
||||
this._cache.clear();
|
||||
});
|
||||
}
|
||||
@@ -772,7 +771,6 @@ export default class SchemaController {
|
||||
return true;
|
||||
}
|
||||
let classPerms = this.perms[className];
|
||||
let perms = classPerms[operation];
|
||||
// No matching CLP, let's check the Pointer permissions
|
||||
// And handle those later
|
||||
let permissionField = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
|
||||
@@ -785,11 +783,11 @@ export default class SchemaController {
|
||||
|
||||
// Process the readUserFields later
|
||||
if (Array.isArray(classPerms[permissionField]) && classPerms[permissionField].length > 0) {
|
||||
return Promise.resolve();
|
||||
return Promise.resolve();
|
||||
}
|
||||
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN,
|
||||
`Permission denied for action ${operation} on class ${className}.`);
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the expected type for a className+key combination
|
||||
// or undefined if the schema is not set
|
||||
@@ -799,7 +797,7 @@ export default class SchemaController {
|
||||
return expectedType === 'map' ? 'Object' : expectedType;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
|
||||
// Checks if a given class is in the schema.
|
||||
hasClass(className) {
|
||||
@@ -859,23 +857,23 @@ function thenValidateRequiredColumns(schemaPromise, className, object, query) {
|
||||
function getType(obj) {
|
||||
let type = typeof obj;
|
||||
switch(type) {
|
||||
case 'boolean':
|
||||
return 'Boolean';
|
||||
case 'string':
|
||||
return 'String';
|
||||
case 'number':
|
||||
return 'Number';
|
||||
case 'map':
|
||||
case 'object':
|
||||
if (!obj) {
|
||||
return undefined;
|
||||
}
|
||||
return getObjectType(obj);
|
||||
case 'function':
|
||||
case 'symbol':
|
||||
case 'undefined':
|
||||
default:
|
||||
throw 'bad obj: ' + obj;
|
||||
case 'boolean':
|
||||
return 'Boolean';
|
||||
case 'string':
|
||||
return 'String';
|
||||
case 'number':
|
||||
return 'Number';
|
||||
case 'map':
|
||||
case 'object':
|
||||
if (!obj) {
|
||||
return undefined;
|
||||
}
|
||||
return getObjectType(obj);
|
||||
case 'function':
|
||||
case 'symbol':
|
||||
case 'undefined':
|
||||
default:
|
||||
throw 'bad obj: ' + obj;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -888,63 +886,68 @@ function getObjectType(obj) {
|
||||
}
|
||||
if (obj.__type){
|
||||
switch(obj.__type) {
|
||||
case 'Pointer' :
|
||||
if(obj.className) {
|
||||
return {
|
||||
type: 'Pointer',
|
||||
targetClass: obj.className
|
||||
}
|
||||
case 'Pointer' :
|
||||
if(obj.className) {
|
||||
return {
|
||||
type: 'Pointer',
|
||||
targetClass: obj.className
|
||||
}
|
||||
case 'Relation' :
|
||||
if(obj.className) {
|
||||
return {
|
||||
type: 'Relation',
|
||||
targetClass: obj.className
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Relation' :
|
||||
if(obj.className) {
|
||||
return {
|
||||
type: 'Relation',
|
||||
targetClass: obj.className
|
||||
}
|
||||
case 'File' :
|
||||
if(obj.name) {
|
||||
return 'File';
|
||||
}
|
||||
case 'Date' :
|
||||
if(obj.iso) {
|
||||
return 'Date';
|
||||
}
|
||||
case 'GeoPoint' :
|
||||
if(obj.latitude != null && obj.longitude != null) {
|
||||
return 'GeoPoint';
|
||||
}
|
||||
case 'Bytes' :
|
||||
if(obj.base64) {
|
||||
return;
|
||||
}
|
||||
default:
|
||||
throw new Parse.Error(Parse.Error.INCORRECT_TYPE, "This is not a valid "+obj.__type);
|
||||
}
|
||||
break;
|
||||
case 'File' :
|
||||
if(obj.name) {
|
||||
return 'File';
|
||||
}
|
||||
break;
|
||||
case 'Date' :
|
||||
if(obj.iso) {
|
||||
return 'Date';
|
||||
}
|
||||
break;
|
||||
case 'GeoPoint' :
|
||||
if(obj.latitude != null && obj.longitude != null) {
|
||||
return 'GeoPoint';
|
||||
}
|
||||
break;
|
||||
case 'Bytes' :
|
||||
if(obj.base64) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new Parse.Error(Parse.Error.INCORRECT_TYPE, "This is not a valid "+obj.__type);
|
||||
}
|
||||
if (obj['$ne']) {
|
||||
return getObjectType(obj['$ne']);
|
||||
}
|
||||
if (obj.__op) {
|
||||
switch(obj.__op) {
|
||||
case 'Increment':
|
||||
return 'Number';
|
||||
case 'Delete':
|
||||
return null;
|
||||
case 'Add':
|
||||
case 'AddUnique':
|
||||
case 'Remove':
|
||||
return 'Array';
|
||||
case 'AddRelation':
|
||||
case 'RemoveRelation':
|
||||
return {
|
||||
type: 'Relation',
|
||||
targetClass: obj.objects[0].className
|
||||
}
|
||||
case 'Batch':
|
||||
return getObjectType(obj.ops[0]);
|
||||
default:
|
||||
throw 'unexpected op: ' + obj.__op;
|
||||
case 'Increment':
|
||||
return 'Number';
|
||||
case 'Delete':
|
||||
return null;
|
||||
case 'Add':
|
||||
case 'AddUnique':
|
||||
case 'Remove':
|
||||
return 'Array';
|
||||
case 'AddRelation':
|
||||
case 'RemoveRelation':
|
||||
return {
|
||||
type: 'Relation',
|
||||
targetClass: obj.objects[0].className
|
||||
}
|
||||
case 'Batch':
|
||||
return getObjectType(obj.ops[0]);
|
||||
default:
|
||||
throw 'unexpected op: ' + obj.__op;
|
||||
}
|
||||
}
|
||||
return 'Object';
|
||||
|
||||
@@ -3,10 +3,9 @@ import { inflate } from '../triggers';
|
||||
import AdaptableController from './AdaptableController';
|
||||
import MailAdapter from '../Adapters/Email/MailAdapter';
|
||||
import rest from '../rest';
|
||||
import Parse from 'parse/node';
|
||||
|
||||
var RestWrite = require('../RestWrite');
|
||||
var RestQuery = require('../RestQuery');
|
||||
var hash = require('../password').hash;
|
||||
var Auth = require('../Auth');
|
||||
|
||||
export class UserController extends AdaptableController {
|
||||
@@ -118,7 +117,7 @@ export class UserController extends AdaptableController {
|
||||
}
|
||||
const token = encodeURIComponent(user._email_verify_token);
|
||||
// We may need to fetch the user in case of update email
|
||||
this.getUserIfNeeded(user).then((user) => {
|
||||
this.getUserIfNeeded(user).then((user) => {
|
||||
const username = encodeURIComponent(user.username);
|
||||
let link = `${this.config.verifyEmailURL}?token=${token}&username=${username}`;
|
||||
let options = {
|
||||
@@ -148,7 +147,6 @@ export class UserController extends AdaptableController {
|
||||
if (!this.adapter) {
|
||||
throw "Trying to send a reset password but no adapter is set";
|
||||
// TODO: No adapter?
|
||||
return;
|
||||
}
|
||||
|
||||
return this.setPasswordResetToken(email)
|
||||
@@ -173,7 +171,7 @@ export class UserController extends AdaptableController {
|
||||
});
|
||||
}
|
||||
|
||||
updatePassword(username, token, password, config) {
|
||||
updatePassword(username, token, password) {
|
||||
return this.checkResetTokenValidity(username, token)
|
||||
.then(user => updateUserPassword(user.objectId, password, this.config))
|
||||
// clear reset password token
|
||||
@@ -191,9 +189,9 @@ export class UserController extends AdaptableController {
|
||||
|
||||
defaultVerificationEmail({link, user, appName, }) {
|
||||
let text = "Hi,\n\n" +
|
||||
"You are being asked to confirm the e-mail address " + user.get("email") + " with " + appName + "\n\n" +
|
||||
"" +
|
||||
"Click here to confirm it:\n" + link;
|
||||
"You are being asked to confirm the e-mail address " + user.get("email") + " with " + appName + "\n\n" +
|
||||
"" +
|
||||
"Click here to confirm it:\n" + link;
|
||||
let to = user.get("email");
|
||||
let subject = 'Please verify your e-mail for ' + appName;
|
||||
return { text, to, subject };
|
||||
@@ -215,6 +213,6 @@ function updateUserPassword(userId, password, config) {
|
||||
return rest.update(config, Auth.master(config), '_User', userId, {
|
||||
password: password
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default UserController;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Parse from 'parse/node';
|
||||
import logger from '../logger';
|
||||
|
||||
import type { FlattenedObjectData } from './Subscription';
|
||||
@@ -54,7 +53,7 @@ class Client {
|
||||
this.subscriptionInfos.set(requestId, subscriptionInfo);
|
||||
}
|
||||
|
||||
getSubscriptionInfo(requestId: numner): any {
|
||||
getSubscriptionInfo(requestId: number): any {
|
||||
return this.subscriptionInfos.get(requestId);
|
||||
}
|
||||
|
||||
|
||||
@@ -251,21 +251,21 @@ class ParseLiveQueryServer {
|
||||
}
|
||||
|
||||
switch(request.op) {
|
||||
case 'connect':
|
||||
this._handleConnect(parseWebsocket, request);
|
||||
break;
|
||||
case 'subscribe':
|
||||
this._handleSubscribe(parseWebsocket, request);
|
||||
break;
|
||||
case 'update':
|
||||
this._handleUpdateSubscription(parseWebsocket, request);
|
||||
break;
|
||||
case 'unsubscribe':
|
||||
this._handleUnsubscribe(parseWebsocket, request);
|
||||
break;
|
||||
default:
|
||||
Client.pushError(parseWebsocket, 3, 'Get unknown operation');
|
||||
logger.error('Get unknown operation', request.op);
|
||||
case 'connect':
|
||||
this._handleConnect(parseWebsocket, request);
|
||||
break;
|
||||
case 'subscribe':
|
||||
this._handleSubscribe(parseWebsocket, request);
|
||||
break;
|
||||
case 'update':
|
||||
this._handleUpdateSubscription(parseWebsocket, request);
|
||||
break;
|
||||
case 'unsubscribe':
|
||||
this._handleUnsubscribe(parseWebsocket, request);
|
||||
break;
|
||||
default:
|
||||
Client.pushError(parseWebsocket, 3, 'Get unknown operation');
|
||||
logger.error('Get unknown operation', request.op);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -335,48 +335,48 @@ class ParseLiveQueryServer {
|
||||
// Resolve false right away if the acl doesn't have any roles
|
||||
const acl_has_roles = Object.keys(acl.permissionsById).some(key => key.startsWith("role:"));
|
||||
if (!acl_has_roles) {
|
||||
return resolve(false);
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
this.sessionTokenCache.getUserId(subscriptionSessionToken)
|
||||
.then((userId) => {
|
||||
|
||||
// Pass along a null if there is no user id
|
||||
if (!userId) {
|
||||
return Parse.Promise.as(null);
|
||||
}
|
||||
if (!userId) {
|
||||
return Parse.Promise.as(null);
|
||||
}
|
||||
|
||||
// Prepare a user object to query for roles
|
||||
// To eliminate a query for the user, create one locally with the id
|
||||
var user = new Parse.User();
|
||||
user.id = userId;
|
||||
return user;
|
||||
var user = new Parse.User();
|
||||
user.id = userId;
|
||||
return user;
|
||||
|
||||
})
|
||||
.then((user) => {
|
||||
|
||||
// Pass along an empty array (of roles) if no user
|
||||
if (!user) {
|
||||
return Parse.Promise.as([]);
|
||||
}
|
||||
if (!user) {
|
||||
return Parse.Promise.as([]);
|
||||
}
|
||||
|
||||
// Then get the user's roles
|
||||
var rolesQuery = new Parse.Query(Parse.Role);
|
||||
rolesQuery.equalTo("users", user);
|
||||
return rolesQuery.find();
|
||||
var rolesQuery = new Parse.Query(Parse.Role);
|
||||
rolesQuery.equalTo("users", user);
|
||||
return rolesQuery.find();
|
||||
}).
|
||||
then((roles) => {
|
||||
|
||||
// Finally, see if any of the user's roles allow them read access
|
||||
for (let role of roles) {
|
||||
if (acl.getRoleReadAccess(role)) {
|
||||
return resolve(true);
|
||||
}
|
||||
for (let role of roles) {
|
||||
if (acl.getRoleReadAccess(role)) {
|
||||
return resolve(true);
|
||||
}
|
||||
resolve(false);
|
||||
}
|
||||
resolve(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
reject(error);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -393,7 +393,7 @@ class ParseLiveQueryServer {
|
||||
});
|
||||
}).then((isMatched) => {
|
||||
return Parse.Promise.as(isMatched);
|
||||
}, (error) => {
|
||||
}, () => {
|
||||
return Parse.Promise.as(false);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export class ParseWebSocket {
|
||||
this.ws.on(wsType, callback);
|
||||
}
|
||||
|
||||
send(message: any, channel: string): void {
|
||||
send(message: any): void {
|
||||
this.ws.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,117 +167,118 @@ function matchesKeyConstraints(object, key, constraints) {
|
||||
compareTo = Parse._decode(key, compareTo);
|
||||
}
|
||||
switch (condition) {
|
||||
case '$lt':
|
||||
if (object[key] >= compareTo) {
|
||||
case '$lt':
|
||||
if (object[key] >= compareTo) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$lte':
|
||||
if (object[key] > compareTo) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$gt':
|
||||
if (object[key] <= compareTo) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$gte':
|
||||
if (object[key] < compareTo) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$ne':
|
||||
if (equalObjects(object[key], compareTo)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$in':
|
||||
if (compareTo.indexOf(object[key]) < 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$nin':
|
||||
if (compareTo.indexOf(object[key]) > -1) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$all':
|
||||
for (i = 0; i < compareTo.length; i++) {
|
||||
if (object[key].indexOf(compareTo[i]) < 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$lte':
|
||||
if (object[key] > compareTo) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$gt':
|
||||
if (object[key] <= compareTo) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$gte':
|
||||
if (object[key] < compareTo) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$ne':
|
||||
if (equalObjects(object[key], compareTo)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$in':
|
||||
if (compareTo.indexOf(object[key]) < 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$nin':
|
||||
if (compareTo.indexOf(object[key]) > -1) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$all':
|
||||
for (i = 0; i < compareTo.length; i++) {
|
||||
if (object[key].indexOf(compareTo[i]) < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '$exists':
|
||||
let propertyExists = typeof object[key] !== 'undefined';
|
||||
let existenceIsRequired = constraints['$exists'];
|
||||
if (typeof constraints['$exists'] !== 'boolean') {
|
||||
}
|
||||
break;
|
||||
case '$exists': {
|
||||
let propertyExists = typeof object[key] !== 'undefined';
|
||||
let existenceIsRequired = constraints['$exists'];
|
||||
if (typeof constraints['$exists'] !== 'boolean') {
|
||||
// The SDK will never submit a non-boolean for $exists, but if someone
|
||||
// tries to submit a non-boolean for $exits outside the SDKs, just ignore it.
|
||||
break;
|
||||
}
|
||||
if ((!propertyExists && existenceIsRequired) || (propertyExists && !existenceIsRequired)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$regex':
|
||||
if (typeof compareTo === 'object') {
|
||||
return compareTo.test(object[key]);
|
||||
}
|
||||
}
|
||||
if ((!propertyExists && existenceIsRequired) || (propertyExists && !existenceIsRequired)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '$regex':
|
||||
if (typeof compareTo === 'object') {
|
||||
return compareTo.test(object[key]);
|
||||
}
|
||||
// JS doesn't support perl-style escaping
|
||||
var expString = '';
|
||||
var escapeEnd = -2;
|
||||
var escapeStart = compareTo.indexOf('\\Q');
|
||||
while (escapeStart > -1) {
|
||||
var expString = '';
|
||||
var escapeEnd = -2;
|
||||
var escapeStart = compareTo.indexOf('\\Q');
|
||||
while (escapeStart > -1) {
|
||||
// Add the unescaped portion
|
||||
expString += compareTo.substring(escapeEnd + 2, escapeStart);
|
||||
escapeEnd = compareTo.indexOf('\\E', escapeStart);
|
||||
if (escapeEnd > -1) {
|
||||
expString += compareTo.substring(escapeStart + 2, escapeEnd)
|
||||
expString += compareTo.substring(escapeEnd + 2, escapeStart);
|
||||
escapeEnd = compareTo.indexOf('\\E', escapeStart);
|
||||
if (escapeEnd > -1) {
|
||||
expString += compareTo.substring(escapeStart + 2, escapeEnd)
|
||||
.replace(/\\\\\\\\E/g, '\\E').replace(/\W/g, '\\$&');
|
||||
}
|
||||
}
|
||||
|
||||
escapeStart = compareTo.indexOf('\\Q', escapeEnd);
|
||||
}
|
||||
expString += compareTo.substring(Math.max(escapeStart, escapeEnd + 2));
|
||||
var exp = new RegExp(expString, constraints.$options || '');
|
||||
if (!exp.test(object[key])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$nearSphere':
|
||||
var distance = compareTo.radiansTo(object[key]);
|
||||
var max = constraints.$maxDistance || Infinity;
|
||||
return distance <= max;
|
||||
case '$within':
|
||||
var southWest = compareTo.$box[0];
|
||||
var northEast = compareTo.$box[1];
|
||||
if (southWest.latitude > northEast.latitude ||
|
||||
escapeStart = compareTo.indexOf('\\Q', escapeEnd);
|
||||
}
|
||||
expString += compareTo.substring(Math.max(escapeStart, escapeEnd + 2));
|
||||
var exp = new RegExp(expString, constraints.$options || '');
|
||||
if (!exp.test(object[key])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '$nearSphere':
|
||||
var distance = compareTo.radiansTo(object[key]);
|
||||
var max = constraints.$maxDistance || Infinity;
|
||||
return distance <= max;
|
||||
case '$within':
|
||||
var southWest = compareTo.$box[0];
|
||||
var northEast = compareTo.$box[1];
|
||||
if (southWest.latitude > northEast.latitude ||
|
||||
southWest.longitude > northEast.longitude) {
|
||||
// Invalid box, crosses the date line
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
object[key].latitude > southWest.latitude &&
|
||||
object[key].latitude < northEast.latitude &&
|
||||
object[key].longitude > southWest.longitude &&
|
||||
object[key].longitude < northEast.longitude
|
||||
);
|
||||
case '$options':
|
||||
);
|
||||
case '$options':
|
||||
// Not a query type, but a way to add options to $regex. Ignore and
|
||||
// avoid the default
|
||||
break;
|
||||
case '$maxDistance':
|
||||
break;
|
||||
case '$maxDistance':
|
||||
// Not a query type, but a way to add a cap to $nearSphere. Ignore and
|
||||
// avoid the default
|
||||
break;
|
||||
case '$select':
|
||||
return false;
|
||||
case '$dontSelect':
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
case '$select':
|
||||
return false;
|
||||
case '$dontSelect':
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -61,7 +61,7 @@ let subscribe = {
|
||||
'fields': {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
@@ -99,7 +99,7 @@ let update = {
|
||||
'fields': {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import {matchesQuery, queryHash} from './QueryTools';
|
||||
import logger from '../logger';
|
||||
|
||||
export type FlattenedObjectData = { [attr: string]: any };
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
// ParseServer - open-source compatible API Server for Parse apps
|
||||
|
||||
var batch = require('./batch'),
|
||||
bodyParser = require('body-parser'),
|
||||
express = require('express'),
|
||||
middlewares = require('./middlewares'),
|
||||
multer = require('multer'),
|
||||
Parse = require('parse/node').Parse,
|
||||
path = require('path'),
|
||||
url = require('url'),
|
||||
authDataManager = require('./authDataManager');
|
||||
bodyParser = require('body-parser'),
|
||||
express = require('express'),
|
||||
middlewares = require('./middlewares'),
|
||||
Parse = require('parse/node').Parse,
|
||||
path = require('path'),
|
||||
url = require('url'),
|
||||
authDataManager = require('./authDataManager');
|
||||
|
||||
import defaults from './defaults';
|
||||
import * as logging from './logger';
|
||||
import AppCache from './cache';
|
||||
import Config from './Config';
|
||||
import parseServerPackage from '../package.json';
|
||||
import PromiseRouter from './PromiseRouter';
|
||||
import requiredParameter from './requiredParameter';
|
||||
import { AnalyticsRouter } from './Routers/AnalyticsRouter';
|
||||
@@ -43,7 +41,6 @@ import { PublicAPIRouter } from './Routers/PublicAPIRouter';
|
||||
import { PushController } from './Controllers/PushController';
|
||||
import { PushRouter } from './Routers/PushRouter';
|
||||
import { CloudCodeRouter } from './Routers/CloudCodeRouter';
|
||||
import { randomString } from './cryptoUtils';
|
||||
import { RolesRouter } from './Routers/RolesRouter';
|
||||
import { SchemasRouter } from './Routers/SchemasRouter';
|
||||
import { SessionsRouter } from './Routers/SessionsRouter';
|
||||
@@ -260,20 +257,20 @@ class ParseServer {
|
||||
try {
|
||||
const parsedURI = url.parse(databaseURI);
|
||||
protocol = parsedURI.protocol ? parsedURI.protocol.toLowerCase() : null;
|
||||
} catch(e) {}
|
||||
} catch(e) { /* */ }
|
||||
switch (protocol) {
|
||||
case 'postgres:':
|
||||
return new PostgresStorageAdapter({
|
||||
uri: databaseURI,
|
||||
collectionPrefix,
|
||||
databaseOptions
|
||||
});
|
||||
default:
|
||||
return new MongoStorageAdapter({
|
||||
uri: databaseURI,
|
||||
collectionPrefix,
|
||||
mongoOptions: databaseOptions,
|
||||
});
|
||||
case 'postgres:':
|
||||
return new PostgresStorageAdapter({
|
||||
uri: databaseURI,
|
||||
collectionPrefix,
|
||||
databaseOptions
|
||||
});
|
||||
default:
|
||||
return new MongoStorageAdapter({
|
||||
uri: databaseURI,
|
||||
collectionPrefix,
|
||||
mongoOptions: databaseOptions,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +306,9 @@ class ParseServer {
|
||||
if (!process.env.TESTING) {
|
||||
process.on('uncaughtException', (err) => {
|
||||
if ( err.code === "EADDRINUSE" ) { // user-friendly message for this common error
|
||||
/* eslint-disable no-console */
|
||||
console.error(`Unable to listen on port ${err.port}. The port is already in use.`);
|
||||
/* eslint-enable no-console */
|
||||
process.exit(0);
|
||||
} else {
|
||||
throw err;
|
||||
|
||||
@@ -13,7 +13,7 @@ function getSessionToken(options) {
|
||||
|
||||
function getAuth(options, config) {
|
||||
if (options.useMasterKey) {
|
||||
return Parse.Promise.as(new Auth.Auth({config, isMaster: true, installationId: 'cloud' }));
|
||||
return Parse.Promise.as(new Auth.Auth({config, isMaster: true, installationId: 'cloud' }));
|
||||
}
|
||||
return getSessionToken(options).then((sessionToken) => {
|
||||
if (sessionToken) {
|
||||
@@ -75,7 +75,7 @@ function ParseServerRESTController(applicationId, router) {
|
||||
return Promise.resolve().then(() => {
|
||||
return router.tryRouteRequest(method, path, request);
|
||||
}).then((response) => {
|
||||
resolve(response.response, response.status, response);
|
||||
resolve(response.response, response.status, response);
|
||||
}, (err) => {
|
||||
if (err instanceof Parse.Error &&
|
||||
err.code == Parse.Error.INVALID_JSON &&
|
||||
@@ -87,13 +87,13 @@ function ParseServerRESTController(applicationId, router) {
|
||||
});
|
||||
}, reject);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
request: handleRequest,
|
||||
ajax: RESTController.ajax
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default ParseServerRESTController;
|
||||
export { ParseServerRESTController };
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
// themselves use our routing information, without disturbing express
|
||||
// components that external developers may be modifying.
|
||||
|
||||
import AppCache from './cache';
|
||||
import Parse from 'parse/node';
|
||||
import express from 'express';
|
||||
import url from 'url';
|
||||
import log from './logger';
|
||||
import {inspect} from 'util';
|
||||
const Layer = require('express/lib/router/layer');
|
||||
@@ -52,7 +51,7 @@ export default class PromiseRouter {
|
||||
for (var route of router.routes) {
|
||||
this.routes.push(route);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
route(method, path, ...handlers) {
|
||||
switch(method) {
|
||||
@@ -68,10 +67,9 @@ export default class PromiseRouter {
|
||||
let handler = handlers[0];
|
||||
|
||||
if (handlers.length > 1) {
|
||||
const length = handlers.length;
|
||||
handler = function(req) {
|
||||
return handlers.reduce((promise, handler) => {
|
||||
return promise.then((result) => {
|
||||
return promise.then(() => {
|
||||
return handler(req);
|
||||
});
|
||||
}, Promise.resolve());
|
||||
@@ -84,7 +82,7 @@ export default class PromiseRouter {
|
||||
handler: handler,
|
||||
layer: new Layer(path, null, handler)
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Returns an object with:
|
||||
// handler: the handler that should deal with this request
|
||||
@@ -105,17 +103,17 @@ export default class PromiseRouter {
|
||||
return {params: params, handler: route.handler};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Mount the routes on this router onto an express app (or express router)
|
||||
mountOnto(expressApp) {
|
||||
this.routes.forEach((route) => {
|
||||
this.routes.forEach((route) => {
|
||||
let method = route.method.toLowerCase();
|
||||
let handler = makeExpressHandler(this.appId, route.handler);
|
||||
expressApp[method].call(expressApp, route.path, handler);
|
||||
});
|
||||
return expressApp;
|
||||
};
|
||||
}
|
||||
|
||||
expressRouter() {
|
||||
return this.mountOnto(express.Router());
|
||||
@@ -140,7 +138,6 @@ export default class PromiseRouter {
|
||||
// Express handlers should never throw; if a promise handler throws we
|
||||
// just treat it like it resolved to an error.
|
||||
function makeExpressHandler(appId, promiseHandler) {
|
||||
let config = AppCache.get(appId);
|
||||
return function(req, res, next) {
|
||||
try {
|
||||
let url = maskSensitiveUrl(req);
|
||||
|
||||
@@ -5,8 +5,6 @@ var SchemaController = require('./Controllers/SchemaController');
|
||||
var Parse = require('parse/node').Parse;
|
||||
const triggers = require('./triggers');
|
||||
|
||||
import { default as FilesController } from './Controllers/FilesController';
|
||||
|
||||
// restOptions can include:
|
||||
// skip
|
||||
// limit
|
||||
@@ -34,11 +32,11 @@ function RestQuery(config, auth, className, restWhere = {}, restOptions = {}, cl
|
||||
}
|
||||
this.restWhere = {
|
||||
'$and': [this.restWhere, {
|
||||
'user': {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: this.auth.user.id
|
||||
}
|
||||
'user': {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: this.auth.user.id
|
||||
}
|
||||
}]
|
||||
};
|
||||
}
|
||||
@@ -188,7 +186,7 @@ RestQuery.prototype.validateClientClassCreation = function() {
|
||||
'This user is not allowed to access ' +
|
||||
'non-existent class: ' + this.className);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -397,41 +395,41 @@ RestQuery.prototype.runFind = function(options = {}) {
|
||||
}
|
||||
let findOptions = Object.assign({}, this.findOptions);
|
||||
if (this.keys) {
|
||||
findOptions.keys = Array.from(this.keys).map((key) => {
|
||||
findOptions.keys = Array.from(this.keys).map((key) => {
|
||||
return key.split('.')[0];
|
||||
});
|
||||
}
|
||||
if (options.op) {
|
||||
findOptions.op = options.op;
|
||||
findOptions.op = options.op;
|
||||
}
|
||||
return this.config.database.find(
|
||||
this.className, this.restWhere, findOptions).then((results) => {
|
||||
if (this.className === '_User') {
|
||||
for (var result of results) {
|
||||
delete result.password;
|
||||
if (this.className === '_User') {
|
||||
for (var result of results) {
|
||||
delete result.password;
|
||||
|
||||
if (result.authData) {
|
||||
Object.keys(result.authData).forEach((provider) => {
|
||||
if (result.authData[provider] === null) {
|
||||
delete result.authData[provider];
|
||||
if (result.authData) {
|
||||
Object.keys(result.authData).forEach((provider) => {
|
||||
if (result.authData[provider] === null) {
|
||||
delete result.authData[provider];
|
||||
}
|
||||
});
|
||||
if (Object.keys(result.authData).length == 0) {
|
||||
delete result.authData;
|
||||
}
|
||||
});
|
||||
if (Object.keys(result.authData).length == 0) {
|
||||
delete result.authData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.config.filesController.expandFilesInObject(this.config, results);
|
||||
this.config.filesController.expandFilesInObject(this.config, results);
|
||||
|
||||
if (this.redirectClassName) {
|
||||
for (var r of results) {
|
||||
r.className = this.redirectClassName;
|
||||
if (this.redirectClassName) {
|
||||
for (var r of results) {
|
||||
r.className = this.redirectClassName;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.response = {results: results};
|
||||
});
|
||||
this.response = {results: results};
|
||||
});
|
||||
};
|
||||
|
||||
// Returns a promise for whether it was successful.
|
||||
@@ -510,7 +508,7 @@ function includePath(config, auth, response, path, restOptions = {}) {
|
||||
let includeRestOptions = {};
|
||||
if (restOptions.keys) {
|
||||
let keys = new Set(restOptions.keys.split(','));
|
||||
let keySet = Array.from(keys).reduce((set, key) => {
|
||||
let keySet = Array.from(keys).reduce((set, key) => {
|
||||
let keyPath = key.split('.');
|
||||
let i=0;
|
||||
for (i; i<path.length; i++) {
|
||||
@@ -528,10 +526,10 @@ function includePath(config, auth, response, path, restOptions = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
let queryPromises = Object.keys(pointersHash).map((className) => {
|
||||
let queryPromises = Object.keys(pointersHash).map((className) => {
|
||||
let where = {'objectId': {'$in': Array.from(pointersHash[className])}};
|
||||
var query = new RestQuery(config, auth, className, where, includeRestOptions);
|
||||
return query.execute({op: 'get'}).then((results) => {
|
||||
return query.execute({op: 'get'}).then((results) => {
|
||||
results.className = className;
|
||||
return Promise.resolve(results);
|
||||
})
|
||||
@@ -604,7 +602,7 @@ function findPointers(object, path) {
|
||||
function replacePointers(object, path, replace) {
|
||||
if (object instanceof Array) {
|
||||
return object.map((obj) => replacePointers(obj, path, replace))
|
||||
.filter((obj) => typeof obj !== 'undefined');
|
||||
.filter((obj) => typeof obj !== 'undefined');
|
||||
}
|
||||
|
||||
if (typeof object !== 'object' || !object) {
|
||||
@@ -642,7 +640,7 @@ function findObjectWithKey(root, key) {
|
||||
}
|
||||
if (root instanceof Array) {
|
||||
for (var item of root) {
|
||||
var answer = findObjectWithKey(item, key);
|
||||
let answer = findObjectWithKey(item, key);
|
||||
if (answer) {
|
||||
return answer;
|
||||
}
|
||||
@@ -652,7 +650,7 @@ function findObjectWithKey(root, key) {
|
||||
return root;
|
||||
}
|
||||
for (var subkey in root) {
|
||||
var answer = findObjectWithKey(root[subkey], key);
|
||||
let answer = findObjectWithKey(root[subkey], key);
|
||||
if (answer) {
|
||||
return answer;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ var SchemaController = require('./Controllers/SchemaController');
|
||||
var deepcopy = require('deepcopy');
|
||||
|
||||
var Auth = require('./Auth');
|
||||
var Config = require('./Config');
|
||||
var cryptoUtils = require('./cryptoUtils');
|
||||
var passwordCrypto = require('./password');
|
||||
var Parse = require('parse/node');
|
||||
@@ -124,7 +123,7 @@ RestWrite.prototype.validateClientClassCreation = function() {
|
||||
'This user is not allowed to access ' +
|
||||
'non-existent class: ' + this.className);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -236,7 +235,7 @@ RestWrite.prototype.validateAuthData = function() {
|
||||
};
|
||||
|
||||
RestWrite.prototype.handleAuthDataValidation = function(authData) {
|
||||
let validations = Object.keys(authData).map((provider) => {
|
||||
let validations = Object.keys(authData).map((provider) => {
|
||||
if (authData[provider] === null) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -244,7 +243,7 @@ RestWrite.prototype.handleAuthDataValidation = function(authData) {
|
||||
if (!validateAuthData) {
|
||||
throw new Parse.Error(Parse.Error.UNSUPPORTED_SERVICE,
|
||||
'This authentication method is unsupported.');
|
||||
};
|
||||
}
|
||||
return validateAuthData(authData[provider]);
|
||||
});
|
||||
return Promise.all(validations);
|
||||
@@ -252,7 +251,7 @@ RestWrite.prototype.handleAuthDataValidation = function(authData) {
|
||||
|
||||
RestWrite.prototype.findUsersWithAuthData = function(authData) {
|
||||
let providers = Object.keys(authData);
|
||||
let query = providers.reduce((memo, provider) => {
|
||||
let query = providers.reduce((memo, provider) => {
|
||||
if (!authData[provider]) {
|
||||
return memo;
|
||||
}
|
||||
@@ -261,13 +260,13 @@ RestWrite.prototype.findUsersWithAuthData = function(authData) {
|
||||
query[queryKey] = authData[provider].id;
|
||||
memo.push(query);
|
||||
return memo;
|
||||
}, []).filter((q) => {
|
||||
}, []).filter((q) => {
|
||||
return typeof q !== 'undefined';
|
||||
});
|
||||
|
||||
let findPromise = Promise.resolve([]);
|
||||
if (query.length > 0) {
|
||||
findPromise = this.config.database.find(
|
||||
findPromise = this.config.database.find(
|
||||
this.className,
|
||||
{'$or': query}, {})
|
||||
}
|
||||
@@ -279,8 +278,8 @@ RestWrite.prototype.findUsersWithAuthData = function(authData) {
|
||||
RestWrite.prototype.handleAuthData = function(authData) {
|
||||
let results;
|
||||
return this.handleAuthDataValidation(authData).then(() => {
|
||||
return this.findUsersWithAuthData(authData);
|
||||
}).then((r) => {
|
||||
return this.findUsersWithAuthData(authData);
|
||||
}).then((r) => {
|
||||
results = r;
|
||||
if (results.length > 1) {
|
||||
// More than 1 user with the passed id's
|
||||
@@ -317,9 +316,9 @@ RestWrite.prototype.handleAuthData = function(authData) {
|
||||
// We have authData that is updated on login
|
||||
// that can happen when token are refreshed,
|
||||
// We should update the token and let the user in
|
||||
if (Object.keys(mutatedAuthData).length > 0) {
|
||||
if (Object.keys(mutatedAuthData).length > 0) {
|
||||
// Assign the new authData in the response
|
||||
Object.keys(mutatedAuthData).forEach((provider) => {
|
||||
Object.keys(mutatedAuthData).forEach((provider) => {
|
||||
this.response.response.authData[provider] = mutatedAuthData[provider];
|
||||
});
|
||||
// Run the DB update directly, as 'master'
|
||||
@@ -507,10 +506,10 @@ RestWrite.prototype.handleFollowup = function() {
|
||||
if (this.storage && this.storage['clearSessions'] && this.config.revokeSessionOnPasswordReset) {
|
||||
var sessionQuery = {
|
||||
user: {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: this.objectId()
|
||||
}
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: this.objectId()
|
||||
}
|
||||
};
|
||||
delete this.storage['clearSessions'];
|
||||
return this.config.database.destroy('_Session', sessionQuery)
|
||||
@@ -636,7 +635,7 @@ RestWrite.prototype.handleInstallation = function() {
|
||||
let orQueries = [];
|
||||
if (this.query && this.query.objectId) {
|
||||
orQueries.push({
|
||||
objectId: this.query.objectId
|
||||
objectId: this.query.objectId
|
||||
});
|
||||
}
|
||||
if (installationId) {
|
||||
@@ -652,12 +651,12 @@ RestWrite.prototype.handleInstallation = function() {
|
||||
return;
|
||||
}
|
||||
|
||||
promise = promise.then(() => {
|
||||
promise = promise.then(() => {
|
||||
return this.config.database.find('_Installation', {
|
||||
'$or': orQueries
|
||||
'$or': orQueries
|
||||
}, {});
|
||||
}).then((results) => {
|
||||
results.forEach((result) => {
|
||||
}).then((results) => {
|
||||
results.forEach((result) => {
|
||||
if (this.query && this.query.objectId && result.objectId == this.query.objectId) {
|
||||
objectIdMatch = result;
|
||||
}
|
||||
@@ -677,23 +676,23 @@ RestWrite.prototype.handleInstallation = function() {
|
||||
}
|
||||
if (this.data.installationId && objectIdMatch.installationId &&
|
||||
this.data.installationId !== objectIdMatch.installationId) {
|
||||
throw new Parse.Error(136,
|
||||
throw new Parse.Error(136,
|
||||
'installationId may not be changed in this ' +
|
||||
'operation');
|
||||
}
|
||||
if (this.data.deviceToken && objectIdMatch.deviceToken &&
|
||||
}
|
||||
if (this.data.deviceToken && objectIdMatch.deviceToken &&
|
||||
this.data.deviceToken !== objectIdMatch.deviceToken &&
|
||||
!this.data.installationId && !objectIdMatch.installationId) {
|
||||
throw new Parse.Error(136,
|
||||
throw new Parse.Error(136,
|
||||
'deviceToken may not be changed in this ' +
|
||||
'operation');
|
||||
}
|
||||
if (this.data.deviceType && this.data.deviceType &&
|
||||
}
|
||||
if (this.data.deviceType && this.data.deviceType &&
|
||||
this.data.deviceType !== objectIdMatch.deviceType) {
|
||||
throw new Parse.Error(136,
|
||||
throw new Parse.Error(136,
|
||||
'deviceType may not be changed in this ' +
|
||||
'operation');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.query && this.query.objectId && objectIdMatch) {
|
||||
@@ -748,7 +747,7 @@ RestWrite.prototype.handleInstallation = function() {
|
||||
// Exactly one device token match and it doesn't have an installation
|
||||
// ID. This is the one case where we want to merge with the existing
|
||||
// object.
|
||||
var delQuery = {objectId: idMatch.objectId};
|
||||
let delQuery = {objectId: idMatch.objectId};
|
||||
return this.config.database.destroy('_Installation', delQuery)
|
||||
.then(() => {
|
||||
return deviceTokenMatches[0]['objectId'];
|
||||
@@ -759,7 +758,7 @@ RestWrite.prototype.handleInstallation = function() {
|
||||
// We're setting the device token on an existing installation, so
|
||||
// we should try cleaning out old installations that match this
|
||||
// device token.
|
||||
var delQuery = {
|
||||
let delQuery = {
|
||||
'deviceToken': this.data.deviceToken,
|
||||
};
|
||||
// We have a unique install Id, use that to preserve
|
||||
@@ -969,7 +968,7 @@ RestWrite.prototype.objectId = function() {
|
||||
|
||||
// Returns a copy of the data and delete bad keys (_auth_data, _hashed_password...)
|
||||
RestWrite.prototype.sanitizedData = function() {
|
||||
let data = Object.keys(this.data).reduce((data, key) => {
|
||||
let data = Object.keys(this.data).reduce((data, key) => {
|
||||
// Regexp comes from Parse.Object.prototype.validate
|
||||
if (!(/^[A-Za-z][0-9A-Za-z_]*$/).test(key)) {
|
||||
delete data[key];
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
import rest from '../rest';
|
||||
import _ from 'lodash';
|
||||
import url from 'url';
|
||||
import Parse from 'parse/node';
|
||||
|
||||
const ALLOWED_GET_QUERY_KEYS = ['keys', 'include'];
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export class CloudCodeRouter extends PromiseRouter {
|
||||
let config = req.config;
|
||||
let jobs = triggers.getJobs(config.applicationId) || {};
|
||||
return Promise.resolve({
|
||||
response: Object.keys(jobs).map((jobName) => {
|
||||
response: Object.keys(jobs).map((jobName) => {
|
||||
return {
|
||||
jobName,
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@ export class FeaturesRouter extends PromiseRouter {
|
||||
};
|
||||
|
||||
return { response: {
|
||||
features: features,
|
||||
parseServerVersion: version,
|
||||
} };
|
||||
features: features,
|
||||
parseServerVersion: version,
|
||||
} };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import express from 'express';
|
||||
import BodyParser from 'body-parser';
|
||||
import * as Middlewares from '../middlewares';
|
||||
import { randomHexString } from '../cryptoUtils';
|
||||
import Parse from 'parse/node';
|
||||
import Config from '../Config';
|
||||
import mime from 'mime';
|
||||
|
||||
@@ -40,7 +40,7 @@ export class FilesRouter {
|
||||
if (isFileStreamable(req, filesController)) {
|
||||
filesController.getFileStream(config, filename).then((stream) => {
|
||||
handleFileStream(stream, req, res, contentType);
|
||||
}).catch((err) => {
|
||||
}).catch(() => {
|
||||
res.status(404);
|
||||
res.set('Content-Type', 'text/plain');
|
||||
res.end('File not found.');
|
||||
@@ -51,7 +51,7 @@ export class FilesRouter {
|
||||
res.set('Content-Type', contentType);
|
||||
res.set('Content-Length', data.length);
|
||||
res.end(data);
|
||||
}).catch((err) => {
|
||||
}).catch(() => {
|
||||
res.status(404);
|
||||
res.set('Content-Type', 'text/plain');
|
||||
res.end('File not found.');
|
||||
@@ -87,7 +87,7 @@ export class FilesRouter {
|
||||
res.status(201);
|
||||
res.set('Location', result.url);
|
||||
res.json(result);
|
||||
}).catch((err) => {
|
||||
}).catch(() => {
|
||||
next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Could not store file.'));
|
||||
});
|
||||
}
|
||||
@@ -98,7 +98,7 @@ export class FilesRouter {
|
||||
res.status(200);
|
||||
// TODO: return useful JSON here?
|
||||
res.end();
|
||||
}).catch((error) => {
|
||||
}).catch(() => {
|
||||
next(new Parse.Error(Parse.Error.FILE_DELETE_ERROR,
|
||||
'Could not delete file.'));
|
||||
});
|
||||
@@ -138,7 +138,7 @@ function handleFileStream(stream, req, res, contentType) {
|
||||
|
||||
if (!partialend) {
|
||||
if (((stream.length-1) - start) < (buffer_size)) {
|
||||
end = stream.length - 1;
|
||||
end = stream.length - 1;
|
||||
}else{
|
||||
end = start + (buffer_size);
|
||||
}
|
||||
@@ -159,8 +159,6 @@ function handleFileStream(stream, req, res, contentType) {
|
||||
stream.seek(start, function () {
|
||||
// get gridFile stream
|
||||
var gridFileStream = stream.stream(true);
|
||||
var ended = false;
|
||||
var bufferIdx = 0;
|
||||
var bufferAvail = 0;
|
||||
var range = (end - start) + 1;
|
||||
var totalbyteswanted = (end - start) + 1;
|
||||
@@ -176,7 +174,6 @@ function handleFileStream(stream, req, res, contentType) {
|
||||
res.write(buff);
|
||||
totalbyteswritten += buff.length;
|
||||
range -= buff.length;
|
||||
bufferIdx += buff.length;
|
||||
bufferAvail -= buff.length;
|
||||
}
|
||||
} else {
|
||||
@@ -185,7 +182,6 @@ function handleFileStream(stream, req, res, contentType) {
|
||||
const buffer = buff.slice(0,range);
|
||||
res.write(buffer);
|
||||
totalbyteswritten += buffer.length;
|
||||
bufferIdx += range;
|
||||
bufferAvail -= range;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// FunctionsRouter.js
|
||||
|
||||
var express = require('express'),
|
||||
Parse = require('parse/node').Parse,
|
||||
triggers = require('../triggers');
|
||||
var Parse = require('parse/node').Parse,
|
||||
triggers = require('../triggers');
|
||||
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
import { promiseEnforceMasterKeyAccess } from '../middlewares';
|
||||
@@ -12,9 +11,9 @@ import { logger } from '../logger';
|
||||
|
||||
function parseObject(obj) {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => {
|
||||
return parseObject(item);
|
||||
});
|
||||
return obj.map((item) => {
|
||||
return parseObject(item);
|
||||
});
|
||||
} else if (obj && obj.__type == 'Date') {
|
||||
return Object.assign(new Date(obj.iso), obj);
|
||||
} else if (obj && obj.__type == 'File') {
|
||||
@@ -63,10 +62,10 @@ export class FunctionsRouter extends PromiseRouter {
|
||||
error: jobHandler.setFailed.bind(jobHandler),
|
||||
message: jobHandler.setMessage.bind(jobHandler)
|
||||
}
|
||||
return jobHandler.setRunning(jobName, params).then((jobStatus) => {
|
||||
return jobHandler.setRunning(jobName, params).then((jobStatus) => {
|
||||
request.jobId = jobStatus.objectId
|
||||
// run the function async
|
||||
process.nextTick(() => {
|
||||
process.nextTick(() => {
|
||||
jobFunction(request, status);
|
||||
});
|
||||
return {
|
||||
@@ -131,10 +130,10 @@ export class FunctionsRouter extends PromiseRouter {
|
||||
const cleanResult = logger.truncateLogMessage(JSON.stringify(result.response.result));
|
||||
logger.info(`Ran cloud function ${functionName} for user ${userString} `
|
||||
+ `with:\n Input: ${cleanInput }\n Result: ${cleanResult }`, {
|
||||
functionName,
|
||||
params,
|
||||
user: userString,
|
||||
});
|
||||
functionName,
|
||||
params,
|
||||
user: userString,
|
||||
});
|
||||
resolve(result);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
@@ -144,11 +143,11 @@ export class FunctionsRouter extends PromiseRouter {
|
||||
logger.error(`Failed running cloud function ${functionName} for `
|
||||
+ `user ${userString} with:\n Input: ${cleanInput}\n Error: `
|
||||
+ JSON.stringify(error), {
|
||||
functionName,
|
||||
error,
|
||||
params,
|
||||
user: userString
|
||||
});
|
||||
functionName,
|
||||
error,
|
||||
params,
|
||||
user: userString
|
||||
});
|
||||
reject(error);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
|
||||
@@ -22,7 +22,7 @@ export class GlobalConfigRouter extends PromiseRouter {
|
||||
acc[`params.${key}`] = params[key];
|
||||
return acc;
|
||||
}, {});
|
||||
return req.config.database.update('_GlobalConfig', {objectId: "1"}, update, {upsert: true}).then(() => ({ response: { result: true } }));
|
||||
return req.config.database.update('_GlobalConfig', {objectId: "1"}, update, {upsert: true}).then(() => ({ response: { result: true } }));
|
||||
}
|
||||
|
||||
mountRoutes() {
|
||||
|
||||
@@ -5,15 +5,15 @@ import * as middleware from "../middlewares";
|
||||
export class HooksRouter extends PromiseRouter {
|
||||
createHook(aHook, config) {
|
||||
return config.hooksController.createHook(aHook).then( (hook) => ({response: hook}));
|
||||
};
|
||||
}
|
||||
|
||||
updateHook(aHook, config) {
|
||||
return config.hooksController.updateHook(aHook).then((hook) => ({response: hook}));
|
||||
};
|
||||
}
|
||||
|
||||
handlePost(req) {
|
||||
return this.createHook(req.body, req.config);
|
||||
};
|
||||
}
|
||||
|
||||
handleGetFunctions(req) {
|
||||
var hooksController = req.config.hooksController;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
var request = require("request");
|
||||
var rest = require("../rest");
|
||||
var Auth = require("../Auth");
|
||||
import Parse from 'parse/node';
|
||||
|
||||
// TODO move validation logic in IAPValidationController
|
||||
const IAP_SANDBOX_URL = "https://sandbox.itunes.apple.com/verifyReceipt";
|
||||
@@ -59,7 +59,7 @@ function getFileForProductIdentifier(productIdentifier, req) {
|
||||
|
||||
export class IAPValidationRouter extends PromiseRouter {
|
||||
|
||||
handleRequest(req) {
|
||||
handleRequest(req) {
|
||||
let receipt = req.body.receipt;
|
||||
const productIdentifier = req.body.productIdentifier;
|
||||
|
||||
@@ -81,11 +81,11 @@ export class IAPValidationRouter extends PromiseRouter {
|
||||
}
|
||||
|
||||
function successCallback() {
|
||||
return getFileForProductIdentifier(productIdentifier, req);
|
||||
};
|
||||
return getFileForProductIdentifier(productIdentifier, req);
|
||||
}
|
||||
|
||||
function errorCallback(error) {
|
||||
return Promise.resolve({response: appStoreError(error.status) });
|
||||
return Promise.resolve({response: appStoreError(error.status) });
|
||||
}
|
||||
|
||||
return validateWithAppStore(IAP_PRODUCTION_URL, receipt).then( () => {
|
||||
@@ -95,10 +95,10 @@ export class IAPValidationRouter extends PromiseRouter {
|
||||
}, (error) => {
|
||||
if (error.status == 21007) {
|
||||
return validateWithAppStore(IAP_SANDBOX_URL, receipt).then( () => {
|
||||
return successCallback();
|
||||
}, (error) => {
|
||||
return errorCallback(error);
|
||||
}
|
||||
return successCallback();
|
||||
}, (error) => {
|
||||
return errorCallback(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
import UserController from '../Controllers/UserController';
|
||||
import Config from '../Config';
|
||||
import express from 'express';
|
||||
import path from 'path';
|
||||
@@ -46,7 +45,7 @@ export class PublicAPIRouter extends PromiseRouter {
|
||||
});
|
||||
}
|
||||
// Should we keep the file in memory or leave like that?
|
||||
fs.readFile(path.resolve(views, "choose_password"), 'utf-8', (err, data) => {
|
||||
fs.readFile(path.resolve(views, "choose_password"), 'utf-8', (err, data) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
@@ -72,7 +71,7 @@ export class PublicAPIRouter extends PromiseRouter {
|
||||
return this.invalidLink(req);
|
||||
}
|
||||
|
||||
return config.userController.checkResetTokenValidity(username, token).then( (user) => {
|
||||
return config.userController.checkResetTokenValidity(username, token).then( () => {
|
||||
let params = qs.stringify({token, id: config.applicationId, username, app: config.appName, });
|
||||
return Promise.resolve({
|
||||
status: 302,
|
||||
@@ -101,7 +100,7 @@ export class PublicAPIRouter extends PromiseRouter {
|
||||
return this.invalidLink(req);
|
||||
}
|
||||
|
||||
return config.userController.updatePassword(username, token, new_password).then((result) => {
|
||||
return config.userController.updatePassword(username, token, new_password).then(() => {
|
||||
let params = qs.stringify({username: username});
|
||||
return Promise.resolve({
|
||||
status: 302,
|
||||
@@ -119,8 +118,8 @@ export class PublicAPIRouter extends PromiseRouter {
|
||||
|
||||
invalidLink(req) {
|
||||
return Promise.resolve({
|
||||
status: 302,
|
||||
location: req.config.invalidLinkURL
|
||||
status: 302,
|
||||
location: req.config.invalidLinkURL
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
|
||||
import ClassesRouter from './ClassesRouter';
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
import rest from '../rest';
|
||||
|
||||
export class RolesRouter extends ClassesRouter {
|
||||
handleFind(req) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// schemas.js
|
||||
|
||||
var express = require('express'),
|
||||
Parse = require('parse/node').Parse,
|
||||
var Parse = require('parse/node').Parse,
|
||||
SchemaController = require('../Controllers/SchemaController');
|
||||
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import ClassesRouter from './ClassesRouter';
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
import Parse from 'parse/node';
|
||||
import rest from '../rest';
|
||||
import Auth from '../Auth';
|
||||
import RestWrite from '../RestWrite';
|
||||
@@ -75,14 +75,14 @@ export class SessionsRouter extends ClassesRouter {
|
||||
expiresAt: Parse._encode(expiresAt)
|
||||
};
|
||||
const create = new RestWrite(config, masterAuth, '_Session', null, sessionData);
|
||||
return create.execute().then(() => {
|
||||
return create.execute().then(() => {
|
||||
// delete the session token, use the db to skip beforeSave
|
||||
return config.database.update('_User', {
|
||||
objectId: user.id
|
||||
}, {
|
||||
sessionToken: {__op: 'Delete'}
|
||||
});
|
||||
}).then((res) => {
|
||||
}).then(() => {
|
||||
return Promise.resolve({ response: sessionData });
|
||||
});
|
||||
}
|
||||
@@ -94,7 +94,7 @@ export class SessionsRouter extends ClassesRouter {
|
||||
this.route('POST', '/sessions', req => { return this.handleCreate(req); });
|
||||
this.route('PUT', '/sessions/:objectId', req => { return this.handleUpdate(req); });
|
||||
this.route('DELETE', '/sessions/:objectId', req => { return this.handleDelete(req); });
|
||||
this.route('POST', '/upgradeToRevocableSession', req => { return this.handleUpdateToRevocableSession(req); })
|
||||
this.route('POST', '/upgradeToRevocableSession', req => { return this.handleUpdateToRevocableSession(req); })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
// These methods handle the User-related routes.
|
||||
|
||||
import deepcopy from 'deepcopy';
|
||||
import Parse from 'parse/node';
|
||||
import Config from '../Config';
|
||||
import AccountLockout from '../AccountLockout';
|
||||
import ClassesRouter from './ClassesRouter';
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
import rest from '../rest';
|
||||
import Auth from '../Auth';
|
||||
import passwordCrypto from '../password';
|
||||
import RestWrite from '../RestWrite';
|
||||
let cryptoUtils = require('../cryptoUtils');
|
||||
let triggers = require('../triggers');
|
||||
|
||||
export class UsersRouter extends ClassesRouter {
|
||||
handleFind(req) {
|
||||
@@ -216,10 +215,10 @@ export class UsersRouter extends ClassesRouter {
|
||||
throw new Parse.Error(Parse.Error.INVALID_EMAIL_ADDRESS, 'you must provide a valid email string');
|
||||
}
|
||||
let userController = req.config.userController;
|
||||
return userController.sendPasswordResetEmail(email).then(token => {
|
||||
return Promise.resolve({
|
||||
response: {}
|
||||
});
|
||||
return userController.sendPasswordResetEmail(email).then(() => {
|
||||
return Promise.resolve({
|
||||
response: {}
|
||||
});
|
||||
}, err => {
|
||||
if (err.code === Parse.Error.OBJECT_NOT_FOUND) {
|
||||
throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, `No user found with email ${email}.`);
|
||||
|
||||
@@ -5,7 +5,7 @@ const PUSH_STATUS_COLLECTION = '_PushStatus';
|
||||
const JOB_STATUS_COLLECTION = '_JobStatus';
|
||||
|
||||
export function flatten(array) {
|
||||
return array.reduce((memo, element) => {
|
||||
return array.reduce((memo, element) => {
|
||||
if (Array.isArray(element)) {
|
||||
memo = memo.concat(flatten(element));
|
||||
} else {
|
||||
@@ -19,8 +19,8 @@ function statusHandler(className, database) {
|
||||
let lastPromise = Promise.resolve();
|
||||
|
||||
function create(object) {
|
||||
lastPromise = lastPromise.then(() => {
|
||||
return database.create(className, object).then(() => {
|
||||
lastPromise = lastPromise.then(() => {
|
||||
return database.create(className, object).then(() => {
|
||||
return Promise.resolve(object);
|
||||
});
|
||||
});
|
||||
@@ -28,7 +28,7 @@ function statusHandler(className, database) {
|
||||
}
|
||||
|
||||
function update(where, object) {
|
||||
lastPromise = lastPromise.then(() => {
|
||||
lastPromise = lastPromise.then(() => {
|
||||
return database.update(className, where, object);
|
||||
});
|
||||
return lastPromise;
|
||||
@@ -44,7 +44,6 @@ export function jobStatusHandler(config) {
|
||||
let jobStatus;
|
||||
let objectId = newObjectId();
|
||||
let database = config.database;
|
||||
let lastPromise = Promise.resolve();
|
||||
let handler = statusHandler(JOB_STATUS_COLLECTION, database);
|
||||
let setRunning = function(jobName, params) {
|
||||
let now = new Date();
|
||||
@@ -138,7 +137,7 @@ export function pushStatusHandler(config) {
|
||||
|
||||
let setRunning = function(installations) {
|
||||
logger.verbose('sending push to %d installations', installations.length);
|
||||
return handler.update({status:"pending", objectId: objectId},
|
||||
return handler.update({status:"pending", objectId: objectId},
|
||||
{status: "running", updatedAt: new Date() });
|
||||
}
|
||||
|
||||
@@ -151,7 +150,7 @@ export function pushStatusHandler(config) {
|
||||
};
|
||||
if (Array.isArray(results)) {
|
||||
results = flatten(results);
|
||||
results.reduce((memo, result) => {
|
||||
results.reduce((memo, result) => {
|
||||
// Cannot handle that
|
||||
if (!result || !result.device || !result.device.deviceType) {
|
||||
return memo;
|
||||
|
||||
@@ -6,11 +6,11 @@ export function destroyAllDataPermanently() {
|
||||
throw 'Only supported in test environment';
|
||||
}
|
||||
return Promise.all(Object.keys(AppCache.cache).map(appId => {
|
||||
const app = AppCache.get(appId);
|
||||
if (app.databaseController) {
|
||||
return app.databaseController.deleteEverything();
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}));
|
||||
const app = AppCache.get(appId);
|
||||
if (app.databaseController) {
|
||||
return app.databaseController.deleteEverything();
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ OAuth.prototype.send = function(method, path, params, body){
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', function(e) {
|
||||
}).on('error', function() {
|
||||
reject('Failed to make an OAuth request');
|
||||
});
|
||||
if (request.body) {
|
||||
httpRequest.write(request.body);
|
||||
httpRequest.write(request.body);
|
||||
}
|
||||
httpRequest.end();
|
||||
});
|
||||
@@ -63,11 +63,11 @@ OAuth.prototype.buildRequest = function(method, path, params, body) {
|
||||
}
|
||||
|
||||
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) {
|
||||
return this.send("POST", path, params, body);
|
||||
return this.send("POST", path, params, body);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -115,29 +115,27 @@ OAuth.version = "1.0";
|
||||
Generate a nonce
|
||||
*/
|
||||
OAuth.nonce = function(){
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for( var i=0; i < 30; i++ )
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
for( var i=0; i < 30; i++ )
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
|
||||
return text;
|
||||
return text;
|
||||
}
|
||||
|
||||
OAuth.buildParameterString = function(obj){
|
||||
var result = {};
|
||||
|
||||
// Sort keys and encode values
|
||||
if (obj) {
|
||||
var keys = Object.keys(obj).sort();
|
||||
if (obj) {
|
||||
var keys = Object.keys(obj).sort();
|
||||
|
||||
// Map key=value, join them by &
|
||||
return keys.map(function(key){
|
||||
return key + "=" + OAuth.encode(obj[key]);
|
||||
}).join("&");
|
||||
}
|
||||
return keys.map(function(key){
|
||||
return key + "=" + OAuth.encode(obj[key]);
|
||||
}).join("&");
|
||||
}
|
||||
|
||||
return "";
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -145,81 +143,81 @@ OAuth.buildParameterString = function(obj){
|
||||
*/
|
||||
|
||||
OAuth.buildSignatureString = function(method, url, parameters){
|
||||
return [method.toUpperCase(), OAuth.encode(url), OAuth.encode(parameters)].join("&");
|
||||
return [method.toUpperCase(), OAuth.encode(url), OAuth.encode(parameters)].join("&");
|
||||
}
|
||||
|
||||
/*
|
||||
Retuns encoded HMAC-SHA1 from key and text
|
||||
*/
|
||||
OAuth.signature = function(text, key){
|
||||
crypto = require("crypto");
|
||||
return OAuth.encode(crypto.createHmac('sha1', key).update(text).digest('base64'));
|
||||
crypto = require("crypto");
|
||||
return OAuth.encode(crypto.createHmac('sha1', key).update(text).digest('base64'));
|
||||
}
|
||||
|
||||
OAuth.signRequest = function(request, oauth_parameters, consumer_secret, auth_token_secret){
|
||||
oauth_parameters = oauth_parameters || {};
|
||||
oauth_parameters = oauth_parameters || {};
|
||||
|
||||
// Set default values
|
||||
if (!oauth_parameters.oauth_nonce) {
|
||||
oauth_parameters.oauth_nonce = OAuth.nonce();
|
||||
}
|
||||
if (!oauth_parameters.oauth_timestamp) {
|
||||
oauth_parameters.oauth_timestamp = Math.floor(new Date().getTime()/1000);
|
||||
}
|
||||
if (!oauth_parameters.oauth_signature_method) {
|
||||
oauth_parameters.oauth_signature_method = OAuth.signatureMethod;
|
||||
}
|
||||
if (!oauth_parameters.oauth_version) {
|
||||
oauth_parameters.oauth_version = OAuth.version;
|
||||
}
|
||||
if (!oauth_parameters.oauth_nonce) {
|
||||
oauth_parameters.oauth_nonce = OAuth.nonce();
|
||||
}
|
||||
if (!oauth_parameters.oauth_timestamp) {
|
||||
oauth_parameters.oauth_timestamp = Math.floor(new Date().getTime()/1000);
|
||||
}
|
||||
if (!oauth_parameters.oauth_signature_method) {
|
||||
oauth_parameters.oauth_signature_method = OAuth.signatureMethod;
|
||||
}
|
||||
if (!oauth_parameters.oauth_version) {
|
||||
oauth_parameters.oauth_version = OAuth.version;
|
||||
}
|
||||
|
||||
if(!auth_token_secret){
|
||||
auth_token_secret="";
|
||||
}
|
||||
if(!auth_token_secret){
|
||||
auth_token_secret="";
|
||||
}
|
||||
// Force GET method if unset
|
||||
if (!request.method) {
|
||||
request.method = "GET"
|
||||
}
|
||||
if (!request.method) {
|
||||
request.method = "GET"
|
||||
}
|
||||
|
||||
// Collect all the parameters in one signatureParameters object
|
||||
var signatureParams = {};
|
||||
var parametersToMerge = [request.params, request.body, oauth_parameters];
|
||||
for(var i in parametersToMerge) {
|
||||
var parameters = parametersToMerge[i];
|
||||
for(var k in parameters) {
|
||||
signatureParams[k] = parameters[k];
|
||||
}
|
||||
}
|
||||
var signatureParams = {};
|
||||
var parametersToMerge = [request.params, request.body, oauth_parameters];
|
||||
for(var i in parametersToMerge) {
|
||||
var parameters = parametersToMerge[i];
|
||||
for(var k in parameters) {
|
||||
signatureParams[k] = parameters[k];
|
||||
}
|
||||
}
|
||||
|
||||
// Create a string based on the parameters
|
||||
var parameterString = OAuth.buildParameterString(signatureParams);
|
||||
var parameterString = OAuth.buildParameterString(signatureParams);
|
||||
|
||||
// Build the signature string
|
||||
var url = "https://"+request.host+""+request.path;
|
||||
var url = "https://"+request.host+""+request.path;
|
||||
|
||||
var signatureString = OAuth.buildSignatureString(request.method, url, parameterString);
|
||||
var signatureString = OAuth.buildSignatureString(request.method, url, parameterString);
|
||||
// Hash the signature string
|
||||
var signatureKey = [OAuth.encode(consumer_secret), OAuth.encode(auth_token_secret)].join("&");
|
||||
var signatureKey = [OAuth.encode(consumer_secret), OAuth.encode(auth_token_secret)].join("&");
|
||||
|
||||
var signature = OAuth.signature(signatureString, signatureKey);
|
||||
var signature = OAuth.signature(signatureString, signatureKey);
|
||||
|
||||
// Set the signature in the params
|
||||
oauth_parameters.oauth_signature = signature;
|
||||
if(!request.headers){
|
||||
request.headers = {};
|
||||
}
|
||||
oauth_parameters.oauth_signature = signature;
|
||||
if(!request.headers){
|
||||
request.headers = {};
|
||||
}
|
||||
|
||||
// Set the authorization header
|
||||
var signature = Object.keys(oauth_parameters).sort().map(function(key){
|
||||
var value = oauth_parameters[key];
|
||||
return key+'="'+value+'"';
|
||||
}).join(", ")
|
||||
var authHeader = Object.keys(oauth_parameters).sort().map(function(key){
|
||||
var value = oauth_parameters[key];
|
||||
return key+'="'+value+'"';
|
||||
}).join(", ")
|
||||
|
||||
request.headers.Authorization = 'OAuth ' + signature;
|
||||
request.headers.Authorization = 'OAuth ' + authHeader;
|
||||
|
||||
// Set the content type header
|
||||
request.headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||
return request;
|
||||
request.headers["Content-Type"] = "application/x-www-form-urlencoded";
|
||||
return request;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ function graphRequest(path) {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', function(e) {
|
||||
}).on('error', function() {
|
||||
reject('Failed to validate this access token with Facebook.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@ function request(path, access_token) {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', function(e) {
|
||||
}).on('error', function() {
|
||||
reject('Failed to validate this access token with Github.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,7 +58,7 @@ function request(path) {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', function(e) {
|
||||
}).on('error', function() {
|
||||
reject('Failed to validate this access token with Google.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -79,7 +79,7 @@ module.exports = function(oauthOptions = {}, enableAnonymousUsers = true) {
|
||||
if (optionalProvider.module) {
|
||||
validateAuthData = require(optionalProvider.module).validateAuthData;
|
||||
validateAppId = require(optionalProvider.module).validateAppId;
|
||||
};
|
||||
}
|
||||
|
||||
if (optionalProvider.validateAuthData) {
|
||||
validateAuthData = optionalProvider.validateAuthData;
|
||||
@@ -94,7 +94,7 @@ module.exports = function(oauthOptions = {}, enableAnonymousUsers = true) {
|
||||
}
|
||||
|
||||
return function(authData) {
|
||||
return validateAuthData(authData, optionalProvider).then(() => {
|
||||
return validateAuthData(authData, optionalProvider).then(() => {
|
||||
if (appIds) {
|
||||
return validateAppId(appIds, authData, optionalProvider);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ function request(path) {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', function(e) {
|
||||
}).on('error', function() {
|
||||
reject('Failed to validate this access token with Instagram.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ function request(host, access_token) {
|
||||
res.on('end', function () {
|
||||
resolve(JSON.parse(data));
|
||||
});
|
||||
}).on('error', function(e) {
|
||||
}).on('error', function() {
|
||||
reject('Failed to validate this access token with Janrain capture.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,7 +41,7 @@ function request(api_key, auth_token) {
|
||||
}
|
||||
};
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
return new Promise(function (resolve) {
|
||||
// Create the post request.
|
||||
var post_req = https.request(post_options, function (res) {
|
||||
var data = '';
|
||||
|
||||
@@ -45,7 +45,7 @@ function request(path, access_token, is_mobile_sdk) {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', function(e) {
|
||||
}).on('error', function() {
|
||||
reject('Failed to validate this access token with Linkedin.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ function request(path, access_token) {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', function(e) {
|
||||
}).on('error', function() {
|
||||
reject('Failed to validate this access token with Meetup.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ function validateAuthData(authData) {
|
||||
}
|
||||
|
||||
// Returns a promise that fulfills if this app id is valid.
|
||||
function validateAppId(appIds, authData) {
|
||||
function validateAppId() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ function graphRequest(path) {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', function (e) {
|
||||
}).on('error', function () {
|
||||
reject('Failed to validate this access token with qq.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ function request(path, access_token) {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', function(e) {
|
||||
}).on('error', function() {
|
||||
reject('Failed to validate this access token with Spotify.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ function handleMultipleConfigurations(authData, options) {
|
||||
logger.error('Twitter Auth', 'Multiple twitter configurations are available, by no consumer_key was sent by the client.');
|
||||
throw new Parse.Error(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;
|
||||
});
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ function request(host, path) {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', function (e) {
|
||||
}).on('error', function () {
|
||||
reject('Failed to validate this access token with Vk.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ function validateAuthData(authData) {
|
||||
}
|
||||
|
||||
// Returns a promise that fulfills if this app id is valid.
|
||||
function validateAppId(appIds, authData) {
|
||||
function validateAppId() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ function graphRequest(path) {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', function (e) {
|
||||
}).on('error', function () {
|
||||
reject('Failed to validate this access token with weixin.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ function validateAuthData(authData) {
|
||||
}
|
||||
|
||||
// Returns a promise that fulfills if this app id is valid.
|
||||
function validateAppId(appIds, authData) {
|
||||
function validateAppId() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ function graphRequest(access_token) {
|
||||
data = JSON.parse(data);
|
||||
resolve(data);
|
||||
});
|
||||
res.on('error', function (err) {
|
||||
res.on('error', function () {
|
||||
reject('Failed to validate this access token with weibo.');
|
||||
});
|
||||
});
|
||||
req.on('error', function (e){
|
||||
req.on('error', function () {
|
||||
reject('Failed to validate this access token with weibo.');
|
||||
});
|
||||
req.write(postData);
|
||||
|
||||
@@ -13,7 +13,7 @@ function mountOnto(router) {
|
||||
// Returns a promise for a {response} object.
|
||||
// TODO: pass along auth correctly
|
||||
function handleBatch(router, req) {
|
||||
if (!req.body.requests instanceof Array) {
|
||||
if (!Array.isArray(req.body.requests)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON,
|
||||
'requests must be an array');
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
numberParser,
|
||||
numberOrBoolParser,
|
||||
objectParser,
|
||||
arrayParser,
|
||||
moduleOrObjectParser,
|
||||
booleanParser,
|
||||
nullParser
|
||||
numberParser
|
||||
} from '../utils/parsers';
|
||||
|
||||
|
||||
@@ -40,9 +34,9 @@ export default {
|
||||
help: "Optional. This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE. Defaults to INFO.",
|
||||
},
|
||||
"port": {
|
||||
env: "PORT",
|
||||
help: "The port to run the ParseServer. defaults to 1337.",
|
||||
default: 1337,
|
||||
action: numberParser("port")
|
||||
env: "PORT",
|
||||
help: "The port to run the ParseServer. defaults to 1337.",
|
||||
default: 1337,
|
||||
action: numberParser("port")
|
||||
},
|
||||
};
|
||||
|
||||
@@ -21,10 +21,10 @@ export default {
|
||||
required: true
|
||||
},
|
||||
"port": {
|
||||
env: "PORT",
|
||||
help: "The port to run the ParseServer. defaults to 1337.",
|
||||
default: 1337,
|
||||
action: numberParser("port")
|
||||
env: "PORT",
|
||||
help: "The port to run the ParseServer. defaults to 1337.",
|
||||
default: 1337,
|
||||
action: numberParser("port")
|
||||
},
|
||||
"databaseURI": {
|
||||
env: "PARSE_SERVER_DATABASE_URI",
|
||||
@@ -150,11 +150,6 @@ export default {
|
||||
help: "Adapter module for the logging sub-system",
|
||||
action: moduleOrObjectParser
|
||||
},
|
||||
"liveQuery": {
|
||||
env: "PARSE_SERVER_LIVE_QUERY_OPTIONS",
|
||||
help: "liveQuery options",
|
||||
action: objectParser
|
||||
},
|
||||
"customPages": {
|
||||
env: "PARSE_SERVER_CUSTOM_PAGES",
|
||||
help: "custom pages for password validation and reset",
|
||||
@@ -210,7 +205,8 @@ export default {
|
||||
help: "Run with cluster, optionally set the number of processes default to os.cpus().length",
|
||||
action: numberOrBoolParser("cluster")
|
||||
},
|
||||
"liveQuery": {
|
||||
"liveQuery": {
|
||||
env: "PARSE_SERVER_LIVE_QUERY_OPTIONS",
|
||||
help: "parse-server's LiveQuery configuration object",
|
||||
action: objectParser
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import path from 'path';
|
||||
/* eslint-disable no-console */
|
||||
import express from 'express';
|
||||
import { ParseServer } from '../index';
|
||||
import definitions from './definitions/parse-server';
|
||||
@@ -65,7 +65,7 @@ function startServer(options, callback) {
|
||||
for (const socketId in sockets) {
|
||||
try {
|
||||
sockets[socketId].destroy();
|
||||
} catch (e) { }
|
||||
} catch (e) { /* */ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,12 +115,12 @@ runner({
|
||||
for(var i = 0; i < numCPUs; i++) {
|
||||
cluster.fork();
|
||||
}
|
||||
cluster.on('exit', (worker, code, signal) => {
|
||||
console.log(`worker ${worker.process.pid} died... Restarting`);
|
||||
cluster.on('exit', (worker, code) => {
|
||||
console.log(`worker ${worker.process.pid} died (${code})... Restarting`);
|
||||
cluster.fork();
|
||||
});
|
||||
} else {
|
||||
startServer(options, () => {
|
||||
startServer(options, () => {
|
||||
console.log('['+process.pid+'] parse-server running on '+options.serverURL);
|
||||
});
|
||||
}
|
||||
@@ -134,5 +134,4 @@ runner({
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
/* eslint-enable no-console */
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-console */
|
||||
import { Command } from 'commander';
|
||||
import path from 'path';
|
||||
let _definitions;
|
||||
@@ -27,18 +28,18 @@ Command.prototype.loadDefinitions = function(definitions) {
|
||||
}, {});
|
||||
|
||||
_reverseDefinitions = Object.keys(definitions).reduce((object, key) => {
|
||||
let value = definitions[key];
|
||||
if (typeof value == "object") {
|
||||
value = value.env;
|
||||
}
|
||||
if (value) {
|
||||
object[value] = key;
|
||||
}
|
||||
return object;
|
||||
}, {});
|
||||
let value = definitions[key];
|
||||
if (typeof value == "object") {
|
||||
value = value.env;
|
||||
}
|
||||
if (value) {
|
||||
object[value] = key;
|
||||
}
|
||||
return object;
|
||||
}, {});
|
||||
|
||||
/* istanbul ignore next */
|
||||
this.on('--help', function(){
|
||||
this.on('--help', function(){
|
||||
console.log(' Configure From Environment:');
|
||||
console.log('');
|
||||
Object.keys(_reverseDefinitions).forEach((key) => {
|
||||
@@ -93,9 +94,9 @@ function parseConfigFile(program) {
|
||||
|
||||
Command.prototype.setValuesIfNeeded = function(options) {
|
||||
Object.keys(options).forEach((key) => {
|
||||
if (!this[key]) {
|
||||
this[key] = options[key];
|
||||
}
|
||||
if (!this[key]) {
|
||||
this[key] = options[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -124,3 +125,4 @@ Command.prototype.getOptions = function() {
|
||||
}
|
||||
|
||||
export default new Command();
|
||||
/* eslint-enable no-console */
|
||||
|
||||
@@ -46,7 +46,7 @@ export function moduleOrObjectParser(opt) {
|
||||
}
|
||||
try {
|
||||
return JSON.parse(opt);
|
||||
} catch(e) {}
|
||||
} catch(e) { /* */ }
|
||||
return opt;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
import program from './commander';
|
||||
import { mergeWithOptions } from './commander';
|
||||
|
||||
function logStartupOptions(options) {
|
||||
for (let key in options) {
|
||||
@@ -11,7 +10,9 @@ function logStartupOptions(options) {
|
||||
if (typeof value === 'object') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
/* eslint-disable no-console */
|
||||
console.log(`${key}: ${value}`);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export default class HTTPResponse {
|
||||
_data = body;
|
||||
}
|
||||
|
||||
let getText = () => {
|
||||
let getText = () => {
|
||||
if (!_text && this.buffer) {
|
||||
_text = this.buffer.toString('utf-8');
|
||||
} else if (!_text && _data) {
|
||||
@@ -26,8 +26,8 @@ export default class HTTPResponse {
|
||||
let getData = () => {
|
||||
if (!_data) {
|
||||
try {
|
||||
_data = JSON.parse(getText());
|
||||
} catch (e) {}
|
||||
_data = JSON.parse(getText());
|
||||
} catch (e) { /* */ }
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {nullParser} from './cli/utils/parsers';
|
||||
|
||||
let logsFolder = (() => {
|
||||
let logsFolder = (() => {
|
||||
let folder = './logs/';
|
||||
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
|
||||
folder = './test_logs/'
|
||||
@@ -11,7 +11,7 @@ let logsFolder = (() => {
|
||||
return folder;
|
||||
})();
|
||||
|
||||
let { verbose, level } = (() => {
|
||||
let { verbose, level } = (() => {
|
||||
let verbose = process.env.VERBOSE ? true : false;
|
||||
return { verbose, level: verbose ? 'verbose' : undefined }
|
||||
})();
|
||||
|
||||
@@ -142,21 +142,21 @@ export function handleParseHeaders(req, res, next) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Promise.resolve().then(() => {
|
||||
return Promise.resolve().then(() => {
|
||||
// handle the upgradeToRevocableSession path on it's own
|
||||
if (info.sessionToken &&
|
||||
req.url === '/upgradeToRevocableSession' &&
|
||||
info.sessionToken.indexOf('r:') != 0) {
|
||||
return auth.getAuthForLegacySessionToken({ config: req.config, installationId: info.installationId, sessionToken: info.sessionToken })
|
||||
return auth.getAuthForLegacySessionToken({ config: req.config, installationId: info.installationId, sessionToken: info.sessionToken })
|
||||
} else {
|
||||
return auth.getAuthForSessionToken({ config: req.config, installationId: info.installationId, sessionToken: info.sessionToken })
|
||||
return auth.getAuthForSessionToken({ config: req.config, installationId: info.installationId, sessionToken: info.sessionToken })
|
||||
}
|
||||
}).then((auth) => {
|
||||
if (auth) {
|
||||
req.auth = auth;
|
||||
next();
|
||||
}
|
||||
})
|
||||
if (auth) {
|
||||
req.auth = auth;
|
||||
next();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if(error instanceof Parse.Error) {
|
||||
next(error);
|
||||
@@ -221,7 +221,7 @@ export function allowCrossDomain(req, res, next) {
|
||||
else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function allowMethodOverride(req, res, next) {
|
||||
if (req.method === 'POST' && req.body._method) {
|
||||
@@ -230,7 +230,7 @@ export function allowMethodOverride(req, res, next) {
|
||||
delete req.body._method;
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
export function handleParseErrors(err, req, res, next) {
|
||||
// TODO: Add logging as those errors won't make it to the PromiseRouter
|
||||
@@ -258,10 +258,10 @@ export function handleParseErrors(err, req, res, next) {
|
||||
log.error('Uncaught internal server error.', err, err.stack);
|
||||
res.status(500);
|
||||
res.json({code: Parse.Error.INTERNAL_SERVER_ERROR,
|
||||
message: 'Internal server error.'});
|
||||
message: 'Internal server error.'});
|
||||
}
|
||||
next(err);
|
||||
};
|
||||
}
|
||||
|
||||
export function enforceMasterKeyAccess(req, res, next) {
|
||||
if (!req.auth.isMaster) {
|
||||
|
||||
@@ -4,7 +4,7 @@ var bcrypt = require('bcryptjs');
|
||||
|
||||
try {
|
||||
bcrypt = require('bcrypt');
|
||||
} catch(e) {}
|
||||
} catch(e) { /* */ }
|
||||
|
||||
// Returns a promise for a hashed password string.
|
||||
function hash(password) {
|
||||
|
||||
@@ -17,7 +17,7 @@ var triggers = require('./triggers');
|
||||
// Returns a promise for an object with optional keys 'results' and 'count'.
|
||||
function find(config, auth, className, restWhere, restOptions, clientSDK) {
|
||||
enforceRoleSecurity('find', className, auth);
|
||||
return triggers.maybeRunQueryTrigger(triggers.Types.beforeFind, className, restWhere, restOptions, config, auth).then((result) => {
|
||||
return triggers.maybeRunQueryTrigger(triggers.Types.beforeFind, className, restWhere, restOptions, config, auth).then((result) => {
|
||||
restWhere = result.restWhere || restWhere;
|
||||
restOptions = result.restOptions || restOptions;
|
||||
let query = new RestQuery(config, auth, className, restWhere, restOptions, clientSDK);
|
||||
@@ -33,7 +33,7 @@ const get = (config, auth, className, objectId, restOptions, clientSDK) => {
|
||||
}
|
||||
|
||||
// Returns a promise that doesn't resolve to any useful value.
|
||||
function del(config, auth, className, objectId, clientSDK) {
|
||||
function del(config, auth, className, objectId) {
|
||||
if (typeof objectId !== 'string') {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON,
|
||||
'bad objectId');
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// triggers.js
|
||||
import Parse from 'parse/node';
|
||||
import AppCache from './cache';
|
||||
import { logger } from './logger';
|
||||
|
||||
export const Types = {
|
||||
@@ -51,18 +50,18 @@ export function addTrigger(type, className, handler, applicationId) {
|
||||
}
|
||||
|
||||
export function removeFunction(functionName, applicationId) {
|
||||
applicationId = applicationId || Parse.applicationId;
|
||||
delete _triggerStore[applicationId].Functions[functionName]
|
||||
applicationId = applicationId || Parse.applicationId;
|
||||
delete _triggerStore[applicationId].Functions[functionName]
|
||||
}
|
||||
|
||||
export function removeJob(jobName, applicationId) {
|
||||
applicationId = applicationId || Parse.applicationId;
|
||||
delete _triggerStore[applicationId].Jobs[jobName]
|
||||
applicationId = applicationId || Parse.applicationId;
|
||||
delete _triggerStore[applicationId].Jobs[jobName]
|
||||
}
|
||||
|
||||
export function removeTrigger(type, className, applicationId) {
|
||||
applicationId = applicationId || Parse.applicationId;
|
||||
delete _triggerStore[applicationId].Triggers[type][className]
|
||||
applicationId = applicationId || Parse.applicationId;
|
||||
delete _triggerStore[applicationId].Triggers[type][className]
|
||||
}
|
||||
|
||||
export function _unregister(appId,category,className,type) {
|
||||
@@ -90,7 +89,7 @@ export function getTrigger(className, triggerType, applicationId) {
|
||||
return manager.Triggers[triggerType][className];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export function triggerExists(className: string, type: string, applicationId: string): boolean {
|
||||
return (getTrigger(className, type, applicationId) != undefined);
|
||||
@@ -100,7 +99,7 @@ export function getFunction(functionName, applicationId) {
|
||||
var manager = _triggerStore[applicationId];
|
||||
if (manager && manager.Functions) {
|
||||
return manager.Functions[functionName];
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -108,7 +107,7 @@ export function getJob(jobName, applicationId) {
|
||||
var manager = _triggerStore[applicationId];
|
||||
if (manager && manager.Jobs) {
|
||||
return manager.Jobs[jobName];
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -116,7 +115,7 @@ export function getJobs(applicationId) {
|
||||
var manager = _triggerStore[applicationId];
|
||||
if (manager && manager.Jobs) {
|
||||
return manager.Jobs;
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -125,7 +124,7 @@ export function getValidator(functionName, applicationId) {
|
||||
var manager = _triggerStore[applicationId];
|
||||
if (manager && manager.Validators) {
|
||||
return manager.Validators[functionName];
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -215,7 +214,7 @@ export function getResponseObject(request, resolve, reject) {
|
||||
return reject(scriptError);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function userIdForLog(auth) {
|
||||
return (auth && auth.user) ? auth.user.id : undefined;
|
||||
@@ -258,12 +257,12 @@ export function maybeRunAfterFindTrigger(triggerType, auth, className, objects,
|
||||
}
|
||||
const request = getRequestObject(triggerType, auth, null, null, config);
|
||||
const response = getResponseObject(request,
|
||||
object => {
|
||||
object => {
|
||||
resolve(object);
|
||||
},
|
||||
error => {
|
||||
error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
logTriggerSuccessBeforeHook(triggerType, className, 'AfterFind', JSON.stringify(objects), auth);
|
||||
request.objects = objects.map(object => {
|
||||
//setting the class name to transform into parse object
|
||||
@@ -297,23 +296,23 @@ export function maybeRunQueryTrigger(triggerType, className, restWhere, restOpti
|
||||
|
||||
let parseQuery = new Parse.Query(className);
|
||||
if (restWhere) {
|
||||
parseQuery._where = restWhere;
|
||||
parseQuery._where = restWhere;
|
||||
}
|
||||
if (restOptions) {
|
||||
if (restOptions.include && restOptions.include.length > 0) {
|
||||
parseQuery._include = restOptions.include.split(',');
|
||||
}
|
||||
if (restOptions.skip) {
|
||||
parseQuery._skip = restOptions.skip;
|
||||
parseQuery._skip = restOptions.skip;
|
||||
}
|
||||
if (restOptions.limit) {
|
||||
parseQuery._limit = restOptions.limit;
|
||||
parseQuery._limit = restOptions.limit;
|
||||
}
|
||||
}
|
||||
let requestObject = getRequestQueryObject(triggerType, auth, parseQuery, config);
|
||||
return Promise.resolve().then(() => {
|
||||
return Promise.resolve().then(() => {
|
||||
return trigger(requestObject);
|
||||
}).then((result) => {
|
||||
}).then((result) => {
|
||||
let queryResult = parseQuery;
|
||||
if (result && result instanceof Parse.Query) {
|
||||
queryResult = result;
|
||||
@@ -338,7 +337,7 @@ export function maybeRunQueryTrigger(triggerType, className, restWhere, restOpti
|
||||
restWhere,
|
||||
restOptions
|
||||
};
|
||||
}, (err) => {
|
||||
}, (err) => {
|
||||
if (typeof err === 'string') {
|
||||
throw new Parse.Error(1, err);
|
||||
} else {
|
||||
@@ -360,11 +359,11 @@ export function maybeRunTrigger(triggerType, auth, parseObject, originalParseObj
|
||||
var trigger = getTrigger(parseObject.className, triggerType, config.applicationId);
|
||||
if (!trigger) return resolve();
|
||||
var request = getRequestObject(triggerType, auth, parseObject, originalParseObject, config);
|
||||
var response = getResponseObject(request, (object) => {
|
||||
var response = getResponseObject(request, (object) => {
|
||||
logTriggerSuccessBeforeHook(
|
||||
triggerType, parseObject.className, parseObject.toJSON(), object, auth);
|
||||
resolve(object);
|
||||
}, (error) => {
|
||||
}, (error) => {
|
||||
logTriggerErrorBeforeHook(
|
||||
triggerType, parseObject.className, parseObject.toJSON(), auth, error);
|
||||
reject(error);
|
||||
@@ -382,16 +381,16 @@ export function maybeRunTrigger(triggerType, auth, parseObject, originalParseObj
|
||||
var triggerPromise = trigger(request, response);
|
||||
if(triggerType === Types.afterSave || triggerType === Types.afterDelete)
|
||||
{
|
||||
logTriggerAfterHook(triggerType, parseObject.className, parseObject.toJSON(), auth);
|
||||
if(triggerPromise && typeof triggerPromise.then === "function") {
|
||||
return triggerPromise.then(resolve, resolve);
|
||||
}
|
||||
else {
|
||||
return resolve();
|
||||
}
|
||||
logTriggerAfterHook(triggerType, parseObject.className, parseObject.toJSON(), auth);
|
||||
if(triggerPromise && typeof triggerPromise.then === "function") {
|
||||
return triggerPromise.then(resolve, resolve);
|
||||
}
|
||||
else {
|
||||
return resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Converts a REST-format object to a Parse.Object
|
||||
// data is either className or an object
|
||||
|
||||
274
src/vendor/mongodbUrl.js
vendored
274
src/vendor/mongodbUrl.js
vendored
@@ -120,18 +120,18 @@ Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
|
||||
// Only convert backslashes while we haven't seen a split character
|
||||
if (!split) {
|
||||
switch (code) {
|
||||
case 35: // '#'
|
||||
hasHash = true;
|
||||
case 35: // '#'
|
||||
hasHash = true;
|
||||
// Fall through
|
||||
case 63: // '?'
|
||||
split = true;
|
||||
break;
|
||||
case 92: // '\\'
|
||||
if (i - lastPos > 0)
|
||||
rest += url.slice(lastPos, i);
|
||||
rest += '/';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 63: // '?'
|
||||
split = true;
|
||||
break;
|
||||
case 92: // '\\'
|
||||
if (i - lastPos > 0)
|
||||
rest += url.slice(lastPos, i);
|
||||
rest += '/';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
}
|
||||
} else if (!hasHash && code === 35/*#*/) {
|
||||
hasHash = true;
|
||||
@@ -226,40 +226,40 @@ Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
|
||||
var nonHost = -1;
|
||||
for (i = 0; i < rest.length; ++i) {
|
||||
switch (rest.charCodeAt(i)) {
|
||||
case 9: // '\t'
|
||||
case 10: // '\n'
|
||||
case 13: // '\r'
|
||||
case 32: // ' '
|
||||
case 34: // '"'
|
||||
case 37: // '%'
|
||||
case 39: // '\''
|
||||
case 59: // ';'
|
||||
case 60: // '<'
|
||||
case 62: // '>'
|
||||
case 92: // '\\'
|
||||
case 94: // '^'
|
||||
case 96: // '`'
|
||||
case 123: // '{'
|
||||
case 124: // '|'
|
||||
case 125: // '}'
|
||||
case 9: // '\t'
|
||||
case 10: // '\n'
|
||||
case 13: // '\r'
|
||||
case 32: // ' '
|
||||
case 34: // '"'
|
||||
case 37: // '%'
|
||||
case 39: // '\''
|
||||
case 59: // ';'
|
||||
case 60: // '<'
|
||||
case 62: // '>'
|
||||
case 92: // '\\'
|
||||
case 94: // '^'
|
||||
case 96: // '`'
|
||||
case 123: // '{'
|
||||
case 124: // '|'
|
||||
case 125: // '}'
|
||||
// Characters that are never ever allowed in a hostname from RFC 2396
|
||||
if (nonHost === -1)
|
||||
nonHost = i;
|
||||
break;
|
||||
case 35: // '#'
|
||||
case 47: // '/'
|
||||
case 63: // '?'
|
||||
if (nonHost === -1)
|
||||
nonHost = i;
|
||||
break;
|
||||
case 35: // '#'
|
||||
case 47: // '/'
|
||||
case 63: // '?'
|
||||
// Find the first instance of any host-ending characters
|
||||
if (nonHost === -1)
|
||||
nonHost = i;
|
||||
hostEnd = i;
|
||||
break;
|
||||
case 64: // '@'
|
||||
if (nonHost === -1)
|
||||
nonHost = i;
|
||||
hostEnd = i;
|
||||
break;
|
||||
case 64: // '@'
|
||||
// At this point, either we have an explicit point where the
|
||||
// auth portion cannot go past, or the last @ char is the decider.
|
||||
atSign = i;
|
||||
nonHost = -1;
|
||||
break;
|
||||
atSign = i;
|
||||
nonHost = -1;
|
||||
break;
|
||||
}
|
||||
if (hostEnd !== -1)
|
||||
break;
|
||||
@@ -440,90 +440,90 @@ function autoEscapeStr(rest) {
|
||||
// Automatically escape all delimiters and unwise characters from RFC 2396
|
||||
// Also escape single quotes in case of an XSS attack
|
||||
switch (rest.charCodeAt(i)) {
|
||||
case 9: // '\t'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%09';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 10: // '\n'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%0A';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 13: // '\r'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%0D';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 32: // ' '
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%20';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 34: // '"'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%22';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 39: // '\''
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%27';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 60: // '<'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%3C';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 62: // '>'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%3E';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 92: // '\\'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%5C';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 94: // '^'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%5E';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 96: // '`'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%60';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 123: // '{'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%7B';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 124: // '|'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%7C';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 125: // '}'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%7D';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 9: // '\t'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%09';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 10: // '\n'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%0A';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 13: // '\r'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%0D';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 32: // ' '
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%20';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 34: // '"'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%22';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 39: // '\''
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%27';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 60: // '<'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%3C';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 62: // '>'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%3E';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 92: // '\\'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%5C';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 94: // '^'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%5E';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 96: // '`'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%60';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 123: // '{'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%7B';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 124: // '|'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%7C';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 125: // '}'
|
||||
if (i - lastPos > 0)
|
||||
newRest += rest.slice(lastPos, i);
|
||||
newRest += '%7D';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lastPos === 0)
|
||||
@@ -589,18 +589,18 @@ Url.prototype.format = function() {
|
||||
var lastPos = 0;
|
||||
for (var i = 0; i < pathname.length; ++i) {
|
||||
switch (pathname.charCodeAt(i)) {
|
||||
case 35: // '#'
|
||||
if (i - lastPos > 0)
|
||||
newPathname += pathname.slice(lastPos, i);
|
||||
newPathname += '%23';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 63: // '?'
|
||||
if (i - lastPos > 0)
|
||||
newPathname += pathname.slice(lastPos, i);
|
||||
newPathname += '%3F';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 35: // '#'
|
||||
if (i - lastPos > 0)
|
||||
newPathname += pathname.slice(lastPos, i);
|
||||
newPathname += '%23';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
case 63: // '?'
|
||||
if (i - lastPos > 0)
|
||||
newPathname += pathname.slice(lastPos, i);
|
||||
newPathname += '%3F';
|
||||
lastPos = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lastPos > 0) {
|
||||
|
||||
Reference in New Issue
Block a user