Post to Slack (#603)

* Migrations

* WIP: Integration model, slack perms / hooks

* So so rough it pains me. Building this new model is revealing just how much needs to be refactored

* Working connect and post

* Cleanup UI, upating documents

* Show when slack command is connected

* stash

* 💚

* Add documents.update trigger

* Authorization, tidying

* Fixed integration policy

* pick integration presenter keys
This commit is contained in:
Tom Moor
2018-04-03 20:36:25 -07:00
committed by GitHub
parent 17900c6a11
commit 44cb509ebf
38 changed files with 665 additions and 105 deletions

View File

@@ -1,6 +1,7 @@
/* eslint-disable flowtype/require-valid-file-annotation */
import { flushdb, seed } from '../test/support';
import { Collection, Document } from '../models';
import uuid from 'uuid';
beforeEach(flushdb);
beforeEach(jest.resetAllMocks);
@@ -15,34 +16,37 @@ describe('#getUrl', () => {
describe('#addDocumentToStructure', async () => {
test('should add as last element without index', async () => {
const { collection } = await seed();
const id = uuid.v4();
const newDocument = new Document({
id: '5',
id,
title: 'New end node',
parentDocumentId: null,
});
await collection.addDocumentToStructure(newDocument);
expect(collection.documentStructure.length).toBe(3);
expect(collection.documentStructure[2].id).toBe('5');
expect(collection.documentStructure[2].id).toBe(id);
});
test('should add with an index', async () => {
const { collection } = await seed();
const id = uuid.v4();
const newDocument = new Document({
id: '5',
id,
title: 'New end node',
parentDocumentId: null,
});
await collection.addDocumentToStructure(newDocument, 1);
expect(collection.documentStructure.length).toBe(3);
expect(collection.documentStructure[1].id).toBe('5');
expect(collection.documentStructure[1].id).toBe(id);
});
test('should add as a child if with parent', async () => {
const { collection, document } = await seed();
const id = uuid.v4();
const newDocument = new Document({
id: '5',
id,
title: 'New end node',
parentDocumentId: document.id,
});
@@ -51,18 +55,19 @@ describe('#addDocumentToStructure', async () => {
expect(collection.documentStructure.length).toBe(2);
expect(collection.documentStructure[1].id).toBe(document.id);
expect(collection.documentStructure[1].children.length).toBe(1);
expect(collection.documentStructure[1].children[0].id).toBe('5');
expect(collection.documentStructure[1].children[0].id).toBe(id);
});
test('should add as a child if with parent with index', async () => {
const { collection, document } = await seed();
const newDocument = new Document({
id: '5',
id: uuid.v4(),
title: 'node',
parentDocumentId: document.id,
});
const id = uuid.v4();
const secondDocument = new Document({
id: '6',
id,
title: 'New start node',
parentDocumentId: document.id,
});
@@ -72,14 +77,15 @@ describe('#addDocumentToStructure', async () => {
expect(collection.documentStructure.length).toBe(2);
expect(collection.documentStructure[1].id).toBe(document.id);
expect(collection.documentStructure[1].children.length).toBe(2);
expect(collection.documentStructure[1].children[0].id).toBe('6');
expect(collection.documentStructure[1].children[0].id).toBe(id);
});
describe('options: documentJson', async () => {
test("should append supplied json over document's own", async () => {
const { collection } = await seed();
const id = uuid.v4();
const newDocument = new Document({
id: '5',
id: uuid.v4(),
title: 'New end node',
parentDocumentId: null,
});
@@ -88,7 +94,7 @@ describe('#addDocumentToStructure', async () => {
documentJson: {
children: [
{
id: '7',
id,
title: 'Totally fake',
children: [],
},
@@ -96,7 +102,7 @@ describe('#addDocumentToStructure', async () => {
},
});
expect(collection.documentStructure[2].children.length).toBe(1);
expect(collection.documentStructure[2].children[0].id).toBe('7');
expect(collection.documentStructure[2].children[0].id).toBe(id);
});
});
});

View File

@@ -7,6 +7,7 @@ import Plain from 'slate-plain-serializer';
import { Op } from 'sequelize';
import isUUID from 'validator/lib/isUUID';
import { Collection } from '../models';
import { DataTypes, sequelize } from '../sequelize';
import events from '../events';
import parseTitle from '../../shared/utils/parseTitle';
@@ -107,6 +108,10 @@ Document.associate = models => {
foreignKey: 'atlasId',
onDelete: 'cascade',
});
Document.belongsTo(models.Team, {
as: 'team',
foreignKey: 'teamId',
});
Document.belongsTo(models.User, {
as: 'createdBy',
foreignKey: 'createdById',
@@ -223,23 +228,51 @@ Document.searchForUser = async (
// Hooks
Document.addHook('afterCreate', model =>
events.add({ name: 'documents.create', model })
);
Document.addHook('beforeSave', async model => {
if (!model.publishedAt) return;
const collection = await Collection.findById(model.atlasId);
if (collection.type !== 'atlas') return;
await collection.updateDocument(model);
model.collection = collection;
});
Document.addHook('afterCreate', async model => {
if (!model.publishedAt) return;
const collection = await Collection.findById(model.atlasId);
if (collection.type !== 'atlas') return;
await collection.addDocumentToStructure(model);
model.collection = collection;
events.add({ name: 'documents.create', model });
return model;
});
Document.addHook('afterDestroy', model =>
events.add({ name: 'documents.delete', model })
);
Document.addHook('afterUpdate', model => {
if (!model.previous('publishedAt') && model.publishedAt) {
events.add({ name: 'documents.publish', model });
}
events.add({ name: 'documents.update', model });
});
// Instance methods
Document.prototype.publish = async function() {
if (this.publishedAt) return this.save();
const collection = await Collection.findById(this.atlasId);
if (collection.type !== 'atlas') return this.save();
await collection.addDocumentToStructure(this);
this.publishedAt = new Date();
await this.save();
this.collection = collection;
events.add({ name: 'documents.publish', model: this });
return this;
};
Document.prototype.getTimestamp = function() {
return Math.round(new Date(this.updatedAt).getTime() / 1000);
};

View File

@@ -0,0 +1,35 @@
// @flow
import { DataTypes, sequelize } from '../sequelize';
const Integration = sequelize.define('integration', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
type: DataTypes.STRING,
serviceId: DataTypes.STRING,
settings: DataTypes.JSONB,
events: DataTypes.ARRAY(DataTypes.STRING),
});
Integration.associate = models => {
Integration.belongsTo(models.User, {
as: 'user',
foreignKey: 'userId',
});
Integration.belongsTo(models.Team, {
as: 'team',
foreignKey: 'teamId',
});
Integration.belongsTo(models.Collection, {
as: 'collection',
foreignKey: 'collectionId',
});
Integration.belongsTo(models.Authentication, {
as: 'authentication',
foreignKey: 'authenticationId',
});
};
export default Integration;

View File

@@ -1,10 +1,11 @@
/* eslint-disable flowtype/require-valid-file-annotation */
import { flushdb, seed } from '../test/support';
import { flushdb } from '../test/support';
import { buildUser } from '../test/factories';
beforeEach(flushdb);
it('should set JWT secret and password digest', async () => {
const { user } = await seed();
const user = await buildUser({ password: 'test123!' });
expect(user.passwordDigest).toBeTruthy();
expect(user.getJwtToken()).toBeTruthy();

View File

@@ -1,5 +1,6 @@
// @flow
import Authentication from './Authentication';
import Integration from './Integration';
import Event from './Event';
import User from './User';
import Team from './Team';
@@ -12,6 +13,7 @@ import Star from './Star';
const models = {
Authentication,
Integration,
Event,
User,
Team,
@@ -32,6 +34,7 @@ Object.keys(models).forEach(modelName => {
export {
Authentication,
Integration,
Event,
User,
Team,