Huge performance improvement on roles queries
This commit is contained in:
@@ -114,7 +114,7 @@ describe('Parse Role testing', () => {
|
|||||||
return Parse.Object.saveAll(roles, { useMasterKey: true });
|
return Parse.Object.saveAll(roles, { useMasterKey: true });
|
||||||
}).then( () => {
|
}).then( () => {
|
||||||
auth = new Auth({config: new Config("test"), isMaster: true, user: user});
|
auth = new Auth({config: new Config("test"), isMaster: true, user: user});
|
||||||
getAllRolesSpy = spyOn(auth, "_getAllRoleNamesForId").and.callThrough();
|
getAllRolesSpy = spyOn(auth, "_getAllRolesNamesForRoleIds").and.callThrough();
|
||||||
|
|
||||||
return auth._loadRoles();
|
return auth._loadRoles();
|
||||||
}).then ( (roles) => {
|
}).then ( (roles) => {
|
||||||
@@ -125,15 +125,14 @@ describe('Parse Role testing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 1 Query for the initial setup
|
// 1 Query for the initial setup
|
||||||
// 4 Queries for all the specific roles
|
// 1 query for the parent roles
|
||||||
// 1 Query for the final $in
|
expect(restExecute.calls.count()).toEqual(2);
|
||||||
expect(restExecute.calls.count()).toEqual(6);
|
|
||||||
|
|
||||||
// 4 One query for each of the roles
|
// 1 call for the 1st layer of roles
|
||||||
// 3 Queries for the "RootRole"
|
// 1 call for the 2nd layer
|
||||||
expect(getAllRolesSpy.calls.count()).toEqual(7);
|
expect(getAllRolesSpy.calls.count()).toEqual(2);
|
||||||
done()
|
done()
|
||||||
}).catch( () => {
|
}).catch( (err) => {
|
||||||
fail("should succeed");
|
fail("should succeed");
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -206,11 +205,11 @@ describe('Parse Role testing', () => {
|
|||||||
// For each role, fetch their sibling, what they inherit
|
// For each role, fetch their sibling, what they inherit
|
||||||
// return with result and roleId for later comparison
|
// return with result and roleId for later comparison
|
||||||
let promises = [admin, moderator, contentManager, superModerator].map((role) => {
|
let promises = [admin, moderator, contentManager, superModerator].map((role) => {
|
||||||
return auth._getAllRoleNamesForId(role.id).then((result) => {
|
return auth._getAllRolesNamesForRoleIds([role.id]).then((result) => {
|
||||||
return Parse.Promise.as({
|
return Parse.Promise.as({
|
||||||
id: role.id,
|
id: role.id,
|
||||||
name: role.get('name'),
|
name: role.get('name'),
|
||||||
roleIds: result
|
roleNames: result
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -219,26 +218,25 @@ describe('Parse Role testing', () => {
|
|||||||
}).then((results) => {
|
}).then((results) => {
|
||||||
results.forEach((result) => {
|
results.forEach((result) => {
|
||||||
let id = result.id;
|
let id = result.id;
|
||||||
let roleIds = result.roleIds;
|
let roleNames = result.roleNames;
|
||||||
if (id == admin.id) {
|
if (id == admin.id) {
|
||||||
expect(roleIds.length).toBe(2);
|
expect(roleNames.length).toBe(2);
|
||||||
expect(roleIds.indexOf(moderator.id)).not.toBe(-1);
|
expect(roleNames.indexOf("Moderator")).not.toBe(-1);
|
||||||
expect(roleIds.indexOf(contentManager.id)).not.toBe(-1);
|
expect(roleNames.indexOf("ContentManager")).not.toBe(-1);
|
||||||
} else if (id == moderator.id) {
|
} else if (id == moderator.id) {
|
||||||
expect(roleIds.length).toBe(1);
|
expect(roleNames.length).toBe(1);
|
||||||
expect(roleIds.indexOf(contentManager.id)).toBe(0);
|
expect(roleNames.indexOf("ContentManager")).toBe(0);
|
||||||
} else if (id == contentManager.id) {
|
} else if (id == contentManager.id) {
|
||||||
expect(roleIds.length).toBe(0);
|
expect(roleNames.length).toBe(0);
|
||||||
} else if (id == superModerator.id) {
|
} else if (id == superModerator.id) {
|
||||||
expect(roleIds.length).toBe(3);
|
expect(roleNames.length).toBe(3);
|
||||||
expect(roleIds.indexOf(moderator.id)).not.toBe(-1);
|
expect(roleNames.indexOf("Moderator")).not.toBe(-1);
|
||||||
expect(roleIds.indexOf(contentManager.id)).not.toBe(-1);
|
expect(roleNames.indexOf("ContentManager")).not.toBe(-1);
|
||||||
expect(roleIds.indexOf(superContentManager.id)).not.toBe(-1);
|
expect(roleNames.indexOf("SuperContentManager")).not.toBe(-1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
}).fail((err) => {
|
}).fail((err) => {
|
||||||
console.error(err);
|
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -259,7 +257,6 @@ describe('Parse Role testing', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
}, (e) => {
|
}, (e) => {
|
||||||
console.log(e);
|
|
||||||
fail('should not have errored');
|
fail('should not have errored');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -338,10 +335,13 @@ describe('Parse Role testing', () => {
|
|||||||
fail('Customer user should not have been able to save.');
|
fail('Customer user should not have been able to save.');
|
||||||
done();
|
done();
|
||||||
}, (e) => {
|
}, (e) => {
|
||||||
expect(e.code).toEqual(101);
|
if (e) {
|
||||||
|
expect(e.code).toEqual(101);
|
||||||
|
} else {
|
||||||
|
fail('should return an error');
|
||||||
|
}
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
113
src/Auth.js
113
src/Auth.js
@@ -114,30 +114,17 @@ Auth.prototype._loadRoles = function() {
|
|||||||
this.rolePromise = null;
|
this.rolePromise = null;
|
||||||
return Promise.resolve(this.userRoles);
|
return Promise.resolve(this.userRoles);
|
||||||
}
|
}
|
||||||
|
var rolesMap = results.reduce((m, r) => {
|
||||||
|
m.names.push(r.name);
|
||||||
|
m.ids.push(r.objectId);
|
||||||
|
return m;
|
||||||
|
}, {ids: [], names: []});
|
||||||
|
|
||||||
var roleIDs = results.map(r => r.objectId);
|
// run the recursive finding
|
||||||
var promises = [Promise.resolve(roleIDs)];
|
return this._getAllRolesNamesForRoleIds(rolesMap.ids, rolesMap.names)
|
||||||
var queriedRoles = {};
|
.then((roleNames) => {
|
||||||
for (var role of roleIDs) {
|
this.userRoles = roleNames.map((r) => {
|
||||||
promises.push(this._getAllRoleNamesForId(role, queriedRoles));
|
return 'role:' + r;
|
||||||
}
|
|
||||||
return Promise.all(promises).then((results) => {
|
|
||||||
var allIDs = [];
|
|
||||||
for (var x of results) {
|
|
||||||
Array.prototype.push.apply(allIDs, x);
|
|
||||||
}
|
|
||||||
var restWhere = {
|
|
||||||
objectId: {
|
|
||||||
'$in': allIDs
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var query = new RestQuery(this.config, master(this.config),
|
|
||||||
'_Role', restWhere, {});
|
|
||||||
return query.execute();
|
|
||||||
}).then((response) => {
|
|
||||||
var results = response.results;
|
|
||||||
this.userRoles = results.map((r) => {
|
|
||||||
return 'role:' + r.name;
|
|
||||||
});
|
});
|
||||||
this.fetchedRoles = true;
|
this.fetchedRoles = true;
|
||||||
this.rolePromise = null;
|
this.rolePromise = null;
|
||||||
@@ -146,50 +133,52 @@ Auth.prototype._loadRoles = function() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Given a role object id, get any other roles it is part of
|
// Given a list of roleIds, find all the parent roles, returns a promise with all names
|
||||||
Auth.prototype._getAllRoleNamesForId = function(roleID, queriedRoles = {}) {
|
Auth.prototype._getAllRolesNamesForRoleIds = function(roleIDs, names = [], queriedRoles = {}) {
|
||||||
// Don't need to requery this role as it is already being queried for.
|
let ins = roleIDs.filter((roleID) => {
|
||||||
if (queriedRoles[roleID] != null) {
|
return queriedRoles[roleID] !== true;
|
||||||
return Promise.resolve([]);
|
}).map((roleID) => {
|
||||||
|
// mark as queried
|
||||||
|
queriedRoles[roleID] = true;
|
||||||
|
return {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_Role',
|
||||||
|
objectId: roleID
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// all roles are accounted for, return the names
|
||||||
|
if (ins.length == 0) {
|
||||||
|
return Promise.resolve([...new Set(names)]);
|
||||||
}
|
}
|
||||||
queriedRoles[roleID] = true;
|
// Build an OR query across all parentRoles
|
||||||
// As per documentation, a Role inherits AnotherRole
|
let restWhere;
|
||||||
// if this Role is in the roles pointer of this AnotherRole
|
if (ins.length == 1) {
|
||||||
// Let's find all the roles where this role is in a roles relation
|
restWhere = { 'roles': ins[0] };
|
||||||
var rolePointer = {
|
} else {
|
||||||
__type: 'Pointer',
|
restWhere = { 'roles': { '$in': ins }}
|
||||||
className: '_Role',
|
}
|
||||||
objectId: roleID
|
let query = new RestQuery(this.config, master(this.config), '_Role', restWhere, {});
|
||||||
};
|
|
||||||
var restWhere = {
|
|
||||||
'roles': rolePointer
|
|
||||||
};
|
|
||||||
var query = new RestQuery(this.config, master(this.config), '_Role',
|
|
||||||
restWhere, {});
|
|
||||||
return query.execute().then((response) => {
|
return query.execute().then((response) => {
|
||||||
var results = response.results;
|
var results = response.results;
|
||||||
|
// Nothing found
|
||||||
if (!results.length) {
|
if (!results.length) {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve(names);
|
||||||
}
|
}
|
||||||
var roleIDs = results.map(r => r.objectId);
|
// Map the results with all Ids and names
|
||||||
|
let resultMap = results.reduce((memo, role) => {
|
||||||
// we found a list of roles where the roleID
|
memo.names.push(role.name);
|
||||||
// is referenced in the roles relation,
|
memo.ids.push(role.objectId);
|
||||||
// Get the roles where those found roles are also
|
return memo;
|
||||||
// referenced the same way
|
}, {ids: [], names: []});
|
||||||
var parentRolesPromises = roleIDs.map( (roleId) => {
|
// store the new found names
|
||||||
return this._getAllRoleNamesForId(roleId, queriedRoles);
|
names = names.concat(resultMap.names);
|
||||||
});
|
// find the next ones, circular roles will be cut
|
||||||
parentRolesPromises.push(Promise.resolve(roleIDs));
|
return this._getAllRolesNamesForRoleIds(resultMap.ids, names, queriedRoles)
|
||||||
return Promise.all(parentRolesPromises);
|
}).then((names) => {
|
||||||
}).then(function(results){
|
return Promise.resolve([...new Set(names)])
|
||||||
// Flatten
|
})
|
||||||
let roleIDs = results.reduce( (memo, result) => {
|
}
|
||||||
return memo.concat(result);
|
|
||||||
}, []);
|
|
||||||
return Promise.resolve([...new Set(roleIDs)]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Auth: Auth,
|
Auth: Auth,
|
||||||
|
|||||||
Reference in New Issue
Block a user