Merge branch 'master' into asset-manager-tutorial

This commit is contained in:
Kim Gardner
2018-03-15 21:00:23 -04:00
committed by GitHub
26 changed files with 197 additions and 125 deletions
+3 -1
View File
@@ -15,6 +15,7 @@ const SetupService = require('../services/setup');
const UsersService = require('../services/users');
const MigrationService = require('../services/migration');
const errors = require('../errors');
const Context = require('../graph/context');
// Register the shutdown criteria.
util.onshutdown([() => mongoose.disconnect()]);
@@ -184,7 +185,8 @@ const performSetup = async () => {
},
]);
let { user: newUser } = await SetupService.setup({
const ctx = Context.forSystem();
let { user: newUser } = await SetupService.setup(ctx, {
settings: settings.toObject(),
user: {
email: user.email,
+7 -1
View File
@@ -318,7 +318,13 @@ module.exports = {
let user;
try {
user = await UsersService.findOrCreateExternalUser(profile);
const { id, provider, displayName } = profile;
user = await UsersService.findOrCreateExternalUser(
req.context,
id,
provider,
displayName
);
} catch (err) {
return done(err);
}
+24 -23
View File
@@ -1,16 +1,17 @@
const {
SUBSCRIBE_COMMENT_ACCEPTED,
SUBSCRIBE_COMMENT_REJECTED,
SUBSCRIBE_COMMENT_FLAGGED,
SUBSCRIBE_COMMENT_RESET,
SUBSCRIBE_ALL_COMMENT_EDITED,
SUBSCRIBE_ALL_COMMENT_ADDED,
SUBSCRIBE_ALL_USER_SUSPENDED,
SUBSCRIBE_ALL_COMMENT_EDITED,
SUBSCRIBE_ALL_USER_BANNED,
SUBSCRIBE_ALL_USERNAME_REJECTED,
SUBSCRIBE_ALL_USER_CREATED,
SUBSCRIBE_ALL_USER_SUSPENDED,
SUBSCRIBE_ALL_USERNAME_APPROVED,
SUBSCRIBE_ALL_USERNAME_FLAGGED,
SUBSCRIBE_ALL_USERNAME_CHANGED,
SUBSCRIBE_ALL_USERNAME_FLAGGED,
SUBSCRIBE_ALL_USERNAME_REJECTED,
SUBSCRIBE_COMMENT_ACCEPTED,
SUBSCRIBE_COMMENT_FLAGGED,
SUBSCRIBE_COMMENT_REJECTED,
SUBSCRIBE_COMMENT_RESET,
} = require('../../perms/constants');
const merge = require('lodash/merge');
@@ -139,6 +140,8 @@ const setupFunctions = {
}
return !args.user_id || user.id === args.user_id;
},
userCreated: (options, args, user, ctx) =>
ctx.user && ctx.user.can(SUBSCRIBE_ALL_USER_CREATED),
};
/**
@@ -153,19 +156,17 @@ module.exports = plugins.get('server', 'setupFunctions').reduce(
return merge(acc, setupFunctions);
},
Object.keys(setupFunctions)
.map(key => {
const filter = setupFunctions[key];
return {
[key]: (options, args) => ({
[key]: {
filter: (user, ctx) => filter(options, args, user, ctx),
},
}),
};
})
.reduce((setupFunction, setupFunctions) => {
return merge(setupFunctions, setupFunction);
}, {})
// Process the default setupFunctions.
Object.entries(setupFunctions)
.map(([key, filter]) => ({
[key]: (options, args) => ({
[key]: {
filter: (user, ctx) => filter(options, args, user, ctx),
},
}),
}))
.reduce(
(setupFunction, setupFunctions) => merge(setupFunctions, setupFunction),
{}
)
);
+4
View File
@@ -1597,6 +1597,10 @@ type Subscription {
# Get an update whenever a username has been changed. `user_id` must match id
# of current user except for users with the `ADMIN` or `MODERATOR` role.
usernameChanged(user_id: ID): UsernameChangedPayload
# Get an update whenever a user is created. Only accessible to users with the
# `ADMIN` or `MODERATOR` roles.
userCreated: User
}
################################################################################
+1
View File
@@ -11,4 +11,5 @@ module.exports = {
SUBSCRIBE_ALL_USERNAME_APPROVED: 'SUBSCRIBE_ALL_USERNAME_APPROVED',
SUBSCRIBE_ALL_USERNAME_FLAGGED: 'SUBSCRIBE_ALL_USERNAME_FLAGGED',
SUBSCRIBE_ALL_USERNAME_CHANGED: 'SUBSCRIBE_ALL_USERNAME_CHANGED',
SUBSCRIBE_ALL_USER_CREATED: 'SUBSCRIBE_ALL_USER_CREATED',
};
+1
View File
@@ -15,6 +15,7 @@ module.exports = (user, perm) => {
case types.SUBSCRIBE_ALL_USERNAME_APPROVED:
case types.SUBSCRIBE_ALL_USERNAME_FLAGGED:
case types.SUBSCRIBE_ALL_USERNAME_CHANGED:
case types.SUBSCRIBE_ALL_USER_CREATED:
return check(user, ['ADMIN', 'MODERATOR']);
default:
break;
@@ -25,7 +25,14 @@ module.exports = passport => {
async (req, accessToken, refreshToken, profile, done) => {
let user;
try {
user = await UsersService.findOrCreateExternalUser(profile);
const { id, provider, displayName } = profile;
user = await UsersService.findOrCreateExternalUser(
req.context,
id,
provider,
displayName
);
} catch (err) {
return done(err);
}
@@ -24,9 +24,16 @@ module.exports = passport => {
async (req, accessToken, refreshToken, profile, done) => {
let user;
try {
user = await UsersService.findOrCreateExternalUser(profile);
const { id, provider, displayName } = profile;
user = await UsersService.findOrCreateExternalUser(
req.context,
id,
provider,
displayName
);
} catch (err) {
return done(err.toString());
return done(err);
}
return ValidateUserLogin(profile, user, done);
+4 -1
View File
@@ -21,7 +21,10 @@ router.post('/', async (req, res, next) => {
const { settings, user: { email, password, username } } = req.body;
try {
await SetupService.setup({ settings, user: { email, password, username } });
await SetupService.setup(req.context, {
settings,
user: { email, password, username },
});
res.status(204).end();
} catch (err) {
return next(err);
+7 -1
View File
@@ -11,7 +11,13 @@ router.post('/', async (req, res, next) => {
const redirectUri = req.header('X-Pym-Url') || req.header('Referer');
try {
let user = await UsersService.createLocalUser(email, password, username);
// Adjusted the user creation endpoint.
let user = await UsersService.createLocalUser(
req.context,
email,
password,
username
);
// Send an email confirmation. The Front end will know about the
// requireEmailConfirmation as it's included in the settings get endpoint.
+7 -2
View File
@@ -61,7 +61,7 @@ module.exports = class SetupService {
/**
* This will perform the setup.
*/
static async setup({ settings, user: { email, password, username } }) {
static async setup(ctx, { settings, user: { email, password, username } }) {
// Validate the settings first.
await SetupService.validate({
settings,
@@ -79,7 +79,12 @@ module.exports = class SetupService {
// Settings are created! Create the user.
// Create the user.
let user = await UsersService.createLocalUser(email, password, username);
let user = await UsersService.createLocalUser(
ctx,
email,
password,
username
);
// Grant them administrative privileges and confirm the email account.
await Promise.all([
+13 -24
View File
@@ -416,7 +416,7 @@ class UsersService {
* @param {Object} profile - User social/external profile
* @param {Function} done [description]
*/
static async findOrCreateExternalUser({ id, provider, displayName }) {
static async findOrCreateExternalUser(ctx, id, provider, displayName) {
let user = await UserModel.findOne({
profiles: {
$elemMatch: {
@@ -452,6 +452,9 @@ class UsersService {
// Save the user in the database.
await user.save();
// Emit that the user was created.
ctx.pubsub.publish('userCreated', user);
return user;
}
@@ -502,23 +505,6 @@ class UsersService {
);
}
/**
* Creates local users.
* @param {Array} users Users to create
* @return {Promise} Resolves with the users that were created
*/
static createLocalUsers(users) {
return Promise.all(
users.map(user => {
return UsersService.createLocalUser(
user.email,
user.password,
user.username
);
})
);
}
/**
* Check the requested username for blocked words and special chars
* @param {String} username word to be checked for profanity
@@ -553,24 +539,24 @@ class UsersService {
*/
static isValidPassword(password) {
if (!password) {
return Promise.reject(errors.ErrMissingPassword);
throw errors.ErrMissingPassword;
}
if (password.length < 8) {
return Promise.reject(errors.ErrPasswordTooShort);
throw errors.ErrPasswordTooShort;
}
return Promise.resolve(password);
return password;
}
/**
* Creates the local user with a given email, password, and name.
* @param {Object} ctx application context for the request
* @param {String} email email of the new user
* @param {String} password plaintext password of the new user
* @param {String} username name of the display user
* @param {Function} done callback
* @param {String} username name of the display user
*/
static async createLocalUser(email, password, username) {
static async createLocalUser(ctx, email, password, username) {
if (!email) {
throw errors.ErrMissingEmail;
}
@@ -617,6 +603,9 @@ class UsersService {
throw err;
}
// Emit that the user was created.
ctx.pubsub.publish('userCreated', user);
return user;
}
@@ -11,7 +11,9 @@ describe('graph.mutations.changeUsername', () => {
let user;
beforeEach(async () => {
await SettingsService.init();
const ctx = Context.forSystem();
user = await UsersService.createLocalUser(
ctx,
'test@test.com',
'testpassword1!',
'kirk'
@@ -16,8 +16,10 @@ describe('graph.mutations.editComment', () => {
beforeEach(async () => {
timekeeper.reset();
await SettingsService.init();
const ctx = Context.forSystem();
asset = await AssetModel.create({});
user = await UsersService.createLocalUser(
ctx,
'usernameA@example.com',
'password',
'usernameA'
@@ -124,7 +126,9 @@ describe('graph.mutations.editComment', () => {
body: `hello there! ${String(Math.random()).slice(2)}`,
});
const ctx = Context.forSystem();
const userB = await UsersService.createLocalUser(
ctx,
'usernameB@example.com',
'password',
'usernameB'
@@ -34,12 +34,15 @@ describe('graph.mutations.ignoreUser', () => {
});
it('users can ignoreUser', async () => {
const ctx = Context.forSystem();
let currentUser = await UsersService.createLocalUser(
ctx,
'usernameA@example.com',
'password',
'usernameA'
);
const userToIgnore = await UsersService.createLocalUser(
ctx,
'usernameB@example.com',
'password',
'usernameB'
@@ -83,7 +86,9 @@ describe('graph.mutations.ignoreUser', () => {
});
it('users cannot ignore themselves', async () => {
const ctx = Context.forSystem();
const user = await UsersService.createLocalUser(
ctx,
'usernameA@example.com',
'password',
'usernameA'
@@ -124,18 +129,22 @@ describe('graph.mutations.stopIgnoringUser', () => {
// We're going to ignore 2 users,
// then stopIgnoring 1 of them
// then assert myIgnoredUsers only lists the one remaining
const ctx = Context.forSystem();
let currentUser = await UsersService.createLocalUser(
ctx,
'usernameA@example.com',
'password',
'usernameA'
);
const usersToIgnore = await Promise.all([
UsersService.createLocalUser(
ctx,
'usernameB@example.com',
'password',
'usernameB'
),
UsersService.createLocalUser(
ctx,
'usernameC@example.com',
'password',
'usernameC'
@@ -17,7 +17,9 @@ describe('graph.mutations.banUser', () => {
beforeEach(async () => {
await SettingsService.init();
const ctx = Context.forSystem();
user = await UsersService.createLocalUser(
ctx,
'usernameA@example.com',
'password',
'usernameA'
@@ -19,7 +19,9 @@ describe('graph.mutations.suspendUser', () => {
beforeEach(async () => {
await SettingsService.init();
const ctx = Context.forSystem();
user = await UsersService.createLocalUser(
ctx,
'usernameA@example.com',
'password',
'usernameA'
@@ -19,7 +19,9 @@ const { expect } = chai;
beforeEach(async () => {
await SettingsService.init();
const ctx = Context.forSystem();
user = await UsersService.createLocalUser(
ctx,
'usernameA@example.com',
'password',
'usernameA'
+22 -17
View File
@@ -17,23 +17,28 @@ describe('graph.queries.asset', () => {
{ id: '1', url: 'https://example.com/?id=1' },
{ id: '2', url: 'https://example.com/?id=2' },
]);
users = await UsersService.createLocalUsers([
{
email: 'usernameA@example.com',
password: 'password',
username: 'usernameA',
},
{
email: 'usernameB@example.com',
password: 'password',
username: 'usernameB',
},
{
email: 'usernameC@example.com',
password: 'password',
username: 'usernameC',
},
]);
const ctx = Context.forSystem();
users = await Promise.all(
[
{
email: 'usernameA@example.com',
password: 'password',
username: 'usernameA',
},
{
email: 'usernameB@example.com',
password: 'password',
username: 'usernameB',
},
{
email: 'usernameC@example.com',
password: 'password',
username: 'usernameC',
},
].map(({ email, username, password }) =>
UsersService.createLocalUser(ctx, email, password, username)
)
);
comments = await Promise.all(
[0, 0, 1, 1].map(idx =>
CommentsService.publicCreate({
+2 -1
View File
@@ -14,8 +14,9 @@ describe('graph.queries.user', () => {
let user;
beforeEach(async () => {
await SettingsService.init();
const ctx = Context.forSystem();
user = await UsersService.createLocalUser(
ctx,
'usernameA@example.com',
'password',
'usernameA'
+4 -2
View File
@@ -1,12 +1,12 @@
const app = require('../../../../../app');
const Context = require('../../../../../graph/context');
const UsersService = require('../../../../../services/users');
const chai = require('chai');
chai.should();
chai.use(require('chai-http'));
const expect = chai.expect;
const UsersService = require('../../../../../services/users');
describe('/api/v1/auth', () => {
describe('#get', () => {
it('should return nothing when no user is logged in', () => {
@@ -32,7 +32,9 @@ describe('/api/v1/auth/local', () => {
};
await SettingsService.init(settings);
const ctx = Context.forSystem();
mockUser = await UsersService.createLocalUser(
ctx,
'maria@gmail.com',
'password!',
'Maria'
+11 -13
View File
@@ -3,6 +3,7 @@ const passport = require('../../../passport');
const app = require('../../../../../app');
const mailer = require('../../../../../services/mailer');
const Context = require('../../../../../graph/context');
const SettingsService = require('../../../../../services/settings');
const settings = {
id: '1',
@@ -20,19 +21,16 @@ const UsersService = require('../../../../../services/users');
describe('/api/v1/users/:user_id/email/confirm', () => {
let mockUser;
beforeEach(() =>
SettingsService.init(settings)
.then(() => {
return UsersService.createLocalUser(
'ana@gmail.com',
'123321123',
'Ana'
);
})
.then(user => {
mockUser = user;
})
);
beforeEach(async () => {
await SettingsService.init(settings);
const ctx = Context.forSystem();
mockUser = await UsersService.createLocalUser(
ctx,
'ana@gmail.com',
'123321123',
'Ana'
);
});
describe('#post', () => {
it('should send an email when we hit the endpoint', () => {
+10 -5
View File
@@ -1,9 +1,9 @@
const CommentModel = require('../../../models/comment');
const ActionModel = require('../../../models/action');
const UsersService = require('../../../services/users');
const SettingsService = require('../../../services/settings');
const CommentModel = require('../../../models/comment');
const CommentsService = require('../../../services/comments');
const Context = require('../../../graph/context');
const SettingsService = require('../../../services/settings');
const UsersService = require('../../../services/users');
const settings = {
id: '1',
@@ -119,9 +119,14 @@ describe('services.CommentsService', () => {
beforeEach(async () => {
await SettingsService.init(settings);
const ctx = Context.forSystem();
await Promise.all([
CommentModel.create(comments),
UsersService.createLocalUsers(users),
Promise.all(
users.map(({ email, password, username }) =>
UsersService.createLocalUser(ctx, email, password, username)
)
),
ActionModel.create(actions),
]);
});
+3 -1
View File
@@ -1,8 +1,8 @@
const TagsService = require('../../../services/tags');
const UsersService = require('../../../services/users');
const SettingsService = require('../../../services/settings');
const CommentModel = require('../../../models/comment');
const Context = require('../../../graph/context');
const chai = require('chai');
const expect = chai.expect;
@@ -11,7 +11,9 @@ describe('services.TagsService', () => {
let comment, user;
beforeEach(async () => {
await SettingsService.init();
const ctx = Context.forSystem();
user = await UsersService.createLocalUser(
ctx,
'stampi@gmail.com',
'1Coral!!',
'Stampi'
+3
View File
@@ -1,6 +1,7 @@
const TokensService = require('../../../services/tokens');
const UsersService = require('../../../services/users');
const SettingsService = require('../../../services/settings');
const Context = require('../../../graph/context');
const chai = require('chai');
chai.use(require('chai-as-promised'));
@@ -10,7 +11,9 @@ describe('services.TokensService', () => {
let user;
beforeEach(async () => {
await SettingsService.init();
const ctx = Context.forSystem();
user = await UsersService.createLocalUser(
ctx,
'sockmonster@gmail.com',
'2Coral!!',
'Sockmonster'
+33 -30
View File
@@ -1,6 +1,7 @@
const UsersService = require('../../../services/users');
const SettingsService = require('../../../services/settings');
const mailer = require('../../../services/mailer');
const Context = require('../../../graph/context');
const chai = require('chai');
chai.use(require('chai-as-promised'));
@@ -18,23 +19,28 @@ describe('services.UsersService', () => {
};
await SettingsService.init(settings);
mockUsers = await UsersService.createLocalUsers([
{
email: 'stampi@gmail.com',
username: 'Stampi',
password: '1Coral!-',
},
{
email: 'sockmonster@gmail.com',
username: 'Sockmonster',
password: '2Coral!2',
},
{
email: 'marvel@gmail.com',
username: 'Marvel',
password: '3Coral!3',
},
]);
const ctx = Context.forSystem();
mockUsers = await Promise.all(
[
{
email: 'stampi@gmail.com',
username: 'Stampi',
password: '1Coral!-',
},
{
email: 'sockmonster@gmail.com',
username: 'Sockmonster',
password: '2Coral!2',
},
{
email: 'marvel@gmail.com',
username: 'Marvel',
password: '3Coral!3',
},
].map(({ email, username, password }) =>
UsersService.createLocalUser(ctx, email, password, username)
)
);
sinon.spy(mailer, 'send');
});
@@ -102,19 +108,16 @@ describe('services.UsersService', () => {
describe('#createLocalUser', () => {
it('should not create a user with duplicate username', () => {
return UsersService.createLocalUsers([
{
email: 'otrostampi@gmail.com',
username: 'StampiTheSecond',
password: '1Coralito!',
},
])
.then(user => {
expect(user).to.be.null;
})
.catch(error => {
expect(error).to.not.be.null;
});
const ctx = Context.forSystem();
return expect(
UsersService.createLocalUser(
ctx,
'otrostampi@gmail.com',
'1Coralito!',
'Stampi'
)
).be.rejected;
});
});