Skip to content

Commit 7f925bc

Browse files
authored
Merge pull request #46 from commercetools/OC-18-standalone-variant-update-action
feat(standalone variant action): create actions for standalone variants
2 parents 8a64b69 + de35251 commit 7f925bc

5 files changed

Lines changed: 1643 additions & 0 deletions

File tree

.changeset/brown-keys-stay.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 standalone variants actions

packages/sync-actions/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ export { default as createSyncAttributeGroups } from './attribute-groups/attribu
2424
export { default as createSyncApiExtensions } from './api-extensions/api-extensions';
2525
export { default as createSyncBusinessUnits } from './business-units/business-units';
2626
export { default as createSyncSubscriptions } from './subscriptions/subscriptions';
27+
export { default as createSyncStandaloneVariants } from './standalone-variants/standalone-variants';
2728
export * from './utils/types';
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import * as diffpatcher from '../utils/diffpatcher';
2+
import {
3+
Asset,
4+
Attribute,
5+
Delta,
6+
Image,
7+
SyncActionConfig,
8+
UpdateAction,
9+
} from '../utils/types';
10+
11+
export type StandaloneVariant = {
12+
id?: string | number;
13+
key?: string;
14+
published?: boolean;
15+
sku?: string;
16+
attributes?: Attribute[];
17+
images?: Image[];
18+
assets?: Asset[];
19+
};
20+
21+
export type StandaloneVariantUpdateAction = UpdateAction & {
22+
staged?: boolean;
23+
attributes?: Array<{ name: string; value?: string }>;
24+
};
25+
26+
/**
27+
* Converts an attribute to the format required by the setAttributes action.
28+
* The value is JSON stringified as required by the API.
29+
*/
30+
export function convertAttributeToUpdateActionShape(attribute: Attribute): {
31+
name: string;
32+
value?: string;
33+
} {
34+
const { name, value } = attribute;
35+
return {
36+
name,
37+
...(typeof value !== 'undefined'
38+
? {
39+
value: JSON.stringify(value),
40+
}
41+
: {}),
42+
};
43+
}
44+
45+
/**
46+
* Creates a staged action with the given action name and payload.
47+
* Automatically adds staged: true unless config.staged is false.
48+
*/
49+
function _buildStagedAction<T extends Record<string, unknown>>(
50+
actionName: string,
51+
payload: T,
52+
config: SyncActionConfig = {}
53+
): StandaloneVariantUpdateAction {
54+
const action: StandaloneVariantUpdateAction = {
55+
action: actionName,
56+
...payload,
57+
};
58+
59+
if (config.staged !== false) {
60+
action.staged = true;
61+
}
62+
63+
return action;
64+
}
65+
66+
function _buildKeyAction(
67+
variantDiff: Delta,
68+
oldVariant: StandaloneVariant
69+
): StandaloneVariantUpdateAction | null {
70+
if ({}.hasOwnProperty.call(variantDiff, 'key')) {
71+
const newValue = diffpatcher.getDeltaValue(
72+
variantDiff.key
73+
) as unknown as string | undefined;
74+
if (!newValue && !oldVariant.key) return null;
75+
76+
return {
77+
action: 'setKey',
78+
key: newValue || undefined,
79+
};
80+
}
81+
return null;
82+
}
83+
84+
function _buildSkuAction(
85+
variantDiff: Delta,
86+
oldVariant: StandaloneVariant,
87+
config: SyncActionConfig = {}
88+
): StandaloneVariantUpdateAction | null {
89+
if ({}.hasOwnProperty.call(variantDiff, 'sku')) {
90+
const newValue = diffpatcher.getDeltaValue(
91+
variantDiff.sku
92+
) as unknown as string | undefined;
93+
if (!newValue && !oldVariant.sku) return null;
94+
95+
return _buildStagedAction('setSku', { sku: newValue || undefined }, config);
96+
}
97+
return null;
98+
}
99+
100+
function _buildPublishAction(
101+
oldVariant: StandaloneVariant,
102+
newVariant: StandaloneVariant
103+
): StandaloneVariantUpdateAction | null {
104+
// publish: oldObj.published is not true AND newObj.published is true
105+
if (oldVariant.published !== true && newVariant.published === true) {
106+
return { action: 'publish' };
107+
}
108+
109+
// unpublish: oldObj.published is true AND newObj.published is not true
110+
if (oldVariant.published === true && newVariant.published !== true) {
111+
return { action: 'unpublish' };
112+
}
113+
114+
return null;
115+
}
116+
117+
/**
118+
* Maps base actions (setKey, setSku, publish, unpublish) for standalone variants.
119+
* Analyzes the diff between old and new variant and generates appropriate actions.
120+
*
121+
* Accepts flat variant objects directly (not wrapped in a variants array).
122+
*/
123+
export function actionsMapBase(
124+
diff: Delta,
125+
oldVariant: StandaloneVariant,
126+
newVariant: StandaloneVariant,
127+
config: SyncActionConfig = {}
128+
): Array<StandaloneVariantUpdateAction> {
129+
const actions: Array<StandaloneVariantUpdateAction> = [];
130+
131+
if (!oldVariant || !diff) {
132+
return actions;
133+
}
134+
135+
const keyAction = _buildKeyAction(diff, oldVariant);
136+
if (keyAction) {
137+
actions.push(keyAction);
138+
}
139+
140+
const skuAction = _buildSkuAction(diff, oldVariant, config);
141+
if (skuAction) {
142+
actions.push(skuAction);
143+
}
144+
145+
const publishAction = _buildPublishAction(oldVariant, newVariant);
146+
if (publishAction) {
147+
actions.push(publishAction);
148+
}
149+
150+
return actions;
151+
}
152+
153+
/**
154+
* Maps attribute actions for standalone variants.
155+
* Generates a single setAttributes action when attributes have changed.
156+
*/
157+
export function actionsMapAttributes(
158+
diff: Delta,
159+
_oldVariant: StandaloneVariant,
160+
newVariant: StandaloneVariant,
161+
config: SyncActionConfig = {}
162+
): Array<StandaloneVariantUpdateAction> {
163+
if (!diff?.attributes) {
164+
return [];
165+
}
166+
167+
const attributes = (newVariant.attributes || []).map(
168+
convertAttributeToUpdateActionShape
169+
);
170+
return [_buildStagedAction('setAttributes', { attributes }, config)];
171+
}
172+
173+
/**
174+
* Maps image actions for standalone variants.
175+
* Generates a single setImages action when images have changed.
176+
*/
177+
export function actionsMapImages(
178+
diff: Delta,
179+
_oldVariant: StandaloneVariant,
180+
newVariant: StandaloneVariant,
181+
config: SyncActionConfig = {}
182+
): Array<StandaloneVariantUpdateAction> {
183+
if (!diff?.images) {
184+
return [];
185+
}
186+
187+
const images = newVariant.images || [];
188+
return [_buildStagedAction('setImages', { images }, config)];
189+
}
190+
191+
/**
192+
* Maps asset actions for standalone variants.
193+
* Generates a single setAssets action when assets have changed.
194+
*/
195+
export function actionsMapAssets(
196+
diff: Delta,
197+
_oldVariant: StandaloneVariant,
198+
newVariant: StandaloneVariant,
199+
config: SyncActionConfig = {}
200+
): Array<StandaloneVariantUpdateAction> {
201+
if (!diff?.assets) {
202+
return [];
203+
}
204+
205+
const assets = newVariant.assets || [];
206+
return [_buildStagedAction('setAssets', { assets }, config)];
207+
}
208+
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import createBuildActions from '../utils/create-build-actions';
2+
import createMapActionGroup from '../utils/create-map-action-group';
3+
import * as diffpatcher from '../utils/diffpatcher';
4+
import {
5+
actionsMapBase,
6+
actionsMapAttributes,
7+
actionsMapImages,
8+
actionsMapAssets,
9+
StandaloneVariant,
10+
StandaloneVariantUpdateAction,
11+
} from './standalone-variant-actions';
12+
import {
13+
ActionGroup,
14+
Delta,
15+
SyncAction,
16+
SyncActionConfig,
17+
UpdateAction,
18+
} from '../utils/types';
19+
20+
export type { StandaloneVariant, StandaloneVariantUpdateAction };
21+
22+
export const actionGroups: Array<string> = [
23+
'base',
24+
'attributes',
25+
'images',
26+
'assets',
27+
];
28+
29+
function createStandaloneVariantsMapActions(
30+
mapActionGroup: (
31+
type: string,
32+
fn: () => Array<UpdateAction>
33+
) => Array<UpdateAction>,
34+
syncActionConfig: SyncActionConfig
35+
) {
36+
return function doMapActions(
37+
diff: Delta,
38+
newObj: StandaloneVariant,
39+
oldObj: StandaloneVariant
40+
): Array<StandaloneVariantUpdateAction> {
41+
const allActions: Array<StandaloneVariantUpdateAction> = [];
42+
43+
allActions.push(
44+
...mapActionGroup('base', () =>
45+
actionsMapBase(diff, oldObj, newObj, syncActionConfig)
46+
)
47+
);
48+
49+
allActions.push(
50+
...mapActionGroup('attributes', () =>
51+
actionsMapAttributes(diff, oldObj, newObj, syncActionConfig)
52+
)
53+
);
54+
55+
allActions.push(
56+
...mapActionGroup('images', () =>
57+
actionsMapImages(diff, oldObj, newObj, syncActionConfig)
58+
)
59+
);
60+
61+
allActions.push(
62+
...mapActionGroup('assets', () =>
63+
actionsMapAssets(diff, oldObj, newObj, syncActionConfig)
64+
)
65+
);
66+
67+
return allActions;
68+
};
69+
}
70+
71+
export default function createSyncStandaloneVariants(
72+
actionGroupList?: Array<ActionGroup>,
73+
syncActionConfig: SyncActionConfig = {}
74+
): SyncAction<StandaloneVariant, StandaloneVariantUpdateAction> {
75+
const mapActionGroup = createMapActionGroup(actionGroupList);
76+
const doMapActions = createStandaloneVariantsMapActions(
77+
mapActionGroup,
78+
syncActionConfig
79+
);
80+
81+
const buildActions = createBuildActions<
82+
StandaloneVariant,
83+
StandaloneVariantUpdateAction
84+
>(diffpatcher.diff, doMapActions);
85+
86+
return { buildActions };
87+
}

0 commit comments

Comments
 (0)