parent
fc02f57893
commit
d3a901e826
9 changed files with 470 additions and 15 deletions
@ -0,0 +1,43 @@ |
|||||||
|
import * as api from '../../client/src/api'; |
||||||
|
import { EndpointError, EndpointHandler, catchUnhandledErrors } from './types'; |
||||||
|
import Knex from 'knex'; |
||||||
|
|
||||||
|
export const CreateIntegrationEndpointHandler: EndpointHandler = async (req: any, res: any, knex: Knex) => { |
||||||
|
if (!api.checkCreateIntegrationRequest(req)) { |
||||||
|
const e: EndpointError = { |
||||||
|
internalMessage: 'Invalid CreateIntegration request: ' + JSON.stringify(req.body), |
||||||
|
httpStatus: 400 |
||||||
|
}; |
||||||
|
throw e; |
||||||
|
} |
||||||
|
const reqObject: api.CreateIntegrationRequest = req.body; |
||||||
|
const { id: userId } = req.user; |
||||||
|
|
||||||
|
console.log("User ", userId, ": Create Integration ", reqObject); |
||||||
|
|
||||||
|
await knex.transaction(async (trx) => { |
||||||
|
try { |
||||||
|
// Create the new integration.
|
||||||
|
var integration: any = { |
||||||
|
name: reqObject.name, |
||||||
|
user: userId, |
||||||
|
type: reqObject.type, |
||||||
|
details: JSON.stringify(reqObject.details), |
||||||
|
} |
||||||
|
const integrationId = (await trx('integrations') |
||||||
|
.insert(integration) |
||||||
|
.returning('id') // Needed for Postgres
|
||||||
|
)[0]; |
||||||
|
|
||||||
|
// Respond to the request.
|
||||||
|
const responseObject: api.CreateIntegrationResponse = { |
||||||
|
id: integrationId |
||||||
|
}; |
||||||
|
res.status(200).send(responseObject); |
||||||
|
|
||||||
|
} catch (e) { |
||||||
|
catchUnhandledErrors(e); |
||||||
|
trx.rollback(); |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
import * as api from '../../client/src/api'; |
||||||
|
import { EndpointError, EndpointHandler, catchUnhandledErrors } from './types'; |
||||||
|
import Knex from 'knex'; |
||||||
|
|
||||||
|
export const DeleteIntegrationEndpointHandler: EndpointHandler = async (req: any, res: any, knex: Knex) => { |
||||||
|
if (!api.checkDeleteIntegrationRequest(req)) { |
||||||
|
const e: EndpointError = { |
||||||
|
internalMessage: 'Invalid DeleteIntegration request: ' + JSON.stringify(req.body), |
||||||
|
httpStatus: 400 |
||||||
|
}; |
||||||
|
throw e; |
||||||
|
} |
||||||
|
const reqObject: api.DeleteIntegrationRequest = req.body; |
||||||
|
const { id: userId } = req.user; |
||||||
|
|
||||||
|
console.log("User ", userId, ": Delete Integration ", reqObject); |
||||||
|
|
||||||
|
await knex.transaction(async (trx) => { |
||||||
|
try { |
||||||
|
// Start retrieving the integration itself.
|
||||||
|
const integrationId = await trx.select('id') |
||||||
|
.from('integrations') |
||||||
|
.where({ 'user': userId }) |
||||||
|
.where({ id: req.params.id }) |
||||||
|
.then((r: any) => (r && r[0]) ? r[0]['id'] : undefined) |
||||||
|
|
||||||
|
// Check that we found all objects we need.
|
||||||
|
if (!integrationId) { |
||||||
|
const e: EndpointError = { |
||||||
|
internalMessage: 'Integration does not exist for DeleteIntegration request: ' + JSON.stringify(req.body), |
||||||
|
httpStatus: 404 |
||||||
|
}; |
||||||
|
throw e; |
||||||
|
} |
||||||
|
|
||||||
|
// Delete the integration.
|
||||||
|
await trx('integrations') |
||||||
|
.where({ 'user': userId, 'id': integrationId }) |
||||||
|
.del(); |
||||||
|
|
||||||
|
// Respond to the request.
|
||||||
|
res.status(200).send(); |
||||||
|
|
||||||
|
} catch (e) { |
||||||
|
catchUnhandledErrors(e); |
||||||
|
trx.rollback(); |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
import * as api from '../../client/src/api'; |
||||||
|
import { EndpointError, EndpointHandler, catchUnhandledErrors } from './types'; |
||||||
|
import Knex from 'knex'; |
||||||
|
|
||||||
|
export const IntegrationDetailsEndpointHandler: EndpointHandler = async (req: any, res: any, knex: Knex) => { |
||||||
|
if (!api.checkIntegrationDetailsRequest(req)) { |
||||||
|
const e: EndpointError = { |
||||||
|
internalMessage: 'Invalid IntegrationDetails request: ' + JSON.stringify(req.body), |
||||||
|
httpStatus: 400 |
||||||
|
}; |
||||||
|
throw e; |
||||||
|
} |
||||||
|
|
||||||
|
const { id: userId } = req.user; |
||||||
|
|
||||||
|
try { |
||||||
|
const integration = (await knex.select(['id', 'name', 'type', 'details']) |
||||||
|
.from('integrations') |
||||||
|
.where({ 'user': userId, 'id': req.params.id }))[0]; |
||||||
|
|
||||||
|
if (integration) { |
||||||
|
const response: api.IntegrationDetailsResponse = { |
||||||
|
name: integration.name, |
||||||
|
type: integration.type, |
||||||
|
details: JSON.parse(integration.details), |
||||||
|
} |
||||||
|
await res.send(response); |
||||||
|
} else { |
||||||
|
await res.status(404).send({}); |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
catchUnhandledErrors(e) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
import * as api from '../../client/src/api'; |
||||||
|
import { EndpointError, EndpointHandler, catchUnhandledErrors } from './types'; |
||||||
|
import Knex from 'knex'; |
||||||
|
|
||||||
|
export const ModifyIntegrationEndpointHandler: EndpointHandler = async (req: any, res: any, knex: Knex) => { |
||||||
|
if (!api.checkModifyIntegrationRequest(req)) { |
||||||
|
const e: EndpointError = { |
||||||
|
internalMessage: 'Invalid ModifyIntegration request: ' + JSON.stringify(req.body), |
||||||
|
httpStatus: 400 |
||||||
|
}; |
||||||
|
throw e; |
||||||
|
} |
||||||
|
const reqObject: api.ModifyIntegrationRequest = req.body; |
||||||
|
const { id: userId } = req.user; |
||||||
|
|
||||||
|
console.log("User ", userId, ": Modify Integration ", reqObject); |
||||||
|
|
||||||
|
await knex.transaction(async (trx) => { |
||||||
|
try { |
||||||
|
// Start retrieving the integration.
|
||||||
|
const integrationId = await trx.select('id') |
||||||
|
.from('integrations') |
||||||
|
.where({ 'user': userId }) |
||||||
|
.then((r: any) => (r && r[0]) ? r[0]['id'] : undefined) |
||||||
|
|
||||||
|
// Check that we found all objects we need.
|
||||||
|
if (!integrationId) { |
||||||
|
const e: EndpointError = { |
||||||
|
internalMessage: 'Integration does not exist for ModifyIntegration request: ' + JSON.stringify(req.body), |
||||||
|
httpStatus: 404 |
||||||
|
}; |
||||||
|
throw e; |
||||||
|
} |
||||||
|
|
||||||
|
// Modify the integration.
|
||||||
|
var update: any = {}; |
||||||
|
if ("name" in reqObject) { update["name"] = reqObject.name; } |
||||||
|
if ("details" in reqObject) { update["details"] = JSON.stringify(reqObject.details); } |
||||||
|
if ("type" in reqObject) { update["type"] = reqObject.type; } |
||||||
|
await trx('integrations') |
||||||
|
.where({ 'user': userId, 'id': req.params.id }) |
||||||
|
.update(update) |
||||||
|
|
||||||
|
// Respond to the request.
|
||||||
|
res.status(200).send(); |
||||||
|
|
||||||
|
} catch (e) { |
||||||
|
catchUnhandledErrors(e); |
||||||
|
trx.rollback(); |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
import * as Knex from "knex"; |
||||||
|
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> { |
||||||
|
// Integrations table.
|
||||||
|
await knex.schema.createTable( |
||||||
|
'integrations', |
||||||
|
(table: any) => { |
||||||
|
table.increments('id'); |
||||||
|
table.integer('user').unsigned().notNullable().defaultTo(1); |
||||||
|
table.string('name').notNullable(); // Uniquely identifies this integration configuration for the user.
|
||||||
|
table.string('type').notNullable(); // Enumerates different supported integration types (e.g. Spotify)
|
||||||
|
table.json('details'); // Stores anything that might be needed for the integration to work.
|
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> { |
||||||
|
await knex.schema.dropTable('integrations'); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,105 @@ |
|||||||
|
const chai = require('chai'); |
||||||
|
const chaiHttp = require('chai-http'); |
||||||
|
const express = require('express'); |
||||||
|
import { SetupApp } from '../../../app'; |
||||||
|
import * as helpers from './helpers'; |
||||||
|
import { sha512 } from 'js-sha512'; |
||||||
|
import { IntegrationType } from '../../../../client/src/api'; |
||||||
|
|
||||||
|
async function init() { |
||||||
|
chai.use(chaiHttp); |
||||||
|
const app = express(); |
||||||
|
const knex = await helpers.initTestDB(); |
||||||
|
|
||||||
|
// Add test users.
|
||||||
|
await knex.insert({ email: "test1@test.com", passwordHash: sha512('pass1') }).into('users'); |
||||||
|
await knex.insert({ email: "test2@test.com", passwordHash: sha512('pass2') }).into('users'); |
||||||
|
|
||||||
|
SetupApp(app, knex, ''); |
||||||
|
|
||||||
|
// Login as a test user.
|
||||||
|
var agent = chai.request.agent(app); |
||||||
|
await agent |
||||||
|
.post('/login?username=' + encodeURIComponent("test1@test.com") + '&password=' + encodeURIComponent('pass1')) |
||||||
|
.send({}); |
||||||
|
return agent; |
||||||
|
} |
||||||
|
|
||||||
|
describe('POST /integration with missing or wrong data', () => { |
||||||
|
it('should fail', async done => { |
||||||
|
let agent = await init(); |
||||||
|
let req = agent.keepOpen(); |
||||||
|
try { |
||||||
|
await helpers.createIntegration(req, { name: "A", type: IntegrationType.spotify }, 400); |
||||||
|
await helpers.createIntegration(req, { details: {}, type: IntegrationType.spotify }, 400); |
||||||
|
await helpers.createIntegration(req, { name: "A", details: {} }, 400); |
||||||
|
await helpers.createIntegration(req, { name: "A", type: "NonexistentType", details: {} }, 400); |
||||||
|
} finally { |
||||||
|
req.close(); |
||||||
|
agent.close(); |
||||||
|
done(); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('POST /integration with a correct request', () => { |
||||||
|
it('should succeed', async done => { |
||||||
|
let agent = await init(); |
||||||
|
let req = agent.keepOpen(); |
||||||
|
try { |
||||||
|
await helpers.createIntegration(req, { name: "A", type: IntegrationType.spotify, details: {} }, 200, { id: 1 }); |
||||||
|
} finally { |
||||||
|
req.close(); |
||||||
|
agent.close(); |
||||||
|
done(); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('PUT /integration with a correct request', () => { |
||||||
|
it('should succeed', async done => { |
||||||
|
let agent = await init(); |
||||||
|
let req = agent.keepOpen(); |
||||||
|
try { |
||||||
|
await helpers.createIntegration(req, { name: "A", type: IntegrationType.spotify, details: {} }, 200, { id: 1 }); |
||||||
|
await helpers.modifyIntegration(req, 1, { name: "B", type: IntegrationType.spotify, details: { secret: 'cat' } }, 200); |
||||||
|
await helpers.checkIntegration(req, 1, 200, { name: "B", type: IntegrationType.spotify, details: { secret: 'cat' } }) |
||||||
|
} finally { |
||||||
|
req.close(); |
||||||
|
agent.close(); |
||||||
|
done(); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('PUT /integration with wrong data', () => { |
||||||
|
it('should fail', async done => { |
||||||
|
let agent = await init(); |
||||||
|
let req = agent.keepOpen(); |
||||||
|
try { |
||||||
|
await helpers.createIntegration(req, { name: "A", type: IntegrationType.spotify, details: {} }, 200, { id: 1 }); |
||||||
|
await helpers.modifyIntegration(req, 1, { name: "B", type: "UnknownType", details: {} }, 400); |
||||||
|
} finally { |
||||||
|
req.close(); |
||||||
|
agent.close(); |
||||||
|
done(); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('DELETE /integration with a correct request', () => { |
||||||
|
it('should succeed', async done => { |
||||||
|
let agent = await init(); |
||||||
|
let req = agent.keepOpen(); |
||||||
|
try { |
||||||
|
await helpers.createIntegration(req, { name: "A", type: IntegrationType.spotify, details: {} }, 200, { id: 1 }); |
||||||
|
await helpers.checkIntegration(req, 1, 200, { name: "A", type: IntegrationType.spotify, details: {} }) |
||||||
|
await helpers.deleteIntegration(req, 1, 200); |
||||||
|
await helpers.checkIntegration(req, 1, 404); |
||||||
|
} finally { |
||||||
|
req.close(); |
||||||
|
agent.close(); |
||||||
|
done(); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
Loading…
Reference in new issue