-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
33bc5ea
commit dbc8543
Showing
8 changed files
with
3,734 additions
and
3,353 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
const fp = require('fastify-plugin'); | ||
const { v4: uuidv4 } = require('uuid'); | ||
|
||
const HOOKS_TABLE = 'warehouse-hooks'; | ||
|
||
/** | ||
* @typedef {import('../warehouse').WarehouseApp} WarehouseApp | ||
*/ | ||
|
||
/** | ||
* Return a clean Hook record | ||
* | ||
* @param {Object} record Hook record | ||
* @returns {Object} JSON object | ||
*/ | ||
function cleanHookRecord(record) { | ||
const { id, keyname: name, url } = record; | ||
return { id, name, url }; | ||
} | ||
|
||
module.exports = fp( | ||
/** | ||
* Initialize Hook plugin | ||
* | ||
* @param {WarehouseApp} fastify Fastify instance | ||
* @returns {Promise<void>} Promise representing plugin initialization result | ||
*/ | ||
async function (fastify) { | ||
const { dynamo } = fastify; | ||
|
||
fastify.decorate( | ||
'getHooks', | ||
/** | ||
* Get an Object hooks | ||
* | ||
* @param {Object} opts Method parameters | ||
* @param {string} opts.name Object name | ||
* @returns {Promise<Object>} The object | ||
*/ | ||
async function getHooks({ name: keyname }) { | ||
const { Items: hooks } = await dynamo | ||
.query({ | ||
KeyConditionExpression: 'keyname = :name', | ||
ExpressionAttributeValues: { | ||
':name': keyname | ||
}, | ||
TableName: HOOKS_TABLE | ||
}) | ||
.promise(); | ||
return hooks.map((hook) => cleanHookRecord(hook)); | ||
} | ||
); | ||
|
||
fastify.decorate( | ||
'getHook', | ||
/** | ||
* Get an Object hook | ||
* | ||
* @param {Object} opts Method parameters | ||
* @param {string} opts.name Object name | ||
* @param {string} opts.id Hook id | ||
* @returns {Promise<Object>} The hook | ||
*/ | ||
async function getHook({ name: keyname, id }) { | ||
const params = { | ||
Key: { keyname, id }, | ||
TableName: HOOKS_TABLE | ||
}; | ||
const { Item: item } = await dynamo.get(params).promise(); | ||
if (!item) return null; | ||
return cleanHookRecord(item); | ||
} | ||
); | ||
|
||
fastify.decorate( | ||
'createHook', | ||
/** | ||
* Create hook for object | ||
* | ||
* @param {Object} opts Method parameters | ||
* @param {string} opts.name Object name | ||
* @param {string} opts.url Hook URL | ||
* @returns {Promise<string>} The hook id | ||
*/ | ||
async function createHook({ name: keyname, url }) { | ||
const id = uuidv4(); | ||
|
||
await dynamo | ||
.put({ | ||
Item: { | ||
keyname, | ||
id, | ||
url | ||
}, | ||
TableName: HOOKS_TABLE | ||
}) | ||
.promise(); | ||
|
||
return id; | ||
} | ||
); | ||
|
||
fastify.decorate( | ||
'deleteHook', | ||
/** | ||
* Delete an Object hook | ||
* | ||
* @param {Object} opts Method parameters | ||
* @param {string} opts.name Object name | ||
* @param {string} opts.id Hook id | ||
* @returns {Promise<void>} The hook | ||
*/ | ||
async function deleteHook({ name: keyname, id }) { | ||
const params = { | ||
Key: { keyname, id }, | ||
TableName: HOOKS_TABLE | ||
}; | ||
return dynamo.delete(params).promise(); | ||
} | ||
); | ||
}, | ||
{ | ||
fastify: '3.x', | ||
name: 'hook', | ||
decorators: { | ||
fastify: ['dynamo'] | ||
}, | ||
dependencies: ['aws'] | ||
} | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
/** | ||
* @typedef {import('fastify').FastifyRequest} FastifyRequest | ||
* @typedef {import('fastify').FastifyReply} FastifyReply | ||
* @typedef {import('../warehouse').WarehouseApp} WarehouseApp | ||
*/ | ||
|
||
/** | ||
* HTTP Request Handler | ||
* | ||
* @callback FastifyHandler | ||
* @param {FastifyRequest} req Fastify request object | ||
* @param {FastifyReply} res Fastify response object | ||
*/ | ||
|
||
module.exports = | ||
/** | ||
* Initialize hook routes | ||
* | ||
* @param {WarehouseApp} fastify Warehouse app instance | ||
* @returns {Promise<void>} Promise representing routes initialization result | ||
*/ | ||
async function (fastify) { | ||
fastify.route({ | ||
method: 'GET', | ||
url: '/objects/:name/hooks', | ||
preHandler: fastify.auth([fastify.verifyAuthentication]), | ||
schema: { | ||
params: { | ||
type: 'object', | ||
properties: { | ||
name: { type: 'string' } | ||
} | ||
}, | ||
response: { | ||
200: { | ||
type: 'array', | ||
items: { | ||
type: 'object', | ||
properties: { | ||
id: { type: 'string', format: 'uuid' }, | ||
name: { type: 'string' }, | ||
url: { type: 'string', format: 'uri' } | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
handler: | ||
/** | ||
* @type {FastifyHandler} | ||
*/ | ||
// eslint-disable-next-line no-unused-vars | ||
async (req, res) => { | ||
const { | ||
params: { name } | ||
} = req; | ||
|
||
return fastify.getHooks({ name }); | ||
} | ||
}); | ||
|
||
fastify.route({ | ||
method: 'GET', | ||
url: '/objects/:name/hooks/:id', | ||
preHandler: fastify.auth([fastify.verifyAuthentication]), | ||
schema: { | ||
params: { | ||
type: 'object', | ||
properties: { | ||
name: { type: 'string' }, | ||
id: { type: 'string', format: 'uuid' } | ||
} | ||
}, | ||
response: { | ||
200: { | ||
type: 'object', | ||
properties: { | ||
id: { type: 'string', format: 'uuid' }, | ||
name: { type: 'string' }, | ||
url: { type: 'string', format: 'uri' } | ||
} | ||
} | ||
} | ||
}, | ||
handler: | ||
/** | ||
* @type {FastifyHandler} | ||
*/ | ||
// eslint-disable-next-line no-unused-vars | ||
async (req, res) => { | ||
const { | ||
params: { name, id } | ||
} = req; | ||
|
||
const hook = await fastify.getHook({ name, id }); | ||
if (!hook) { | ||
throw fastify.httpErrors.notFound( | ||
`Hook '${id}' not found for object '${name}'` | ||
); | ||
} | ||
|
||
return hook; | ||
} | ||
}); | ||
|
||
fastify.route({ | ||
method: 'POST', | ||
url: '/objects/:name/hooks', | ||
preHandler: fastify.auth([fastify.verifyAuthentication]), | ||
schema: { | ||
params: { | ||
type: 'object', | ||
properties: { | ||
name: { type: 'string' } | ||
} | ||
}, | ||
body: { | ||
type: 'object', | ||
required: ['url'], | ||
properties: { | ||
url: { type: 'string', format: 'uri' } | ||
} | ||
}, | ||
response: { | ||
201: { | ||
type: 'object', | ||
properties: { | ||
id: { type: 'string', format: 'uuid' } | ||
} | ||
} | ||
} | ||
}, | ||
handler: | ||
/** | ||
* @type {FastifyHandler} | ||
*/ | ||
async (req, res) => { | ||
const { | ||
params: { name }, | ||
body: { url } | ||
} = req; | ||
|
||
const hookId = await fastify.createHook({ | ||
name, | ||
url | ||
}); | ||
|
||
if (fastify.log.security) { | ||
fastify.log.security({ | ||
success: true, | ||
message: `Hook "${hookId}" sucessfully created for object "${name}"`, | ||
category: 'database', | ||
type: ['creation', 'allowed'], | ||
method: req.method, | ||
url: req.url, | ||
requestId: req.id, | ||
sourceAddress: req.ip, | ||
host: req.hostname | ||
}); | ||
} | ||
|
||
res.header('Cache-Control', 'no-storage'); | ||
res.status(201).send({ id: hookId }); | ||
} | ||
}); | ||
|
||
fastify.route({ | ||
method: 'DELETE', | ||
url: '/objects/:name/hooks/:id', | ||
preHandler: fastify.auth([fastify.verifyAuthentication]), | ||
schema: { | ||
params: { | ||
type: 'object', | ||
properties: { | ||
name: { type: 'string' }, | ||
id: { type: 'string', format: 'uuid' } | ||
} | ||
}, | ||
response: { | ||
204: {} | ||
} | ||
}, | ||
handler: | ||
/** | ||
* @type {FastifyHandler} | ||
*/ | ||
async (req, res) => { | ||
const { | ||
params: { name, id } | ||
} = req; | ||
|
||
const hook = await fastify.getHook({ name, id }); | ||
if (!hook) { | ||
throw fastify.httpErrors.notFound( | ||
`Hook '${id}' not found for object '${name}'` | ||
); | ||
} | ||
|
||
await fastify.deleteHook({ name, id }); | ||
|
||
if (fastify.log.security) { | ||
fastify.log.security({ | ||
success: true, | ||
message: `Hook "${id}" sucessfully deleted for object "${name}"`, | ||
category: 'database', | ||
type: ['deletion', 'allowed'], | ||
method: req.method, | ||
url: req.url, | ||
requestId: req.id, | ||
sourceAddress: req.ip, | ||
host: req.hostname | ||
}); | ||
} | ||
|
||
res.header('Cache-Control', 'no-storage'); | ||
res.status(204); | ||
} | ||
}); | ||
}; |
Oops, something went wrong.