Skip to content

Commit 27f0d2b

Browse files
authored
sync actions for attribute group (#1876)
* feat: add sync actions for attribute group * chore: add changeset
1 parent 470456f commit 27f0d2b

5 files changed

Lines changed: 335 additions & 0 deletions

File tree

.changeset/tough-turkeys-swim.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@commercetools/sync-actions': minor
3+
---
4+
5+
Add support for attribute groups `changeName`, `setKey`, `setDescription`, `addAttribute` and `removeAttribute` actions.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { buildBaseAttributesActions } from './utils/common-actions'
2+
import createBuildArrayActions, {
3+
ADD_ACTIONS,
4+
REMOVE_ACTIONS,
5+
CHANGE_ACTIONS,
6+
} from './utils/create-build-array-actions'
7+
8+
const hasAttribute = (attributes, newValue) =>
9+
attributes.some((attribute) => attribute.key === newValue.key)
10+
11+
export const baseActionsList = [
12+
{ action: 'changeName', key: 'name' },
13+
{ action: 'setKey', key: 'key' },
14+
{ action: 'setDescription', key: 'description' },
15+
]
16+
17+
export function actionsMapBase(diff, oldObj, newObj, config = {}) {
18+
return buildBaseAttributesActions({
19+
actions: baseActionsList,
20+
diff,
21+
oldObj,
22+
newObj,
23+
shouldOmitEmptyString: config.shouldOmitEmptyString,
24+
})
25+
}
26+
27+
export function actionsMapAttributes(diff, oldObj, newObj) {
28+
const handler = createBuildArrayActions('attributes', {
29+
[ADD_ACTIONS]: (newAttribute) => ({
30+
action: 'addAttribute',
31+
attribute: newAttribute,
32+
}),
33+
[REMOVE_ACTIONS]: (oldAttribute) => {
34+
// We only add the action if the attribute is not included in the new object.
35+
return !hasAttribute(newObj.attributes, oldAttribute)
36+
? {
37+
action: 'removeAttribute',
38+
attribute: oldAttribute,
39+
}
40+
: null
41+
},
42+
[CHANGE_ACTIONS]: (oldAttribute, newAttribute) => {
43+
const result = []
44+
// We only remove the attribute in case that the oldAttribute is not
45+
// included in the new object
46+
if (!hasAttribute(newObj.attributes, oldAttribute))
47+
result.push({
48+
action: 'removeAttribute',
49+
attribute: oldAttribute,
50+
})
51+
52+
// We only add the attribute in case that the newAttribute was not
53+
// included in the old object
54+
if (!hasAttribute(oldObj.attributes, newAttribute))
55+
result.push({
56+
action: 'addAttribute',
57+
attribute: newAttribute,
58+
})
59+
60+
return result
61+
},
62+
})
63+
64+
return handler(diff, oldObj, newObj)
65+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* @flow */
2+
import flatten from 'lodash.flatten'
3+
import type {
4+
SyncAction,
5+
ActionGroup,
6+
UpdateAction,
7+
SyncActionConfig,
8+
} from 'types/sdk'
9+
import * as attributeGroupsActions from './attribute-groups-actions'
10+
import createBuildActions from './utils/create-build-actions'
11+
import createMapActionGroup from './utils/create-map-action-group'
12+
import * as diffpatcher from './utils/diffpatcher'
13+
14+
function createAttributeGroupsMapActions(
15+
mapActionGroup: (
16+
type: string,
17+
fn: () => Array<UpdateAction>
18+
) => Array<UpdateAction>,
19+
syncActionConfig: SyncActionConfig
20+
): (diff: Object, newObj: Object, oldObj: Object) => Array<UpdateAction> {
21+
return function doMapActions(
22+
diff: Object,
23+
newObj: Object,
24+
oldObj: Object
25+
): Array<UpdateAction> {
26+
const allActions = []
27+
allActions.push(
28+
mapActionGroup('base', (): Array<UpdateAction> =>
29+
attributeGroupsActions.actionsMapBase(
30+
diff,
31+
oldObj,
32+
newObj,
33+
syncActionConfig
34+
)
35+
)
36+
)
37+
allActions.push(
38+
flatten(
39+
mapActionGroup('attributes', (): Array<UpdateAction> =>
40+
attributeGroupsActions.actionsMapAttributes(diff, oldObj, newObj)
41+
)
42+
)
43+
)
44+
return flatten(allActions)
45+
}
46+
}
47+
48+
export default (
49+
actionGroupList: Array<ActionGroup>,
50+
syncActionConfig: SyncActionConfig
51+
): SyncAction => {
52+
const mapActionGroup = createMapActionGroup(actionGroupList)
53+
const doMapActions = createAttributeGroupsMapActions(
54+
mapActionGroup,
55+
syncActionConfig
56+
)
57+
const buildActions = createBuildActions(diffpatcher.diff, doMapActions)
58+
return { buildActions }
59+
}

packages/sync-actions/src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ export { default as createSyncProjects } from './projects'
1818
export { default as createSyncStores } from './stores'
1919
export { default as createSyncProductSelections } from './product-selections'
2020
export { default as createSyncStandalonePrices } from './prices'
21+
export { default as createSyncAttributeGroups } from './attribute-groups'
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import attributeGroupSyncFn from '../src/attribute-groups'
2+
import { baseActionsList } from '../src/attribute-groups-actions'
3+
4+
describe('Exports', () => {
5+
test('correctly define base actions list', () => {
6+
expect(baseActionsList).toEqual([
7+
{ action: 'changeName', key: 'name' },
8+
{ action: 'setKey', key: 'key' },
9+
{ action: 'setDescription', key: 'description' },
10+
])
11+
})
12+
})
13+
14+
describe('Actions', () => {
15+
let attributeGroupSync
16+
beforeEach(() => {
17+
attributeGroupSync = attributeGroupSyncFn()
18+
})
19+
20+
test('should build `changeName` action', () => {
21+
const before = {
22+
name: 'John',
23+
}
24+
const now = {
25+
name: 'Robert',
26+
}
27+
28+
const actual = attributeGroupSync.buildActions(now, before)
29+
const expected = [{ action: 'changeName', name: now.name }]
30+
expect(actual).toEqual(expected)
31+
})
32+
33+
test('should build `setDescription` action', () => {
34+
const before = {
35+
description: 'some description',
36+
}
37+
const now = {
38+
description: 'some updated description',
39+
}
40+
41+
const actual = attributeGroupSync.buildActions(now, before)
42+
const expected = [
43+
{
44+
action: 'setDescription',
45+
description: now.description,
46+
},
47+
]
48+
expect(actual).toEqual(expected)
49+
})
50+
51+
test('should build `setKey` action', () => {
52+
const before = {
53+
key: 'some-key',
54+
}
55+
const now = {
56+
key: 'new-key',
57+
}
58+
59+
const actual = attributeGroupSync.buildActions(now, before)
60+
const expected = [
61+
{
62+
action: 'setKey',
63+
key: now.key,
64+
},
65+
]
66+
expect(actual).toEqual(expected)
67+
})
68+
69+
describe('`addAttribute`', () => {
70+
test('should build `addAttribute` action with one attribute', () => {
71+
const before = {
72+
attributes: [],
73+
}
74+
const now = { attributes: [{ key: 'Size' }] }
75+
76+
const actual = attributeGroupSync.buildActions(now, before)
77+
const expected = [
78+
{ action: 'addAttribute', attribute: now.attributes[0] },
79+
]
80+
expect(actual).toEqual(expected)
81+
})
82+
test('should build `addAttribute` action with two attributes', () => {
83+
const before = { attributes: [] }
84+
const now = { attributes: [{ key: 'Size' }, { key: 'Brand' }] }
85+
86+
const actual = attributeGroupSync.buildActions(now, before)
87+
const expected = [
88+
{ action: 'addAttribute', attribute: now.attributes[0] },
89+
{ action: 'addAttribute', attribute: now.attributes[1] },
90+
]
91+
expect(actual).toEqual(expected)
92+
})
93+
})
94+
95+
describe('`removeAttribute`', () => {
96+
test('should build `removeAttribute` action removing one attribute', () => {
97+
const before = {
98+
attributes: [{ key: 'Size' }, { key: 'Brand' }],
99+
}
100+
const now = { attributes: [{ key: 'Size' }] }
101+
102+
const actual = attributeGroupSync.buildActions(now, before)
103+
const expected = [
104+
{ action: 'removeAttribute', attribute: before.attributes[1] },
105+
]
106+
expect(actual).toEqual(expected)
107+
})
108+
test('should build `removeAttribute` action removing two attributes', () => {
109+
const before = {
110+
attributes: [{ key: 'Size' }, { key: 'Brand' }],
111+
}
112+
const now = { attributes: [] }
113+
114+
const actual = attributeGroupSync.buildActions(now, before)
115+
const expected = [
116+
{ action: 'removeAttribute', attribute: before.attributes[0] },
117+
{ action: 'removeAttribute', attribute: before.attributes[1] },
118+
]
119+
expect(actual).toEqual(expected)
120+
})
121+
})
122+
123+
describe('Swap attributes (create one + delete one)', () => {
124+
test('should build `removeAttribute` and `addAttribute`', () => {
125+
const before = { attributes: [{ key: 'Size' }] }
126+
const now = { attributes: [{ key: 'Brand' }] }
127+
128+
const actual = attributeGroupSync.buildActions(now, before)
129+
const expected = [
130+
{ action: 'removeAttribute', attribute: before.attributes[0] },
131+
{ action: 'addAttribute', attribute: now.attributes[0] },
132+
]
133+
expect(actual).toEqual(expected)
134+
})
135+
})
136+
137+
describe('Multiple actions', () => {
138+
test('should build multiple actions for required changes', () => {
139+
const before = {
140+
attributes: [{ key: 'Size' }, { key: 'Brand' }],
141+
}
142+
const now = {
143+
attributes: [{ key: 'Quality' }, { key: 'Brand' }, { key: 'color' }],
144+
}
145+
146+
const actual = attributeGroupSync.buildActions(now, before)
147+
const expected = [
148+
{ action: 'removeAttribute', attribute: before.attributes[0] },
149+
{ action: 'addAttribute', attribute: now.attributes[0] },
150+
{ action: 'addAttribute', attribute: now.attributes[2] },
151+
]
152+
expect(actual).toEqual(expected)
153+
})
154+
})
155+
156+
describe('Delete first attributes', () => {
157+
test('should build multiple actions for required changes', () => {
158+
const before = {
159+
attributes: [{ key: 'Size' }, { key: 'Brand' }, { key: 'Color' }],
160+
}
161+
const now = {
162+
attributes: [{ key: 'Color' }],
163+
}
164+
165+
const actual = attributeGroupSync.buildActions(now, before)
166+
const expected = [
167+
{ action: 'removeAttribute', attribute: before.attributes[0] },
168+
{ action: 'removeAttribute', attribute: before.attributes[1] },
169+
]
170+
expect(actual).toEqual(expected)
171+
})
172+
})
173+
174+
describe('Delete multiple attributes', () => {
175+
test('should build multiple actions for required changes', () => {
176+
const before = {
177+
attributes: [
178+
{ key: 'Size' },
179+
{ key: 'Brand' },
180+
{ key: 'Quality' },
181+
{ key: 'Color' },
182+
{ key: 'Model' },
183+
{ key: 'attr-1' },
184+
],
185+
}
186+
const now = {
187+
attributes: [
188+
{ key: 'Brand' },
189+
{ key: 'Quality' },
190+
{ key: 'Model' },
191+
{ key: 'attr-2' },
192+
],
193+
}
194+
195+
const actual = attributeGroupSync.buildActions(now, before)
196+
const expected = [
197+
{ action: 'removeAttribute', attribute: before.attributes[0] },
198+
{ action: 'removeAttribute', attribute: before.attributes[3] },
199+
{ action: 'addAttribute', attribute: now.attributes[3] },
200+
{ action: 'removeAttribute', attribute: before.attributes[5] },
201+
]
202+
expect(actual).toEqual(expected)
203+
})
204+
})
205+
})

0 commit comments

Comments
 (0)