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