Refactor relation-list route

Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com>
This commit is contained in:
Alexandre Bodin 2020-10-27 16:01:46 +01:00
parent b37a92c60f
commit 7509a16152
6 changed files with 140 additions and 91 deletions

View File

@ -67,6 +67,24 @@
}
},
{
"method": "GET",
"path": "/relations/:model/:targetField",
"handler": "relations.find",
"config": {
"policies": [
"admin::isAuthenticatedAdmin",
[
"plugins::content-manager.hasPermissions",
[
"plugins::content-manager.explorer.create",
"plugins::content-manager.explorer.update"
],
{ "hasAtLeastOne": true }
]
]
}
},
{
"method": "GET",
@ -100,25 +118,7 @@
]
}
},
{
"method": "GET",
"path": "/explorer/:model/relation-list/:targetField",
"handler": "ContentManager.findRelationList",
"config": {
"policies": [
"routing",
"admin::isAuthenticatedAdmin",
[
"plugins::content-manager.hasPermissions",
[
"plugins::content-manager.explorer.create",
"plugins::content-manager.explorer.update"
],
{ "hasAtLeastOne": true }
]
]
}
},
{
"method": "POST",
"path": "/explorer/:model",

View File

@ -342,56 +342,4 @@ module.exports = {
ctx.body = pm.sanitize(unpublishedEntry, { action: ACTIONS.read });
},
async findRelationList(ctx) {
const { model, targetField } = ctx.params;
const { _component, ...query } = ctx.request.query;
const contentManagerServices = strapi.plugins['content-manager'].services;
if (!targetField) {
return ctx.badRequest();
}
const modelDef = _component ? strapi.db.getModel(_component) : strapi.db.getModel(model);
if (!modelDef) {
return ctx.notFound('model.notFound');
}
const attr = modelDef.attributes[targetField];
if (!attr) {
return ctx.badRequest('targetField.invalid');
}
const target = strapi.db.getModelByAssoc(attr);
if (!target) {
return ctx.notFound('target.notFound');
}
const contentManagerService = contentManagerServices.contentmanager;
let entities = [];
if (_.has(ctx.request.query, '_q')) {
entities = await contentManagerService.search(target.uid, query);
} else {
entities = await contentManagerService.fetchAll(target.uid, query);
}
if (!entities) {
return ctx.notFound();
}
const modelConfig = _component
? await contentManagerServices.components.getConfiguration(modelDef.uid)
: await contentManagerServices.contenttypes.getConfiguration(modelDef.uid);
const field = _.get(modelConfig, `metadatas.${targetField}.edit.mainField`, 'id');
const pickFields = [field, 'id', target.primaryKey, PUBLISHED_AT_ATTRIBUTE];
const sanitize = d => _.pick(d, pickFields);
ctx.body = _.isArray(entities) ? entities.map(sanitize) : sanitize(entities);
},
};

View File

