Add validators

This commit is contained in:
Alexandre Bodin 2019-12-24 14:49:44 +01:00
parent 36d662fc79
commit 74d25bb459
11 changed files with 170 additions and 84 deletions

View File

@ -1,28 +1,59 @@
'use strict';
const _ = require('lodash');
const { yup, formatYupErrors } = require('strapi-utils');
const ALLOWED_EVENTS = [
'entry.create',
'entry.update',
'entry.delete',
'media.create',
'media.delete',
];
const webhookValidator = yup
.object({
name: yup.string().required(),
url: yup.string().required(),
headers: yup.lazy(data => {
if (typeof data !== 'object') {
return yup.object().required();
}
return yup
.object(
_.mapValues(data, () => {
yup
.string()
.min(1)
.required();
})
)
.required();
}),
events: yup
.array()
.of(
yup
.string()
.oneOf(ALLOWED_EVENTS)
.required()
)
.min(1)
.required(),
})
.noUnknown();
const updateWebhookValidator = webhookValidator.shape({
isEnabled: yup.boolean(),
});
module.exports = {
async listWebhooks(ctx) {
const webhooks = await strapi.webhookStore.findWebhooks();
ctx.send({ data: webhooks });
},
async createWebhook(ctx) {
const { name, url, headers, events } = ctx.request.body;
// TODO: validate input
const webhook = await strapi.webhookStore.createWebhook({
name,
url,
headers,
events,
});
strapi.webhookRunner.add(webhook);
ctx.created({ data: webhook });
},
async getWebhook(ctx) {
const { id } = ctx.params;
const webhook = await strapi.webhookStore.findWebhook(id);
@ -34,26 +65,63 @@ module.exports = {
ctx.send({ data: webhook });
},
async createWebhook(ctx) {
const { body } = ctx.request;
try {
await webhookValidator.validate(body, {
strict: true,
abortEarly: false,
});
} catch (error) {
return ctx.send(
{
statusCode: 400,
message: 'ValidationError',
error: formatYupErrors(error),
},
400
);
}
const webhook = await strapi.webhookStore.createWebhook(body);
strapi.webhookRunner.add(webhook);
ctx.created({ data: webhook });
},
async updateWebhook(ctx) {
const { id } = ctx.params;
const { body } = ctx.request;
const webhook = await strapi.webhookStore.findWebhook(id);
// TODO: validate input
if (!webhook) {
return ctx.send({ error: 'webhook.notFound' }, 404);
try {
await updateWebhookValidator.validate(body, {
strict: true,
abortEarly: false,
});
} catch (error) {
return ctx.badRequest('ValidationError', {
error: formatYupErrors(error),
});
}
const updatedWebhook = {
const webhook = await strapi.webhookStore.findWebhook(id);
if (!webhook) {
return ctx.notFound('webhook.notFound');
}
const updatedWebhook = await strapi.webhookStore.updateWebhook(id, {
...webhook,
...body,
};
});
await strapi.webhookStore.updateWebhook(id, updatedWebhook);
if (!updatedWebhook) {
return ctx.notFound('webhook.notFound');
}
strapi.webhookRunner.update(webhook);
strapi.webhookRunner.update(updatedWebhook);
ctx.send({ data: updatedWebhook });
},
@ -63,7 +131,7 @@ module.exports = {
const webhook = await strapi.webhookStore.findWebhook(id);
if (!webhook) {
return ctx.send({ error: 'webhook.notFound' }, 404);
return ctx.notFound('webhook.notFound');
}
await strapi.webhookStore.deleteWebhook(id);

View File

@ -1,7 +1,7 @@
'use strict';
const yup = require('yup');
const formatYupErrors = require('./yup-formatter');
const { formatYupErrors } = require('strapi-utils');
const { isValidCategoryName } = require('./common');

View File

@ -2,9 +2,9 @@
const _ = require('lodash');
const yup = require('yup');
const { formatYupErrors } = require('strapi-utils');
const { isValidCategoryName, isValidIcon } = require('./common');
const formatYupErrors = require('./yup-formatter');
const createSchema = require('./model-schema');
const { modelTypes, DEFAULT_TYPES } = require('./constants');

View File

@ -2,7 +2,7 @@
const _ = require('lodash');
const yup = require('yup');
const formatYupErrors = require('./yup-formatter');
const { formatYupErrors } = require('strapi-utils');
const createSchema = require('./model-schema');
const { nestedComponentSchema } = require('./component');

View File

@ -1,5 +1,5 @@
const yup = require('yup');
const formatYupErrors = require('../yup-formatter');
const { formatYupErrors } = require('../validators');
describe('Format yup errors', () => {
test('Format single errors', async () => {

View File

@ -9,13 +9,21 @@ const buildQuery = require('./buildQuery');
const parseMultipartData = require('./parse-multipart');
const sanitizeEntity = require('./sanitize-entity');
const parseType = require('./parse-type');
const finder = require('./finder');
const logger = require('./logger');
const models = require('./models');
const policy = require('./policy');
const templateConfiguration = require('./templateConfiguration');
const { yup, formatYupErrors } = require('./validators');
module.exports = {
finder: require('./finder'),
logger: require('./logger'),
models: require('./models'),
policy: require('./policy'),
templateConfiguration: require('./templateConfiguration'),
yup,
formatYupErrors,
finder,
logger,
models,
policy,
templateConfiguration,
convertRestQueryParams,
buildQuery,
parseMultipartData,

View File

@ -1,6 +1,7 @@
'use strict';
const { ValidationError } = require('yup');
const yup = require('yup');
const { ValidationError } = yup;
/**
* Returns a formatted error for http responses
@ -22,4 +23,7 @@ const formatYupErrors = validationError => {
}, {});
};
module.exports = formatYupErrors;
module.exports = {
yup,
formatYupErrors,
};

View File

@ -23,7 +23,8 @@
"lodash": "^4.17.11",
"pino": "^4.7.1",
"pluralize": "^7.0.0",
"shelljs": "^0.8.3"
"shelljs": "^0.8.3",
"yup": "^0.28.0"
},
"author": {
"email": "hi@strapi.io",

View File

@ -78,15 +78,22 @@ class WebhookRunner {
},
timeout: 10000,
})
.then(res => {
.then(async res => {
if (res.ok) {
return {
statusCode: res.status,
};
}
return {
statusCode: res.status,
message: await res.text(),
};
})
.catch(err => {
return {
statusCode: err.status,
body: err.body,
statusCode: 500,
message: err.message,
};
});
}

View File

@ -32,14 +32,24 @@ const webhookModel = {
},
};
const formatWebhookInfo = webhook => {
const toDBObject = data => {
return {
id: webhook.id,
name: webhook.name,
url: webhook.url,
headers: webhook.headers,
events: webhook.events,
isEnabled: webhook.enabled,
name: data.name,
url: data.url,
headers: data.headers,
events: data.events,
enabled: data.isEnabled,
};
};
const fromDBObject = row => {
return {
id: row.id,
name: row.name,
url: row.url,
headers: row.headers,
events: row.events,
isEnabled: row.enabled,
};
};
@ -50,52 +60,28 @@ const createWebhookStore = ({ db }) => {
async findWebhooks() {
const results = await webhookQueries.find();
return results.map(formatWebhookInfo);
return results.map(fromDBObject);
},
async findWebhook(id) {
const result = await webhookQueries.findOne({ id });
return result ? formatWebhookInfo(result) : null;
return result ? fromDBObject(result) : null;
},
createWebhook(data) {
const { name, url, headers, events } = data;
const webhook = {
name,
url,
headers,
events,
enabled: true,
};
return webhookQueries.create(webhook).then(formatWebhookInfo);
return webhookQueries
.create(toDBObject({ ...data, isEnabled: true }))
.then(fromDBObject);
},
async updateWebhook(id, data) {
const oldWebhook = await this.findWebhook(id);
if (!oldWebhook) {
throw new Error('webhook.notFound');
}
const { name, url, headers, events, isEnabled } = data;
const updatedWebhook = {
name,
url,
headers,
events,
enabled: isEnabled,
};
return webhookQueries
.update({ id }, updatedWebhook)
.then(formatWebhookInfo);
const webhook = await webhookQueries.update({ id }, toDBObject(data));
return webhook ? fromDBObject(webhook) : null;
},
deleteWebhook(id) {
return webhookQueries.delete({ id });
async deleteWebhook(id) {
const webhook = await webhookQueries.delete({ id });
return webhook ? fromDBObject(webhook) : null;
},
};
};

View File

@ -18129,6 +18129,18 @@ yup@^0.27.0:
synchronous-promise "^2.0.6"
toposort "^2.0.2"
yup@^0.28.0:
version "0.28.0"
resolved "https://registry.yarnpkg.com/yup/-/yup-0.28.0.tgz#fdc04d1a495465c83d3757a80c47616884baeddc"
integrity sha512-9ZmsB/PT6/m+oUKF8rT9lWhMMGfx5s/aNCCf8pMu/GEQA0Ro2tLOc+aX12GjfL67Vif5a3c7eZVuxGFqFScnJQ==
dependencies:
"@babel/runtime" "^7.0.0"
fn-name "~2.0.1"
lodash "^4.17.11"
property-expr "^1.5.0"
synchronous-promise "^2.0.6"
toposort "^2.0.2"
zen-observable-ts@^0.8.20:
version "0.8.20"
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.20.tgz#44091e335d3fcbc97f6497e63e7f57d5b516b163"