Skip to content

Commit 267d852

Browse files
authored
feat(api-headless-cms-ddb-es): entry values modifier (#3422)
1 parent 9b7f2f8 commit 267d852

29 files changed

+1161
-281
lines changed

jest.config.base.setup.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33

44
// Runs failed tests five times until they pass or until the max number of retries is exhausted.
55
// https://jestjs.io/docs/jest-object#jestretrytimesnumretries-options
6-
jest.retryTimes(3);
6+
jest.retryTimes(0);
7+
if (process.env.CI === "true") {
8+
jest.retryTimes(3);
9+
}

packages/api-aco/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"directory": "dist"
2323
},
2424
"dependencies": {
25-
"@ungap/structured-clone": "1.2.0",
25+
"@ungap/structured-clone": "^1.2.0",
2626
"@webiny/api": "0.0.0",
2727
"@webiny/api-headless-cms": "0.0.0",
2828
"@webiny/api-i18n": "0.0.0",

packages/api-elasticsearch/src/compression.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export const compress = async (
1616
): Promise<Record<string, any>> => {
1717
const plugins = getCompressionPlugins(pluginsContainer);
1818
if (plugins.length === 0) {
19-
console.log("No compression plugins");
2019
return data;
2120
}
2221
for (const plugin of plugins) {
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/**
2+
* This file tests the CmsEntryElasticsearchValuesModifier plugin.
3+
* It enables a developer to modify the values that are sent to Elasticsearch.
4+
*
5+
* For example, if you want to send just a title of an article into an Elasticsearch index, you can use this plugin to do so.
6+
*/
7+
8+
import { useHandler } from "~tests/graphql/handler";
9+
import { createMockPlugins } from "~tests/converters/mocks";
10+
import { createEntryRawData } from "~tests/converters/mocks/data";
11+
import { configurations } from "~/configurations";
12+
import {
13+
createGlobalModifierPlugin,
14+
createGlobalModifierValues,
15+
createNotApplicableModifierPlugin,
16+
createTargetedModifierPlugin,
17+
createTargetedModifierValues
18+
} from "./mocks/plugins";
19+
import { createExpectedGetResult } from "./mocks/result";
20+
import { fetchFromElasticsearch } from "~tests/api/helpers/fetchFromElasticsearch";
21+
22+
describe("entry values modifier", () => {
23+
const { index: indexName } = configurations.es({
24+
model: {
25+
tenant: "root",
26+
locale: "en-US",
27+
modelId: "converter"
28+
}
29+
});
30+
31+
it("should modify the audit log entry values which are stored into the Elasticsearch - global", async () => {
32+
const { createContext, elasticsearch } = useHandler({
33+
plugins: [...createMockPlugins(), createGlobalModifierPlugin()]
34+
});
35+
const context = await createContext();
36+
37+
const manager = await context.cms.getEntryManager("converter");
38+
39+
const createResult = await manager.create(createEntryRawData());
40+
41+
/**
42+
* Check that we are getting everything properly out of the DynamoDB
43+
*/
44+
const getResult = await manager.get(createResult.id);
45+
expect(getResult).toMatchObject(createExpectedGetResult());
46+
await elasticsearch.indices.refresh({
47+
index: indexName
48+
});
49+
/**
50+
* Then check that we are getting everything properly out of the Elasticsearch, via webiny API.
51+
*/
52+
const [[listResult]] = await manager.listLatest({
53+
where: {
54+
id: createResult.id
55+
}
56+
});
57+
expect(listResult.values).toEqual(createGlobalModifierValues());
58+
});
59+
60+
it("should modify the audit log entry values which are stored into the Elasticsearch - targeted", async () => {
61+
const { createContext, elasticsearch } = useHandler({
62+
plugins: [...createMockPlugins(), createTargetedModifierPlugin()]
63+
});
64+
const context = await createContext();
65+
66+
const manager = await context.cms.getEntryManager("converter");
67+
68+
const createResult = await manager.create(createEntryRawData());
69+
70+
/**
71+
* Check that we are getting everything properly out of the DynamoDB
72+
*/
73+
const getResult = await manager.get(createResult.id);
74+
expect(getResult).toMatchObject(createExpectedGetResult());
75+
await elasticsearch.indices.refresh({
76+
index: indexName
77+
});
78+
/**
79+
* Then check that we are getting everything properly out of the Elasticsearch, via webiny API.
80+
*/
81+
const [[listResult]] = await manager.listLatest({
82+
where: {
83+
id: createResult.id
84+
}
85+
});
86+
expect(listResult.values).toEqual(createTargetedModifierValues());
87+
});
88+
89+
it("should modify the audit log entry values which are stored into the Elasticsearch - not applicable", async () => {
90+
const { createContext, elasticsearch } = useHandler({
91+
plugins: [...createMockPlugins(), createNotApplicableModifierPlugin()]
92+
});
93+
const context = await createContext();
94+
95+
const manager = await context.cms.getEntryManager("converter");
96+
97+
const createResult = await manager.create(createEntryRawData());
98+
99+
/**
100+
* Check that we are getting everything properly out of the DynamoDB
101+
*/
102+
const getResult = await manager.get(createResult.id);
103+
expect(getResult).toMatchObject(createExpectedGetResult());
104+
await elasticsearch.indices.refresh({
105+
index: indexName
106+
});
107+
/**
108+
* Then check that we are getting everything properly out of the Elasticsearch, via webiny API.
109+
*/
110+
const [[listResult]] = await manager.listLatest({
111+
where: {
112+
id: createResult.id
113+
}
114+
});
115+
expect(listResult.values.title).toEqual(createExpectedGetResult().values.title);
116+
});
117+
118+
it("should modify the audit log entry values which are stored into the Elasticsearch - targeted, global and not applicable - transform storageId", async () => {
119+
const { createContext, elasticsearch } = useHandler({
120+
plugins: [
121+
...createMockPlugins(),
122+
createGlobalModifierPlugin(),
123+
createTargetedModifierPlugin({
124+
inherit: true
125+
}),
126+
createNotApplicableModifierPlugin()
127+
]
128+
});
129+
const context = await createContext();
130+
131+
const manager = await context.cms.getEntryManager("converter");
132+
133+
const createResult = await manager.create(createEntryRawData());
134+
135+
/**
136+
* Check that we are getting everything properly out of the DynamoDB
137+
*/
138+
const getResult = await manager.get(createResult.id);
139+
expect(getResult).toMatchObject(createExpectedGetResult());
140+
await elasticsearch.indices.refresh({
141+
index: indexName
142+
});
143+
/**
144+
* Then check that we are getting everything properly out of the Elasticsearch, via webiny API.
145+
*/
146+
const [[listResult]] = await manager.listLatest({
147+
where: {
148+
id: createResult.id
149+
}
150+
});
151+
expect(listResult.values).toEqual({
152+
...createGlobalModifierValues(),
153+
...createTargetedModifierValues()
154+
});
155+
156+
const elasticsearchResult = await fetchFromElasticsearch({
157+
client: elasticsearch,
158+
index: indexName
159+
});
160+
expect(elasticsearchResult).not.toBe(null);
161+
expect(elasticsearchResult).not.toBe(undefined);
162+
expect(elasticsearchResult.values).not.toBe(null);
163+
expect(elasticsearchResult.values).not.toBe(undefined);
164+
165+
expect(elasticsearchResult.values).toEqual({
166+
// from the global plugin
167+
"number@ageFieldIdWithSomeValue": 25,
168+
// from targeted plugin
169+
"text@titleFieldIdWithSomeValue": "A targeted modifier plugin."
170+
});
171+
});
172+
173+
it("should modify the audit log entry values which are stored into the Elasticsearch - targeted, global and not applicable - disable transform storageId", async () => {
174+
process.env.WEBINY_API_TEST_STORAGE_ID_CONVERSION_DISABLE = "true";
175+
const { createContext, elasticsearch } = useHandler({
176+
plugins: [
177+
...createMockPlugins(),
178+
createGlobalModifierPlugin(),
179+
createTargetedModifierPlugin({
180+
inherit: true
181+
}),
182+
createNotApplicableModifierPlugin()
183+
]
184+
});
185+
const context = await createContext();
186+
187+
const manager = await context.cms.getEntryManager("converter");
188+
189+
const createResult = await manager.create(createEntryRawData());
190+
191+
/**
192+
* Check that we are getting everything properly out of the DynamoDB
193+
*/
194+
const getResult = await manager.get(createResult.id);
195+
expect(getResult).toMatchObject(createExpectedGetResult());
196+
await elasticsearch.indices.refresh({
197+
index: indexName
198+
});
199+
/**
200+
* Then check that we are getting everything properly out of the Elasticsearch, via webiny API.
201+
*/
202+
const [[listResult]] = await manager.listLatest({
203+
where: {
204+
id: createResult.id
205+
}
206+
});
207+
expect(listResult.values).toEqual({
208+
...createGlobalModifierValues(),
209+
...createTargetedModifierValues()
210+
});
211+
212+
const elasticsearchResult = await fetchFromElasticsearch({
213+
client: elasticsearch,
214+
index: indexName
215+
});
216+
expect(elasticsearchResult).not.toBe(null);
217+
expect(elasticsearchResult).not.toBe(undefined);
218+
expect(elasticsearchResult.values).not.toBe(null);
219+
expect(elasticsearchResult.values).not.toBe(undefined);
220+
221+
expect(elasticsearchResult.values).toEqual({
222+
// from the global plugin
223+
age: 25,
224+
// from targeted plugin
225+
title: "A targeted modifier plugin."
226+
});
227+
});
228+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { ElasticsearchClient } from "@webiny/project-utils/testing/elasticsearch/createClient";
2+
3+
interface Params {
4+
client: ElasticsearchClient;
5+
index: string;
6+
}
7+
8+
export const fetchFromElasticsearch = async (params: Params) => {
9+
const { client, index } = params;
10+
const result = await client.search({
11+
index
12+
});
13+
return result.body?.hits?.hits[0]?._source;
14+
};
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { CmsEntryElasticsearchValuesModifier } from "~/plugins";
2+
3+
interface ModifierParams {
4+
inherit?: boolean;
5+
}
6+
7+
export const createGlobalModifierValues = () => {
8+
return {
9+
title: "A global modifier plugin.",
10+
age: 25
11+
};
12+
};
13+
export const createGlobalModifierPlugin = (params?: ModifierParams) => {
14+
const plugin = new CmsEntryElasticsearchValuesModifier(({ setValues }) => {
15+
setValues(prev => {
16+
if (params?.inherit) {
17+
return {
18+
...prev,
19+
...createGlobalModifierValues()
20+
};
21+
}
22+
return createGlobalModifierValues();
23+
});
24+
});
25+
plugin.name = "headlessCms.test.global.elasticsearchValueModifier";
26+
return plugin;
27+
};
28+
29+
export const createTargetedModifierValues = () => {
30+
return {
31+
title: "A targeted modifier plugin."
32+
};
33+
};
34+
export const createTargetedModifierPlugin = (params?: ModifierParams) => {
35+
const plugin = new CmsEntryElasticsearchValuesModifier({
36+
models: ["converter"],
37+
modifier: ({ setValues }) => {
38+
setValues(prev => {
39+
if (params?.inherit) {
40+
return {
41+
...prev,
42+
...createTargetedModifierValues()
43+
};
44+
}
45+
return createTargetedModifierValues();
46+
});
47+
}
48+
});
49+
plugin.name = "headlessCms.test.targeted.elasticsearchValueModifier";
50+
return plugin;
51+
};
52+
53+
export const createNotApplicableModifierValues = () => {
54+
return {
55+
title: "This title should not be applied."
56+
};
57+
};
58+
export const createNotApplicableModifierPlugin = (params?: ModifierParams) => {
59+
const plugin = new CmsEntryElasticsearchValuesModifier({
60+
models: ["converterNonExisting"],
61+
modifier: ({ setValues }) => {
62+
setValues(prev => {
63+
if (params?.inherit) {
64+
return {
65+
...prev,
66+
...createNotApplicableModifierValues()
67+
};
68+
}
69+
return createNotApplicableModifierValues();
70+
});
71+
}
72+
});
73+
74+
plugin.name = "headlessCms.test.notApplicable.elasticsearchValueModifier";
75+
return plugin;
76+
};
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
export const createExpectedGetResult = () => {
2+
return {
3+
values: {
4+
title: "Title level 0",
5+
age: 123,
6+
isMarried: true,
7+
dateOfBirth: "2020-01-01",
8+
description: {
9+
compression: "gzip",
10+
value: expect.any(String)
11+
},
12+
body: {
13+
compression: "jsonpack",
14+
value: expect.any(String)
15+
},
16+
information: {
17+
subtitle: "Title level 1",
18+
subAge: 234,
19+
subIsMarried: false,
20+
subDateOfBirth: "2020-01-02",
21+
subDescription: {
22+
compression: "gzip",
23+
value: expect.any(String)
24+
},
25+
subBody: {
26+
compression: "jsonpack",
27+
value: expect.any(String)
28+
},
29+
subInformation: {
30+
subSecondSubtitle: "Title level 2",
31+
subSecondSubAge: 345,
32+
subSecondSubIsMarried: false,
33+
subSecondSubDateOfBirth: "2020-01-03",
34+
subSecondSubDescription: {
35+
compression: "gzip",
36+
value: expect.any(String)
37+
},
38+
subSecondSubBody: {
39+
compression: "jsonpack",
40+
value: expect.any(String)
41+
}
42+
}
43+
}
44+
}
45+
};
46+
};

0 commit comments

Comments
 (0)