mirror of
https://github.com/strapi/strapi.git
synced 2025-11-13 08:38:09 +00:00
Refactor relation-list route
Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com>
This commit is contained in:
parent
b37a92c60f
commit
7509a16152
@ -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",
|
"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",
|
"method": "POST",
|
||||||
"path": "/explorer/:model",
|
"path": "/explorer/:model",
|
||||||
|
|||||||
@ -342,56 +342,4 @@ module.exports = {
|
|||||||
|
|
||||||
ctx.body = pm.sanitize(unpublishedEntry, { action: ACTIONS.read });
|
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);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const createContext = require('../../../../test/helpers/create-context');
|
const createContext = require('../../../../test/helpers/create-context');
|
||||||
const ContentManager = require('../ContentManager');
|
const relations = require('../relations');
|
||||||
|
|
||||||
describe('ContentManager', () => {
|
describe('Relations', () => {
|
||||||
describe('findRelationList', () => {
|
describe('find', () => {
|
||||||
test('Fails on model not found', async () => {
|
test('Fails on model not found', async () => {
|
||||||
const notFound = jest.fn();
|
const notFound = jest.fn();
|
||||||
const ctx = createContext(
|
const ctx = createContext(
|
||||||
@ -26,7 +26,7 @@ describe('ContentManager', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
await ContentManager.findRelationList(ctx);
|
await relations.find(ctx);
|
||||||
|
|
||||||
expect(notFound).toHaveBeenCalledWith('model.notFound');
|
expect(notFound).toHaveBeenCalledWith('model.notFound');
|
||||||
});
|
});
|
||||||
@ -55,7 +55,7 @@ describe('ContentManager', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
await ContentManager.findRelationList(ctx);
|
await relations.find(ctx);
|
||||||
|
|
||||||
expect(badRequest).toHaveBeenCalledWith('targetField.invalid');
|
expect(badRequest).toHaveBeenCalledWith('targetField.invalid');
|
||||||
});
|
});
|
||||||
@ -84,7 +84,7 @@ describe('ContentManager', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
await ContentManager.findRelationList(ctx);
|
await relations.find(ctx);
|
||||||
|
|
||||||
expect(notFound).toHaveBeenCalledWith('target.notFound');
|
expect(notFound).toHaveBeenCalledWith('target.notFound');
|
||||||
});
|
});
|
||||||
@ -111,8 +111,8 @@ describe('ContentManager', () => {
|
|||||||
plugins: {
|
plugins: {
|
||||||
'content-manager': {
|
'content-manager': {
|
||||||
services: {
|
services: {
|
||||||
contenttypes: {
|
'content-types': {
|
||||||
getConfiguration() {
|
findConfiguration() {
|
||||||
return {
|
return {
|
||||||
metadatas: {
|
metadatas: {
|
||||||
target: {
|
target: {
|
||||||
@ -145,7 +145,7 @@ describe('ContentManager', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
await ContentManager.findRelationList(ctx);
|
await relations.find(ctx);
|
||||||
|
|
||||||
expect(ctx.body).toEqual([
|
expect(ctx.body).toEqual([
|
||||||
{
|
{
|
||||||
@ -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));
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -276,14 +276,50 @@ paths:
|
|||||||
description: Suggestion if request value is not available
|
description: Suggestion if request value is not available
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
/content-manager/relationships/{modelUid}/{targetField}:
|
/content-manager/relations/{model}/{targetField}:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Relational fields
|
- Relational fields
|
||||||
description: Fetch list of possible related content
|
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
|
# Collection type
|
||||||
/content-manager/collection-type/{modelUid}:
|
/content-manager/collection-type/{model}:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Collection Types content management
|
- Collection Types content management
|
||||||
@ -292,12 +328,12 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Collection Types content management
|
- Collection Types content management
|
||||||
description: Create an entry
|
description: Create an entry
|
||||||
/content-manager/collection-type/{modelUid}/actions/bulkDelete:
|
/content-manager/collection-type/{model}/actions/bulkDelete:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- Collection Types content management
|
- Collection Types content management
|
||||||
description: Bulk delete entries
|
description: Bulk delete entries
|
||||||
/content-manager/collection-type/{modelUid}/{id}:
|
/content-manager/collection-type/{model}/{id}:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Collection Types content management
|
- Collection Types content management
|
||||||
@ -310,19 +346,19 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Collection Types content management
|
- Collection Types content management
|
||||||
description: Delete one entry
|
description: Delete one entry
|
||||||
/content-manager/collection-type/{modelUid}/{id}/actions/publish:
|
/content-manager/collection-type/{model}/{id}/actions/publish:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- Collection Types content management
|
- Collection Types content management
|
||||||
description: Publish one entry
|
description: Publish one entry
|
||||||
/content-manager/collection-type/{modelUid}/{id}/actions/unpublish:
|
/content-manager/collection-type/{model}/{id}/actions/unpublish:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- Collection Types content management
|
- Collection Types content management
|
||||||
description: Unpublish one entry
|
description: Unpublish one entry
|
||||||
|
|
||||||
# Single type
|
# Single type
|
||||||
/content-manager/single-type/{modelUid}:
|
/content-manager/single-type/{model}:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Single Types content management
|
- Single Types content management
|
||||||
@ -332,11 +368,11 @@ paths:
|
|||||||
delete:
|
delete:
|
||||||
tags:
|
tags:
|
||||||
- Single Types content management
|
- Single Types content management
|
||||||
/content-manager/single-type/{modelUid}/actions/publish:
|
/content-manager/single-type/{model}/actions/publish:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- Single Types content management
|
- Single Types content management
|
||||||
/content-manager/single-type/{modelUid}/actions/unpublish:
|
/content-manager/single-type/{model}/actions/unpublish:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- Single Types content management
|
- Single Types content management
|
||||||
@ -521,6 +557,14 @@ components:
|
|||||||
$ref: '#/components/schemas/kind'
|
$ref: '#/components/schemas/kind'
|
||||||
description: The number of items to skip before starting to collect the result set
|
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:
|
securitySchemes:
|
||||||
bearerAuth:
|
bearerAuth:
|
||||||
type: http
|
type: http
|
||||||
|
|||||||
@ -141,7 +141,7 @@ describe('Relation-list route', () => {
|
|||||||
test('Can get relation-list for products of a shop', async () => {
|
test('Can get relation-list for products of a shop', async () => {
|
||||||
const res = await rq({
|
const res = await rq({
|
||||||
method: 'GET',
|
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);
|
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 () => {
|
test('Can get relation-list for products of a shop', async () => {
|
||||||
const res = await rq({
|
const res = await rq({
|
||||||
method: 'GET',
|
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);
|
expect(res.body).toHaveLength(data.products.length);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user