@ -1,10 +1,10 @@
'use strict';
const createContext = require('../../../../test/helpers/create-context');
const ContentManager = require('../ContentManager');
const relations = require('../relations');
describe('ContentManager', () => {
describe('findRelationList', () => {
describe('Relations', () => {
describe('find', () => {
test('Fails on model not found', async () => {
const notFound = jest.fn();
const ctx = createContext(
@ -26,7 +26,7 @@ describe('ContentManager', () => {
},
};
await ContentManager.findRelationList(ctx);
await relations.find(ctx);
expect(notFound).toHaveBeenCalledWith('model.notFound');
});
@ -55,7 +55,7 @@ describe('ContentManager', () => {
},
};
await ContentManager.findRelationList(ctx);
await relations.find(ctx);
expect(badRequest).toHaveBeenCalledWith('targetField.invalid');
});
@ -84,7 +84,7 @@ describe('ContentManager', () => {
},
};
await ContentManager.findRelationList(ctx);
await relations.find(ctx);
expect(notFound).toHaveBeenCalledWith('target.notFound');
});
@ -111,8 +111,8 @@ describe('ContentManager', () => {
plugins: {
'content-manager': {
services: {
contenttypes: {
getConfiguration() {
'content-types': {
findConfiguration() {
return {
metadatas: {
target: {
@ -145,7 +145,7 @@ describe('ContentManager', () => {
},
};
await ContentManager.findRelationList(ctx);
await relations.find(ctx);
expect(ctx.body).toEqual([
{

View File

@ -0,0 +1,57 @@
'use strict';
const { has, prop, pick } = require('lodash/fp');
const { PUBLISHED_AT_ATTRIBUTE } = require('strapi-utils').contentTypes.constants;
const { getService } = require('../utils');
module.exports = {
async find(ctx) {
const { model, targetField } = ctx.params;
const { _component, ...query } = ctx.request.query;
if (!targetField) {
return ctx.badRequest();
}
const modelDef = _component ? strapi.db.getModel(_component) : strapi.db.getModel(model);
if (!modelDef) {
return ctx.notFound('model.notFound');
}
const attr = modelDef.attributes[targetField];
if (!attr) {
return ctx.badRequest('targetField.invalid');
}
const target = strapi.db.getModelByAssoc(attr);
if (!target) {
return ctx.notFound('target.notFound');
}
const contentManagerService = getService('contentmanager');
let entities = [];
if (has('_q', ctx.request.query)) {
entities = await contentManagerService.search(target.uid, query);
} else {
entities = await contentManagerService.fetchAll(target.uid, query);
}
if (!entities) {
return ctx.notFound();
}
const modelConfig = _component
? await getService('components').findConfiguration(modelDef)
: await getService('content-types').findConfiguration(modelDef);
const field = prop(`metadatas.${targetField}.edit.mainField`, modelConfig) || 'id';
const pickFields = [field, 'id', target.primaryKey, PUBLISHED_AT_ATTRIBUTE];
ctx.body = entities.map(pick(pickFields));
},
};

View File

@ -276,14 +276,50 @@ paths:
description: Suggestion if request value is not available
# Relationships
/content-manager/relationships/{modelUid}/{targetField}:
/content-manager/relations/{model}/{targetField}:
get:
tags:
- Relational fields
description: Fetch list of possible related content
parameters:
- $ref: '#/components/parameters/model'
- in: path
name: targetField
schema:
type: string
required: true
description: name of the field in the model that holds the relation
- in: query
name: _component
schema:
type: string
description: Component uid if the targetField is in a component
responses:
200:
description: Returns a list of sanitized entries based of the relational attribute info
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
oneOf:
- type: string
- type: integer
'[primaryKey]':
oneOf:
- type: string
- type: integer
'[mainField]':
type: string
description: value of the mainField of the entry
published_at:
type: date
# Collection type
/content-manager/collection-type/{modelUid}:
/content-manager/collection-type/{model}:
get:
tags:
- Collection Types content management
@ -292,12 +328,12 @@ paths:
tags:
- Collection Types content management
description: Create an entry
/content-manager/collection-type/{modelUid}/actions/bulkDelete:
/content-manager/collection-type/{model}/actions/bulkDelete:
post:
tags:
- Collection Types content management
description: Bulk delete entries
/content-manager/collection-type/{modelUid}/{id}:
/content-manager/collection-type/{model}/{id}:
get:
tags:
- Collection Types content management
@ -310,19 +346,19 @@ paths:
tags:
- Collection Types content management
description: Delete one entry
/content-manager/collection-type/{modelUid}/{id}/actions/publish:
/content-manager/collection-type/{model}/{id}/actions/publish:
post:
tags:
- Collection Types content management
description: Publish one entry
/content-manager/collection-type/{modelUid}/{id}/actions/unpublish:
/content-manager/collection-type/{model}/{id}/actions/unpublish:
post:
tags:
- Collection Types content management
description: Unpublish one entry
# Single type
/content-manager/single-type/{modelUid}:
/content-manager/single-type/{model}:
get:
tags:
- Single Types content management
@ -332,11 +368,11 @@ paths:
delete:
tags:
- Single Types content management
/content-manager/single-type/{modelUid}/actions/publish:
/content-manager/single-type/{model}/actions/publish:
post:
tags:
- Single Types content management
/content-manager/single-type/{modelUid}/actions/unpublish:
/content-manager/single-type/{model}/actions/unpublish:
post:
tags:
- Single Types content management
@ -521,6 +557,14 @@ components:
$ref: '#/components/schemas/kind'
description: The number of items to skip before starting to collect the result set
model:
in: path
name: model
schema:
type: string
required: true
description: Model uid
securitySchemes:
bearerAuth:
type: http

View File

@ -141,7 +141,7 @@ describe('Relation-list route', () => {
test('Can get relation-list for products of a shop', async () => {
const res = await rq({
method: 'GET',
url: '/content-manager/explorer/application::shop.shop/relation-list/products',
url: '/content-manager/relations/application::shop.shop/products',
});
expect(res.body).toHaveLength(data.products.length);
@ -165,7 +165,7 @@ describe('Relation-list route', () => {
test('Can get relation-list for products of a shop', async () => {
const res = await rq({
method: 'GET',
url: '/content-manager/explorer/application::shop.shop/relation-list/products',
url: '/content-manager/relations/application::shop.shop/products',
});
expect(res.body).toHaveLength(data.products.length);