mirror of
https://github.com/strapi/strapi.git
synced 2025-08-26 09:42:09 +00:00
CRUD group schemas
This commit is contained in:
parent
e9d2c04824
commit
f1a1e82b2c
6
examples/getstarted/groups/cta_facebook.json
Normal file
6
examples/getstarted/groups/cta_facebook.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "CTA Facebook",
|
||||||
|
"connection": "default",
|
||||||
|
"collectionName": "cta_facebook_aa",
|
||||||
|
"attributes": {}
|
||||||
|
}
|
@ -63,6 +63,30 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"policies": []
|
"policies": []
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/groups",
|
||||||
|
"handler": "Groups.createGroup",
|
||||||
|
"config": {
|
||||||
|
"policies": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/groups/:uid",
|
||||||
|
"handler": "Groups.updateGroup",
|
||||||
|
"config": {
|
||||||
|
"policies": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "DELETE",
|
||||||
|
"path": "/groups/:uid",
|
||||||
|
"handler": "Groups.deleteGroup",
|
||||||
|
"config": {
|
||||||
|
"policies": []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,21 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const yup = require('yup');
|
||||||
|
const formatYupErrors = require('./utils/yup-formatter');
|
||||||
|
|
||||||
|
const groupSchema = yup.object().shape({
|
||||||
|
name: yup.string().required(),
|
||||||
|
connection: yup.string(),
|
||||||
|
collectionName: yup.string(),
|
||||||
|
attributes: yup.object().required(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const internals = {
|
||||||
|
get service() {
|
||||||
|
return strapi.plugins['content-type-builder'].services.groups;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Groups controller
|
* Groups controller
|
||||||
*/
|
*/
|
||||||
@ -11,7 +27,7 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
async getGroups(ctx) {
|
async getGroups(ctx) {
|
||||||
const data = await strapi.groupManager.all();
|
const data = await strapi.groupManager.all();
|
||||||
ctx.body = { data };
|
ctx.send({ data });
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,18 +41,74 @@ module.exports = {
|
|||||||
const group = await strapi.groupManager.get(uid);
|
const group = await strapi.groupManager.get(uid);
|
||||||
|
|
||||||
if (!group) {
|
if (!group) {
|
||||||
ctx.status = 404;
|
return ctx.send({ error: 'group.notFound' }, 404);
|
||||||
ctx.body = {
|
|
||||||
error: 'group.notFound',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = { data: group };
|
ctx.send({ data: group });
|
||||||
},
|
},
|
||||||
|
|
||||||
async createGroup() {},
|
async createGroup(ctx) {
|
||||||
|
const { body } = ctx.request;
|
||||||
|
|
||||||
async updateGroup() {},
|
try {
|
||||||
|
await groupSchema.validate(body, {
|
||||||
|
strict: true,
|
||||||
|
abortEarly: false,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return ctx.send({ error: formatYupErrors(error) }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
async deleteGroup() {},
|
const uid = internals.service.createGroupUID(body.name);
|
||||||
|
|
||||||
|
if (strapi.groupManager.get(uid) !== undefined) {
|
||||||
|
return ctx.send({ error: 'group.alreadyExists' }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newGroup = await internals.service.createGroup(uid, body);
|
||||||
|
|
||||||
|
ctx.send({ data: newGroup }, 201);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a group and return it
|
||||||
|
* @param {Object} ctx - enhanced koa context
|
||||||
|
*/
|
||||||
|
async updateGroup(ctx) {
|
||||||
|
const { uid } = ctx.params;
|
||||||
|
const { body } = ctx.request;
|
||||||
|
|
||||||
|
const group = await strapi.groupManager.get(uid);
|
||||||
|
|
||||||
|
if (!group) {
|
||||||
|
return ctx.send({ error: 'group.notFound' }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await groupSchema.validate(body, {
|
||||||
|
strict: true,
|
||||||
|
abortEarly: false,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return ctx.send({ error: formatYupErrors(error) }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedGroup = await internals.service.updateGroup(group, body);
|
||||||
|
|
||||||
|
ctx.send({ data: updatedGroup }, 200);
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteGroup(ctx) {
|
||||||
|
const { uid } = ctx.params;
|
||||||
|
|
||||||
|
const group = await strapi.groupManager.get(uid);
|
||||||
|
|
||||||
|
if (!group) {
|
||||||
|
return ctx.send({ error: 'group.notFound' }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
await internals.service.deleteGroup(group);
|
||||||
|
|
||||||
|
ctx.send({ data: group }, 200);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
const yup = require('yup');
|
||||||
|
const formatYupErrors = require('../yup-formatter');
|
||||||
|
|
||||||
|
describe('Format yup errors', () => {
|
||||||
|
test('Format single errors', async () => {
|
||||||
|
expect.hasAssertions();
|
||||||
|
return yup
|
||||||
|
.object({
|
||||||
|
name: yup.string().required('name is required'),
|
||||||
|
})
|
||||||
|
.validate({})
|
||||||
|
.catch(err => {
|
||||||
|
expect(formatYupErrors(err)).toMatchObject({
|
||||||
|
name: ['name is required'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Format multiple errors', async () => {
|
||||||
|
expect.hasAssertions();
|
||||||
|
return yup
|
||||||
|
.object({
|
||||||
|
name: yup
|
||||||
|
.string()
|
||||||
|
.min(2, 'min length is 2')
|
||||||
|
.required(),
|
||||||
|
})
|
||||||
|
.validate(
|
||||||
|
{
|
||||||
|
name: '1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
strict: true,
|
||||||
|
abortEarly: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch(err => {
|
||||||
|
expect(formatYupErrors(err)).toMatchObject({
|
||||||
|
name: ['min length is 2'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Format multiple errors on multiple keys', async () => {
|
||||||
|
expect.hasAssertions();
|
||||||
|
return yup
|
||||||
|
.object({
|
||||||
|
name: yup
|
||||||
|
.string()
|
||||||
|
.min(2, 'min length is 2')
|
||||||
|
.typeError('name must be a string')
|
||||||
|
.required(),
|
||||||
|
price: yup
|
||||||
|
.number()
|
||||||
|
.integer()
|
||||||
|
.required('price is required'),
|
||||||
|
})
|
||||||
|
.validate(
|
||||||
|
{
|
||||||
|
name: 12,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
strict: true,
|
||||||
|
abortEarly: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch(err => {
|
||||||
|
expect(formatYupErrors(err)).toMatchObject({
|
||||||
|
price: ['price is required'],
|
||||||
|
name: ['name must be a string'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,19 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a formatted error for http responses
|
||||||
|
* @param {Object} validationError - a Yup ValidationError
|
||||||
|
*/
|
||||||
|
const formatYupErrors = validationError => {
|
||||||
|
if (validationError.inner.length === 0) {
|
||||||
|
if (validationError.path === undefined) return validationError.errors;
|
||||||
|
return { [validationError.path]: validationError.errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
return validationError.inner.reduce((acc, err) => {
|
||||||
|
acc[err.path] = err.errors;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = formatYupErrors;
|
@ -8,9 +8,10 @@
|
|||||||
"description": "content-type-builder.plugin.description"
|
"description": "content-type-builder.plugin.description"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"no tests yet\""
|
"test": "jest --verbose controllers"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@sindresorhus/slugify": "^0.9.1",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"fs-extra": "^7.0.0",
|
"fs-extra": "^7.0.0",
|
||||||
"immutable": "^3.8.2",
|
"immutable": "^3.8.2",
|
||||||
|
@ -1,3 +1,95 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports = {};
|
const { pick } = require('lodash');
|
||||||
|
const slugify = require('@sindresorhus/slugify');
|
||||||
|
|
||||||
|
const VALID_FIELDS = ['name', 'connection', 'collectionName', 'attributes'];
|
||||||
|
|
||||||
|
const createSchema = infos => {
|
||||||
|
const { name, connection = 'default', collectionName } = infos;
|
||||||
|
const uid = createGroupUID(name);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
connection,
|
||||||
|
collectionName: collectionName || uid,
|
||||||
|
attributes: {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSchema = (oldSchema, newSchema) =>
|
||||||
|
pick({ ...oldSchema, ...newSchema }, VALID_FIELDS);
|
||||||
|
|
||||||
|
const createGroupUID = str => slugify(str, { separator: '_' });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a group schema file
|
||||||
|
* @param {*} uid
|
||||||
|
* @param {*} infos
|
||||||
|
*/
|
||||||
|
async function createGroup(uid, infos) {
|
||||||
|
const schema = createSchema(infos);
|
||||||
|
|
||||||
|
return writeSchema(uid, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a group schema file
|
||||||
|
* @param {*} group
|
||||||
|
* @param {*} infos
|
||||||
|
*/
|
||||||
|
async function updateGroup(group, infos) {
|
||||||
|
const { uid } = group;
|
||||||
|
const updatedSchema = updateSchema(group.schema, infos);
|
||||||
|
|
||||||
|
if (infos.name !== group.schema.name) {
|
||||||
|
await deleteSchema(uid);
|
||||||
|
|
||||||
|
const newUid = createGroupUID(infos.name);
|
||||||
|
return writeSchema(newUid, updatedSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeSchema(uid, updatedSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteGroup(group) {
|
||||||
|
await deleteSchema(group.uid);
|
||||||
|
process.nextTick(() => strapi.reload());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a group schema file
|
||||||
|
*/
|
||||||
|
async function writeSchema(uid, schema) {
|
||||||
|
strapi.reload.isWatching = false;
|
||||||
|
|
||||||
|
await strapi.fs.writeAppFile(
|
||||||
|
`groups/${uid}.json`,
|
||||||
|
JSON.stringify(schema, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
strapi.reload.isWatching = true;
|
||||||
|
process.nextTick(() => strapi.reload());
|
||||||
|
|
||||||
|
return {
|
||||||
|
uid,
|
||||||
|
schema,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a group schema file
|
||||||
|
* @param {string} ui
|
||||||
|
*/
|
||||||
|
async function deleteSchema(uid) {
|
||||||
|
strapi.reload.isWatching = false;
|
||||||
|
await strapi.fs.removeAppFile(`groups/${uid}.json`);
|
||||||
|
strapi.reload.isWatching = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
createGroup,
|
||||||
|
createGroupUID,
|
||||||
|
updateGroup,
|
||||||
|
deleteGroup,
|
||||||
|
};
|
||||||
|
@ -23,13 +23,44 @@ describe.only('Content Type Builder - Groups', () => {
|
|||||||
|
|
||||||
res.body.data.forEach(el => {
|
res.body.data.forEach(el => {
|
||||||
expect(el).toMatchObject({
|
expect(el).toMatchObject({
|
||||||
id: expect.any(String),
|
uid: expect.any(String),
|
||||||
name: expect.any(String),
|
name: expect.any(String),
|
||||||
icon: expect.any(String),
|
|
||||||
// later
|
|
||||||
schema: expect.objectContaining({}),
|
schema: expect.objectContaining({}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('GET /group/:uid', () => {
|
||||||
|
test('Returns 404 on not found', async () => {
|
||||||
|
const res = await rq({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/content-type-build/groups/nonexistent-group',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Returns correct format', async () => {
|
||||||
|
const res = await rq({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/content-type-build/groups/existing-group',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
expect(res.body).toMatchObject({
|
||||||
|
data: {
|
||||||
|
uid: 'existing-group',
|
||||||
|
name: 'EXISTING-GROUP',
|
||||||
|
schema: {
|
||||||
|
connection: 'default',
|
||||||
|
collectionName: 'existing_groups',
|
||||||
|
attributes: {
|
||||||
|
//...
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs-extra');
|
const fse = require('fs-extra');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create strapi fs layer
|
* create strapi fs layer
|
||||||
*/
|
*/
|
||||||
module.exports = strapi => {
|
module.exports = strapi => {
|
||||||
|
function normalizePath(optPath) {
|
||||||
|
const filePath = Array.isArray(optPath) ? optPath.join('/') : optPath;
|
||||||
|
|
||||||
|
const normalizedPath = path.normalize(filePath).replace(/^(\/?\.\.?)+/, '');
|
||||||
|
|
||||||
|
return path.join(strapi.dir, normalizedPath);
|
||||||
|
}
|
||||||
|
|
||||||
const strapiFS = {
|
const strapiFS = {
|
||||||
/**
|
/**
|
||||||
* Writes a file in a strapi app
|
* Writes a file in a strapi app
|
||||||
@ -14,15 +22,10 @@ module.exports = strapi => {
|
|||||||
* @param {string} data - content
|
* @param {string} data - content
|
||||||
*/
|
*/
|
||||||
writeAppFile(optPath, data) {
|
writeAppFile(optPath, data) {
|
||||||
const filePath = Array.isArray(optPath) ? optPath.join('/') : optPath;
|
const writePath = normalizePath(optPath);
|
||||||
|
return fse
|
||||||
const normalizedPath = path
|
.ensureFile(writePath)
|
||||||
.normalize(filePath)
|
.then(() => fse.writeFile(writePath, data));
|
||||||
.replace(/^(\/?\.\.?)+/, '');
|
|
||||||
|
|
||||||
const writePath = path.join(strapi.dir, normalizedPath);
|
|
||||||
|
|
||||||
return fs.ensureFile(writePath).then(() => fs.writeFile(writePath, data));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,6 +38,14 @@ module.exports = strapi => {
|
|||||||
const newPath = ['extensions', plugin].concat(optPath).join('/');
|
const newPath = ['extensions', plugin].concat(optPath).join('/');
|
||||||
return strapiFS.writeAppFile(newPath, data);
|
return strapiFS.writeAppFile(newPath, data);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a file in strapi app
|
||||||
|
*/
|
||||||
|
removeAppFile(optPath) {
|
||||||
|
const removePath = normalizePath(optPath);
|
||||||
|
return fse.remove(removePath);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return strapiFS;
|
return strapiFS;
|
||||||
|
@ -9,6 +9,39 @@ const _ = require('lodash');
|
|||||||
const Boom = require('boom');
|
const Boom = require('boom');
|
||||||
const delegate = require('delegates');
|
const delegate = require('delegates');
|
||||||
|
|
||||||
|
const boomMethods = [
|
||||||
|
'badRequest',
|
||||||
|
'unauthorized',
|
||||||
|
'paymentRequired',
|
||||||
|
'forbidden',
|
||||||
|
'notFound',
|
||||||
|
'methodNotAllowed',
|
||||||
|
'notAcceptable',
|
||||||
|
'proxyAuthRequired',
|
||||||
|
'clientTimeout',
|
||||||
|
'conflict',
|
||||||
|
'resourceGone',
|
||||||
|
'lengthRequired',
|
||||||
|
'preconditionFailed',
|
||||||
|
'entityTooLarge',
|
||||||
|
'uriTooLong',
|
||||||
|
'unsupportedMediaType',
|
||||||
|
'rangeNotSatisfiable',
|
||||||
|
'expectationFailed',
|
||||||
|
'teapot',
|
||||||
|
'badData',
|
||||||
|
'locked',
|
||||||
|
'failedDependency',
|
||||||
|
'preconditionRequired',
|
||||||
|
'tooManyRequests',
|
||||||
|
'illegal',
|
||||||
|
'badImplementation',
|
||||||
|
'notImplemented',
|
||||||
|
'badGateway',
|
||||||
|
'serverUnavailable',
|
||||||
|
'gatewayTimeout',
|
||||||
|
];
|
||||||
|
|
||||||
module.exports = strapi => {
|
module.exports = strapi => {
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
@ -69,19 +102,19 @@ module.exports = strapi => {
|
|||||||
|
|
||||||
// Custom function to avoid ctx.body repeat
|
// Custom function to avoid ctx.body repeat
|
||||||
createResponses() {
|
createResponses() {
|
||||||
Object.keys(Boom).forEach(key => {
|
boomMethods.forEach(method => {
|
||||||
strapi.app.response[key] = function(...rest) {
|
strapi.app.response[method] = function(...rest) {
|
||||||
const error = Boom[key](...rest) || {};
|
const error = Boom[method](...rest) || {};
|
||||||
|
|
||||||
this.status = error.isBoom ? error.output.statusCode : this.status;
|
this.status = error.isBoom ? error.output.statusCode : this.status;
|
||||||
this.body = error;
|
this.body = error;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.delegator.method(key);
|
this.delegator.method(method);
|
||||||
});
|
});
|
||||||
|
|
||||||
strapi.app.response.send = function(data) {
|
strapi.app.response.send = function(data, status = 200) {
|
||||||
this.status = 200;
|
this.status = status;
|
||||||
this.body = data;
|
this.body = data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ const initMap = groups => {
|
|||||||
|
|
||||||
Object.keys(groups).forEach(key => {
|
Object.keys(groups).forEach(key => {
|
||||||
const {
|
const {
|
||||||
|
name,
|
||||||
connection,
|
connection,
|
||||||
collectionName,
|
collectionName,
|
||||||
description,
|
description,
|
||||||
@ -11,8 +12,7 @@ const initMap = groups => {
|
|||||||
|
|
||||||
map.set(key, {
|
map.set(key, {
|
||||||
uid: key,
|
uid: key,
|
||||||
name: key.toUpperCase(), // get the display name som
|
schema: { name, connection, collectionName, description, attributes },
|
||||||
schema: { connection, collectionName, description, attributes },
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
13
yarn.lock
13
yarn.lock
@ -2008,6 +2008,14 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
|
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
|
||||||
integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
|
integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
|
||||||
|
|
||||||
|
"@sindresorhus/slugify@^0.9.1":
|
||||||
|
version "0.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@sindresorhus/slugify/-/slugify-0.9.1.tgz#892ad24d70b442c0a14fe519cb4019d59bc5069f"
|
||||||
|
integrity sha512-b6heYM9dzZD13t2GOiEQTDE0qX+I1GyOotMwKh9VQqzuNiVdPVT8dM43fe9HNb/3ul+Qwd5oKSEDrDIfhq3bnQ==
|
||||||
|
dependencies:
|
||||||
|
escape-string-regexp "^1.0.5"
|
||||||
|
lodash.deburr "^4.1.0"
|
||||||
|
|
||||||
"@snyk/composer-lockfile-parser@1.0.2":
|
"@snyk/composer-lockfile-parser@1.0.2":
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@snyk/composer-lockfile-parser/-/composer-lockfile-parser-1.0.2.tgz#d748e56076bc1c25b130c1f13ed705fa285a1994"
|
resolved "https://registry.yarnpkg.com/@snyk/composer-lockfile-parser/-/composer-lockfile-parser-1.0.2.tgz#d748e56076bc1c25b130c1f13ed705fa285a1994"
|
||||||
@ -10873,6 +10881,11 @@ lodash.clonedeep@^4.3.0, lodash.clonedeep@^4.3.2, lodash.clonedeep@^4.5.0:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||||
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
|
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
|
||||||
|
|
||||||
|
lodash.deburr@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-4.1.0.tgz#ddb1bbb3ef07458c0177ba07de14422cb033ff9b"
|
||||||
|
integrity sha1-3bG7s+8HRYwBd7oH3hRCLLAz/5s=
|
||||||
|
|
||||||
lodash.defaults@^4.2.0:
|
lodash.defaults@^4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
|
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user