strapi-plugin-comments 2.0.4 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/admin/src/components/FormSwitch/index.js +18 -0
- package/admin/src/components/FormSwitch/styles.js +8 -0
- package/admin/src/pages/Settings/index.js +76 -60
- package/admin/src/translations/en.json +9 -7
- package/package.json +3 -3
- package/server/controllers/client.js +11 -2
- package/server/controllers/utils/__tests__/parsers.test.js +94 -0
- package/server/controllers/utils/parsers.js +15 -13
package/README.md
CHANGED
|
@@ -84,7 +84,7 @@ In our minimum support we're following [official Node.js releases timelines](htt
|
|
|
84
84
|
|
|
85
85
|
**Supported Strapi versions**:
|
|
86
86
|
|
|
87
|
-
- Strapi v4.0.
|
|
87
|
+
- Strapi v4.0.8 (recently tested)
|
|
88
88
|
- Strapi v4.x
|
|
89
89
|
|
|
90
90
|
> This plugin is designed for **Strapi v4** and is not working with v3.x. To get version for **Strapi v3** install version [v1.x](https://github.com/VirtusLab-Open-Source/strapi-plugin-comments/tree/strapi-v3).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Form Switch
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { Stack } from '@strapi/design-system/Stack';
|
|
9
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
10
|
+
import { StyledSwitch } from './styles';
|
|
11
|
+
|
|
12
|
+
const FormSwitch = ({ label, hint, ...props }) => (<Stack size={1}>
|
|
13
|
+
{ label && (<Typography variant="pi" as="label" fontWeight="bold" textColor="neutral800">{ label }</Typography>) }
|
|
14
|
+
<StyledSwitch label={label} { ...props } />
|
|
15
|
+
{ hint && (<Typography variant="pi" as="label" textColor="neutral600">{ hint }</Typography>) }
|
|
16
|
+
</Stack>);
|
|
17
|
+
|
|
18
|
+
export default FormSwitch;
|
|
@@ -12,22 +12,20 @@ import {
|
|
|
12
12
|
useFocusWhenNavigate,
|
|
13
13
|
useOverlayBlocker,
|
|
14
14
|
} from '@strapi/helper-plugin';
|
|
15
|
+
import { Accordion, AccordionToggle, AccordionContent, AccordionGroup } from '@strapi/design-system/Accordion';
|
|
15
16
|
import { Main } from '@strapi/design-system/Main';
|
|
16
17
|
import { ContentLayout, HeaderLayout } from '@strapi/design-system/Layout';
|
|
17
18
|
import { Button } from '@strapi/design-system/Button';
|
|
18
19
|
import { Box } from '@strapi/design-system/Box';
|
|
19
20
|
import { Stack } from '@strapi/design-system/Stack';
|
|
21
|
+
import { Switch } from '@strapi/design-system/Switch';
|
|
20
22
|
import { Typography } from '@strapi/design-system/Typography';
|
|
21
23
|
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
|
22
24
|
import { ToggleInput } from '@strapi/design-system/ToggleInput';
|
|
23
25
|
import { Select, Option } from '@strapi/design-system/Select';
|
|
24
26
|
import { useNotifyAT } from '@strapi/design-system/LiveRegions';
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
CardBody,
|
|
28
|
-
CardContent,
|
|
29
|
-
} from '@strapi/design-system/Card';
|
|
30
|
-
import { Check, Refresh, Play } from '@strapi/icons';
|
|
27
|
+
import { Tooltip } from '@strapi/design-system/Tooltip';
|
|
28
|
+
import { Check, Refresh, Play, Information } from '@strapi/icons';
|
|
31
29
|
|
|
32
30
|
import pluginPermissions from '../../permissions';
|
|
33
31
|
import useConfig from '../../hooks/useConfig';
|
|
@@ -35,6 +33,7 @@ import { fetchAllContentTypes, fetchRoles } from './utils/api';
|
|
|
35
33
|
import { getMessage, parseRegExp } from '../../utils';
|
|
36
34
|
import ConfirmationDialog from '../../components/ConfirmationDialog';
|
|
37
35
|
import { RestartAlert } from './components/RestartAlert/styles';
|
|
36
|
+
import FormSwitch from '../../components/FormSwitch';
|
|
38
37
|
|
|
39
38
|
|
|
40
39
|
const Settings = () => {
|
|
@@ -58,6 +57,7 @@ const Settings = () => {
|
|
|
58
57
|
|
|
59
58
|
const [restoreConfigmationVisible, setRestoreConfigmationVisible] = useState(false);
|
|
60
59
|
const [restartRequired, setRestartRequired] = useState(false);
|
|
60
|
+
const [contentTypeExpanded, setContentTypeExpanded] = useState(undefined);
|
|
61
61
|
|
|
62
62
|
const { fetch, restartMutation, submitMutation, restoreMutation } = useConfig(toggleNotification);
|
|
63
63
|
const { data: configData, isLoading: isConfigLoading, err: configErr } = fetch;
|
|
@@ -166,6 +166,8 @@ const Settings = () => {
|
|
|
166
166
|
};
|
|
167
167
|
const handleRestartDiscard = () => setRestartRequired(false);
|
|
168
168
|
|
|
169
|
+
const handleSetContentTypeExpanded = key => setContentTypeExpanded(key === contentTypeExpanded ? undefined : key);
|
|
170
|
+
|
|
169
171
|
const boxDefaultProps = {
|
|
170
172
|
background: "neutral0",
|
|
171
173
|
hasRadius: true,
|
|
@@ -201,7 +203,7 @@ const Settings = () => {
|
|
|
201
203
|
}
|
|
202
204
|
/>
|
|
203
205
|
<ContentLayout>
|
|
204
|
-
<Stack size={
|
|
206
|
+
<Stack size={4}>
|
|
205
207
|
{ restartRequired && (
|
|
206
208
|
<RestartAlert
|
|
207
209
|
closeLabel={getMessage('page.settings.actions.restart.alert.cancel')}
|
|
@@ -234,6 +236,72 @@ const Settings = () => {
|
|
|
234
236
|
(<Option key={uid} value={uid}>{displayName}</Option>))}
|
|
235
237
|
</Select>
|
|
236
238
|
</GridItem>
|
|
239
|
+
{ !isEmpty(values.enabledCollections) && (
|
|
240
|
+
<GridItem col={12}>
|
|
241
|
+
<AccordionGroup
|
|
242
|
+
label={getMessage('page.settings.form.contentTypesSettings.label')}
|
|
243
|
+
labelAction={<Tooltip description={getMessage('page.settings.form.contentTypesSettings.tooltip')}>
|
|
244
|
+
<Information aria-hidden={true} />
|
|
245
|
+
</Tooltip>}>
|
|
246
|
+
{ orderBy(values.enabledCollections).map(uid => {
|
|
247
|
+
const { schema: { displayName, attributes = {} } } = allCollections.find(_ => _.uid === uid);
|
|
248
|
+
const stringAttributes = Object.keys(attributes).filter(_ => attributes[_].type === 'string');
|
|
249
|
+
const key = `collectionSettings-${uid}`;
|
|
250
|
+
return (<Accordion
|
|
251
|
+
expanded={contentTypeExpanded === key}
|
|
252
|
+
toggle={() => handleSetContentTypeExpanded(key)}
|
|
253
|
+
key={key}
|
|
254
|
+
id={key}
|
|
255
|
+
size="S">
|
|
256
|
+
<AccordionToggle title={displayName} togglePosition="left" />
|
|
257
|
+
<AccordionContent>
|
|
258
|
+
<Box padding={6}>
|
|
259
|
+
<Stack size={4}>
|
|
260
|
+
<FormSwitch
|
|
261
|
+
name={`collectionSettings-${uid}-approvalFlow`}
|
|
262
|
+
label={getMessage('page.settings.form.approvalFlow.label')}
|
|
263
|
+
hint={getMessage({
|
|
264
|
+
id: 'page.settings.form.approvalFlow.hint',
|
|
265
|
+
props: { name: displayName },
|
|
266
|
+
})}
|
|
267
|
+
selected={values.approvalFlow.includes(uid)}
|
|
268
|
+
onChange={() => setFieldValue('approvalFlow', changeApprovalFlowFor(uid, values.approvalFlow, !values.approvalFlow.includes(uid)), [])}
|
|
269
|
+
onLabel={getMessage('compontents.toogle.enabled')}
|
|
270
|
+
offLabel={getMessage('compontents.toogle.disabled')}
|
|
271
|
+
disabled={restartRequired}
|
|
272
|
+
visibleLabels />
|
|
273
|
+
{ !isEmpty(stringAttributes) && (<Select
|
|
274
|
+
name={`collectionSettings-${uid}-entryLabel`}
|
|
275
|
+
label={getMessage('page.settings.form.entryLabel.label')}
|
|
276
|
+
placeholder={getMessage('page.settings.form.entryLabel.placeholder')}
|
|
277
|
+
hint={getMessage('page.settings.form.entryLabel.hint')}
|
|
278
|
+
onClear={() => setFieldValue('entryLabel', changeEntryLabelFor(uid, values.entryLabel))}
|
|
279
|
+
value={values.entryLabel[uid] || []}
|
|
280
|
+
onChange={(value) => setFieldValue('entryLabel', changeEntryLabelFor(uid, values.entryLabel, value))}
|
|
281
|
+
multi
|
|
282
|
+
withTags
|
|
283
|
+
disabled={restartRequired}
|
|
284
|
+
>
|
|
285
|
+
{ stringAttributes.map(key =>
|
|
286
|
+
(<Option key={`collectionSettings-${uid}-entryLabel-${key}`} value={key}>{ capitalize(key.split('_').join(' ')) }</Option>))}
|
|
287
|
+
</Select>) }
|
|
288
|
+
</Stack>
|
|
289
|
+
</Box>
|
|
290
|
+
</AccordionContent>
|
|
291
|
+
</Accordion>);
|
|
292
|
+
})}
|
|
293
|
+
</AccordionGroup>
|
|
294
|
+
</GridItem>)}
|
|
295
|
+
</Grid>
|
|
296
|
+
</Stack>
|
|
297
|
+
</Box>
|
|
298
|
+
|
|
299
|
+
<Box {...boxDefaultProps}>
|
|
300
|
+
<Stack size={4}>
|
|
301
|
+
<Typography variant="delta" as="h2">
|
|
302
|
+
{getMessage('page.settings.section.additional')}
|
|
303
|
+
</Typography>
|
|
304
|
+
<Grid gap={4}>
|
|
237
305
|
<GridItem col={6} xs={12}>
|
|
238
306
|
<Select
|
|
239
307
|
name="moderatorRoles"
|
|
@@ -278,64 +346,12 @@ const Settings = () => {
|
|
|
278
346
|
</Grid>
|
|
279
347
|
</Stack>
|
|
280
348
|
</Box>
|
|
281
|
-
{ !isEmpty(values.enabledCollections) && (<Box {...boxDefaultProps}>
|
|
282
|
-
<Stack size={4}>
|
|
283
|
-
<Typography variant="delta" as="h2">
|
|
284
|
-
{getMessage('page.settings.section.collections')}
|
|
285
|
-
</Typography>
|
|
286
|
-
<Grid gap={4}>
|
|
287
|
-
{ orderBy(values.enabledCollections).map(uid => {
|
|
288
|
-
const { schema: { displayName, attributes = {} } } = allCollections.find(_ => _.uid === uid);
|
|
289
|
-
const stringAttributes = Object.keys(attributes).filter(_ => attributes[_].type === 'string');
|
|
290
|
-
return (<GridItem key={`collectionSettings-${uid}`} col={6} s={12} xs={12}>
|
|
291
|
-
<Card background="primary100" borderColor="primary200">
|
|
292
|
-
<CardBody>
|
|
293
|
-
<CardContent style={{ width: '100%' }}>
|
|
294
|
-
<Stack size={4}>
|
|
295
|
-
<Typography variant="epsilon" fontWeight="semibold" as="h3">{ displayName }</Typography>
|
|
296
|
-
<ToggleInput
|
|
297
|
-
name={`collectionSettings-${uid}-approvalFlow`}
|
|
298
|
-
label={getMessage('page.settings.form.approvalFlow.label')}
|
|
299
|
-
hind={getMessage({
|
|
300
|
-
id: 'page.settings.form.approvalFlow.hint',
|
|
301
|
-
params: { name: displayName },
|
|
302
|
-
})}
|
|
303
|
-
checked={values.approvalFlow.includes(uid)}
|
|
304
|
-
onChange={({ target: { checked } }) => setFieldValue('approvalFlow', changeApprovalFlowFor(uid, values.approvalFlow, checked), [])}
|
|
305
|
-
onLabel={getMessage('compontents.toogle.enabled')}
|
|
306
|
-
offLabel={getMessage('compontents.toogle.disabled')}
|
|
307
|
-
disabled={restartRequired}
|
|
308
|
-
/>
|
|
309
|
-
{ !isEmpty(stringAttributes) && (<Select
|
|
310
|
-
name={`collectionSettings-${uid}-entryLabel`}
|
|
311
|
-
label={getMessage('page.settings.form.entryLabel.label')}
|
|
312
|
-
placeholder={getMessage('page.settings.form.entryLabel.placeholder')}
|
|
313
|
-
hint={getMessage('page.settings.form.entryLabel.hint')}
|
|
314
|
-
onClear={() => setFieldValue('entryLabel', changeEntryLabelFor(uid, values.entryLabel))}
|
|
315
|
-
value={values.entryLabel[uid] || []}
|
|
316
|
-
onChange={(value) => setFieldValue('entryLabel', changeEntryLabelFor(uid, values.entryLabel, value))}
|
|
317
|
-
multi
|
|
318
|
-
withTags
|
|
319
|
-
disabled={restartRequired}
|
|
320
|
-
>
|
|
321
|
-
{ stringAttributes.map(key =>
|
|
322
|
-
(<Option key={`collectionSettings-${uid}-entryLabel-${key}`} value={key}>{ capitalize(key.split('_').join(' ')) }</Option>))}
|
|
323
|
-
</Select>) }
|
|
324
|
-
</Stack>
|
|
325
|
-
</CardContent>
|
|
326
|
-
</CardBody>
|
|
327
|
-
</Card>
|
|
328
|
-
</GridItem>);
|
|
329
|
-
})}
|
|
330
|
-
</Grid>
|
|
331
|
-
</Stack>
|
|
332
|
-
</Box>)}
|
|
333
349
|
|
|
334
350
|
<CheckPermissions permissions={pluginPermissions.settingsChange}>
|
|
335
351
|
<Box {...boxDefaultProps}>
|
|
336
352
|
<Stack size={4}>
|
|
337
353
|
<Stack size={2}>
|
|
338
|
-
<Typography variant="delta"
|
|
354
|
+
<Typography variant="delta" as="h2">
|
|
339
355
|
{getMessage('page.settings.section.restore')}
|
|
340
356
|
</Typography>
|
|
341
357
|
<Typography variant="pi"as="h4">
|
|
@@ -85,15 +85,17 @@
|
|
|
85
85
|
"page.settings.header.title": "Comments",
|
|
86
86
|
"page.settings.header.description": "Configure your comments moderation capabilities",
|
|
87
87
|
"page.settings.actions.submit": "Save",
|
|
88
|
-
"page.settings.actions.restore": "Restore",
|
|
88
|
+
"page.settings.actions.restore": "Restore default settings",
|
|
89
89
|
"page.settings.actions.restart": "Restart Strapi",
|
|
90
90
|
"page.settings.section.general": "General configuration",
|
|
91
|
-
"page.settings.section.
|
|
92
|
-
"page.settings.section.restore": "Restore default
|
|
91
|
+
"page.settings.section.additional": "Additional configuration",
|
|
92
|
+
"page.settings.section.restore": "Restore default settings",
|
|
93
93
|
"page.settings.section.restore.subtitle": "Discarding all of applied settings and getting back to plugin default ones. Use reasonable.",
|
|
94
|
-
"page.settings.form.enabledCollections.label": "Enable
|
|
94
|
+
"page.settings.form.enabledCollections.label": "Enable comments only for",
|
|
95
95
|
"page.settings.form.enabledCollections.placeholder": "Select one or more collection",
|
|
96
|
-
"page.settings.form.enabledCollections.hint": "If none
|
|
96
|
+
"page.settings.form.enabledCollections.hint": "If none is selected, all the content types are enabled",
|
|
97
|
+
"page.settings.form.contentTypesSettings.label": "Content types",
|
|
98
|
+
"page.settings.form.contentTypesSettings.tooltip": "Custom configuration per content type",
|
|
97
99
|
"page.settings.form.moderatorRoles.label": "Send significant notifications to",
|
|
98
100
|
"page.settings.form.moderatorRoles.placeholder": "Select one or more roles",
|
|
99
101
|
"page.settings.form.moderatorRoles.hint": "Roles which are going to be notified by the plugin about significant actions to perform",
|
|
@@ -101,8 +103,8 @@
|
|
|
101
103
|
"page.settings.form.badWords.hint": "If enabled, every post / update of comment is going to be checked against bad wording",
|
|
102
104
|
"page.settings.form.gqlAuth.label": "GraphQL queries authorization",
|
|
103
105
|
"page.settings.form.gqlAuth.hint": "If enabled, GraphQL API queries & mutations can be triggered only by Authenticated Strapi users. Otherwise API remains open.",
|
|
104
|
-
"page.settings.form.approvalFlow.label": "
|
|
105
|
-
"page.settings.form.approvalFlow.hint": "Comments
|
|
106
|
+
"page.settings.form.approvalFlow.label": "Approval flow",
|
|
107
|
+
"page.settings.form.approvalFlow.hint": "Comments associated with content type \"{name}\" are going to be taken through manual approval flow",
|
|
106
108
|
"page.settings.form.entryLabel.label": "Title fields",
|
|
107
109
|
"page.settings.form.entryLabel.placeholder": "Select at least one or leave empty to apply defaults",
|
|
108
110
|
"page.settings.form.entryLabel.hint": "If left empty title rendering is going to take following ordered fields: \"Title\", \"Subject\" & \"Name\"",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-comments",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "Strapi - Comments plugin",
|
|
5
5
|
"strapi": {
|
|
6
6
|
"name": "comments",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"url": "https://github.com/VirtusLab-Open-Source/strapi-plugin-comments"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@strapi/utils": "^4.0.
|
|
22
|
-
"@strapi/plugin-graphql": "^4.0.
|
|
21
|
+
"@strapi/utils": "^4.0.8",
|
|
22
|
+
"@strapi/plugin-graphql": "^4.0.8",
|
|
23
23
|
"bad-words": "^3.0.4"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
@@ -13,9 +13,15 @@ module.exports = {
|
|
|
13
13
|
async findAllFlat(ctx) {
|
|
14
14
|
const { params = {}, query, sort, pagination } = ctx;
|
|
15
15
|
const { relation } = parseParams(params);
|
|
16
|
+
|
|
17
|
+
const { sort: querySort, pagination: queryPagination, ...filterQuery } = query || {};
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
console.log(filterQuery);
|
|
21
|
+
|
|
16
22
|
try {
|
|
17
23
|
return this.getService('common')
|
|
18
|
-
.findAllFlat(flatInput(relation,
|
|
24
|
+
.findAllFlat(flatInput(relation, filterQuery, sort || querySort, pagination || queryPagination));
|
|
19
25
|
} catch (e) {
|
|
20
26
|
throwError(ctx, e);
|
|
21
27
|
}
|
|
@@ -24,10 +30,13 @@ module.exports = {
|
|
|
24
30
|
async findAllInHierarchy(ctx) {
|
|
25
31
|
const { params = {}, query, sort } = ctx;
|
|
26
32
|
const { relation } = parseParams(params);
|
|
33
|
+
|
|
34
|
+
const { sort: querySort, ...filterQuery } = query || {};
|
|
35
|
+
|
|
27
36
|
try {
|
|
28
37
|
return await this.getService('common')
|
|
29
38
|
.findAllInHierarchy({
|
|
30
|
-
...flatInput(relation,
|
|
39
|
+
...flatInput(relation, filterQuery, sort || querySort),
|
|
31
40
|
dropBlockedThreads: true,
|
|
32
41
|
});
|
|
33
42
|
} catch (e) {
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const { flatInput } = require('../parsers');
|
|
2
|
+
|
|
3
|
+
jest.mock
|
|
4
|
+
|
|
5
|
+
describe('Test Comments controller parsers utils', () => {
|
|
6
|
+
|
|
7
|
+
describe('Flat Input properties', () => {
|
|
8
|
+
const relation = 'api::test.relation';
|
|
9
|
+
const filters = {
|
|
10
|
+
content: {
|
|
11
|
+
'$eq': 'Test'
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const sort = {
|
|
15
|
+
createdAt: 'desc'
|
|
16
|
+
};
|
|
17
|
+
const pagination = {
|
|
18
|
+
page: 3,
|
|
19
|
+
pageSize: 5,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
test('Should contain default property \'populate\'', () => {
|
|
23
|
+
expect(flatInput()).toHaveProperty(['populate', 'threadOf', 'populate', 'authorUser'], true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('Should assign relation to \'query\'', () => {
|
|
27
|
+
expect(flatInput(relation)).toHaveProperty(['query', 'related'], relation);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('Should assign filters to \'query\'', () => {
|
|
31
|
+
const result = flatInput(relation, filters);
|
|
32
|
+
expect(result).toHaveProperty(['query', 'content', '$eq'], filters.content['$eq']);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('Should assign not allow to overwrite \'removed\' in $or operator', () => {
|
|
36
|
+
const overwrittenOrOperator = {
|
|
37
|
+
$or: [{ removed: true }, { removed: false }, { removed: null }]
|
|
38
|
+
};
|
|
39
|
+
const result = flatInput(relation, overwrittenOrOperator);
|
|
40
|
+
expect(result).toHaveProperty(['query', '$or', 0, 'removed', '$null'], true);
|
|
41
|
+
expect(result).toHaveProperty(['query', '$or', 1, 'removed'], false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('Should assign sort', () => {
|
|
45
|
+
const result = flatInput(relation, filters, sort);
|
|
46
|
+
expect(result).toHaveProperty(['sort', 'createdAt'], sort.createdAt);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('Should assign pagination', () => {
|
|
50
|
+
const result = flatInput(relation, filters, sort, pagination);
|
|
51
|
+
expect(result).toHaveProperty(['pagination', 'page'], pagination.page);
|
|
52
|
+
expect(result).toHaveProperty(['pagination', 'pageSize'], pagination.pageSize);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('Should build complex output', () => {
|
|
56
|
+
const day = 24 * 60 * 60 * 1000;
|
|
57
|
+
const now = Date.now().toString();
|
|
58
|
+
const oneDayBefore = new Date(Date.now().valueOf() - day).toString();
|
|
59
|
+
const complexFilters = {
|
|
60
|
+
'$or': [{
|
|
61
|
+
createdAt: {
|
|
62
|
+
'$lte': oneDayBefore
|
|
63
|
+
}
|
|
64
|
+
}, {
|
|
65
|
+
createdAt: {
|
|
66
|
+
'$eq': now
|
|
67
|
+
}
|
|
68
|
+
}],
|
|
69
|
+
content: {
|
|
70
|
+
'$contains': 'Test'
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const complexSort = [{
|
|
75
|
+
createdAt: 'desc'
|
|
76
|
+
}, {
|
|
77
|
+
title: 'asc'
|
|
78
|
+
}];
|
|
79
|
+
|
|
80
|
+
const result = flatInput(relation, complexFilters, complexSort, pagination);
|
|
81
|
+
|
|
82
|
+
expect(result).toHaveProperty(['query', 'related'], relation);
|
|
83
|
+
expect(result).toHaveProperty(['query', '$or', 0, 'createdAt', '$lte'], oneDayBefore);
|
|
84
|
+
expect(result).toHaveProperty(['query', '$or', 1, 'createdAt', '$eq'], now);
|
|
85
|
+
expect(result).toHaveProperty(['query', '$or', 2, 'removed', '$null'], true);
|
|
86
|
+
expect(result).toHaveProperty(['query', '$or', 3, 'removed'], false);
|
|
87
|
+
expect(result).toHaveProperty(['query', 'content', '$contains'], complexFilters.content['$contains']);
|
|
88
|
+
expect(result).toHaveProperty(['sort', 0, 'createdAt'], complexSort[0].createdAt);
|
|
89
|
+
expect(result).toHaveProperty(['sort', 1, 'title'], complexSort[1].title);
|
|
90
|
+
expect(result).toHaveProperty(['pagination', 'page'], pagination.page);
|
|
91
|
+
expect(result).toHaveProperty(['pagination', 'pageSize'], pagination.pageSize);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -2,20 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
flatInput(relation, query, sort, pagination) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const filters = query?.filters || query;
|
|
6
|
+
const orOperator = (filters?.$or || []).filter(_ => !Object.keys(_).includes('removed'));
|
|
7
|
+
return {
|
|
8
|
+
query: {
|
|
9
|
+
...filters,
|
|
10
|
+
$or: [...orOperator, { removed: { $null: true } }, { removed: false }],
|
|
11
|
+
related: relation,
|
|
12
|
+
},
|
|
13
|
+
populate: {
|
|
14
|
+
threadOf: {
|
|
15
|
+
populate: { authorUser: true },
|
|
10
16
|
},
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
pagination,
|
|
17
|
-
sort,
|
|
18
|
-
};
|
|
17
|
+
},
|
|
18
|
+
pagination,
|
|
19
|
+
sort,
|
|
20
|
+
};
|
|
19
21
|
},
|
|
20
22
|
|
|
21
23
|
};
|