feed-common 1.0.0 → 1.0.1
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/CHANGELOG.md +9 -0
- package/dist/constants/google.constants.d.ts +11 -0
- package/dist/constants/product.constants.d.ts +4 -0
- package/dist/constants/profile.constants.d.ts +69 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types/company.types.d.ts +9 -0
- package/dist/types/company.types.js +2 -0
- package/dist/types/company.types.js.map +1 -0
- package/dist/types/product.types.d.ts +86 -0
- package/dist/types/profile.types.d.ts +68 -0
- package/dist/utils/gmc.d.ts +16 -0
- package/dist/utils/profile.d.ts +28 -0
- package/dist/utils/utils.d.ts +1 -0
- package/package.json +3 -2
- package/scripts/install.sh +9 -0
- package/src/constants/google.constants.ts +12 -0
- package/src/constants/product.constants.ts +4 -0
- package/src/constants/profile.constants.ts +369 -0
- package/src/index.ts +11 -0
- package/src/types/company.types.ts +10 -0
- package/src/types/product.types.ts +97 -0
- package/src/types/profile.types.ts +73 -0
- package/src/utils/gmc.ts +69 -0
- package/src/utils/profile.ts +348 -0
- package/src/utils/utils.ts +3 -0
- package/.github/workflows/release.yml +0 -38
@@ -0,0 +1,348 @@
|
|
1
|
+
import {
|
2
|
+
OptionType,
|
3
|
+
OptionTypeItem,
|
4
|
+
OptionTypeGroup,
|
5
|
+
ProductUploadProfile,
|
6
|
+
} from '../types/profile.types.js';
|
7
|
+
import { ProductUploadMappings } from '../constants/profile.constants.js';
|
8
|
+
import { ProductUploadMapping } from '../types/profile.types.js';
|
9
|
+
import {
|
10
|
+
ProductUploadRuleFilterType,
|
11
|
+
RuleOperatorsType,
|
12
|
+
} from '../types/profile.types.js';
|
13
|
+
import {
|
14
|
+
ProductUploadRuleItem,
|
15
|
+
ProductUploadRuleSection,
|
16
|
+
ProductUploadRules,
|
17
|
+
} from '../types/profile.types.js';
|
18
|
+
import { code } from './utils.js';
|
19
|
+
|
20
|
+
export function checkRuleDuplication (rules: ProductUploadRuleItem[]): string[] {
|
21
|
+
return rules
|
22
|
+
.map((r) => {
|
23
|
+
for (const rule of rules) {
|
24
|
+
if (
|
25
|
+
r.id !== rule.id &&
|
26
|
+
r.attribute === rule.attribute &&
|
27
|
+
r.operator === rule.operator &&
|
28
|
+
r.value === rule.value
|
29
|
+
) {
|
30
|
+
return r.id;
|
31
|
+
}
|
32
|
+
}
|
33
|
+
return null;
|
34
|
+
})
|
35
|
+
.filter(Boolean) as string[];
|
36
|
+
}
|
37
|
+
|
38
|
+
export function checkRuleAlwaysness (rules: ProductUploadRuleItem[]): string[] {
|
39
|
+
return rules
|
40
|
+
.map((r) => {
|
41
|
+
for (const rule of rules) {
|
42
|
+
if (isExlusive(r, rule)) {
|
43
|
+
return r.id;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
return null;
|
47
|
+
})
|
48
|
+
.filter(Boolean) as string[];
|
49
|
+
}
|
50
|
+
|
51
|
+
export function checkRuleDeadlocks (
|
52
|
+
sections: ProductUploadRuleSection[]
|
53
|
+
): string[] {
|
54
|
+
let totalList = [] as string[];
|
55
|
+
|
56
|
+
for (let i = 0; i < sections.length; i++) {
|
57
|
+
const currentSection = sections[i];
|
58
|
+
const currentList = [] as string[];
|
59
|
+
|
60
|
+
for (let y = 0; y < sections.length; y++) {
|
61
|
+
if (i === y) {
|
62
|
+
continue;
|
63
|
+
}
|
64
|
+
|
65
|
+
from_start: for (const ruleA of currentSection.ruleItems) {
|
66
|
+
for (const ruleB of sections[y].ruleItems) {
|
67
|
+
if (isExlusive(ruleA, ruleB)) {
|
68
|
+
currentList.push(ruleA.id);
|
69
|
+
continue from_start;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
if (currentList.length === currentSection.ruleItems.length) {
|
76
|
+
totalList = [...totalList, ...currentList];
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
return totalList;
|
81
|
+
}
|
82
|
+
|
83
|
+
function isExlusive (
|
84
|
+
rule1: ProductUploadRuleItem,
|
85
|
+
rule2: ProductUploadRuleItem
|
86
|
+
): boolean {
|
87
|
+
return (
|
88
|
+
rule1.id !== rule2.id &&
|
89
|
+
rule1.attribute === rule2.attribute &&
|
90
|
+
rule1.operator ===
|
91
|
+
getOperatorCounterpart(rule2.operator as keyof RuleOperatorsType) &&
|
92
|
+
rule1.value === rule2.value
|
93
|
+
);
|
94
|
+
}
|
95
|
+
|
96
|
+
function getOperatorCounterpart (
|
97
|
+
operator: keyof RuleOperatorsType
|
98
|
+
): keyof RuleOperatorsType {
|
99
|
+
switch (operator) {
|
100
|
+
case 'equals':
|
101
|
+
return 'notEquals';
|
102
|
+
case 'notEquals':
|
103
|
+
return 'equals';
|
104
|
+
case 'contains':
|
105
|
+
return 'notContains';
|
106
|
+
case 'notContains':
|
107
|
+
return 'contains';
|
108
|
+
case 'startsWith':
|
109
|
+
return 'notStartsWith';
|
110
|
+
case 'notStartsWith':
|
111
|
+
return 'startsWith';
|
112
|
+
case 'endsWith':
|
113
|
+
return 'notEndsWith';
|
114
|
+
case 'notEndsWith':
|
115
|
+
return 'endsWith';
|
116
|
+
case 'in':
|
117
|
+
return 'notIn';
|
118
|
+
case 'notIn':
|
119
|
+
return 'in';
|
120
|
+
default:
|
121
|
+
throw new Error(`Unknown operator ${String(operator)}`);
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
export function getEmptyRuleItem (
|
126
|
+
attribute: ProductUploadRuleFilterType['value'] = ''
|
127
|
+
): ProductUploadRuleItem {
|
128
|
+
return { attribute, operator: '', value: '', id: code() };
|
129
|
+
}
|
130
|
+
|
131
|
+
export function sortMappings (
|
132
|
+
a: ProductUploadMapping,
|
133
|
+
b: ProductUploadMapping
|
134
|
+
): number {
|
135
|
+
if (!a.attribute) {
|
136
|
+
return 1;
|
137
|
+
}
|
138
|
+
|
139
|
+
if (!b.attribute) {
|
140
|
+
return -1;
|
141
|
+
}
|
142
|
+
|
143
|
+
const sourceA = ProductUploadMappings.find(
|
144
|
+
(m) => m.attribute === a.attribute
|
145
|
+
);
|
146
|
+
const sourceB = ProductUploadMappings.find(
|
147
|
+
(m) => m.attribute === b.attribute
|
148
|
+
);
|
149
|
+
|
150
|
+
if (sourceA?.required && !sourceB?.required) {
|
151
|
+
return -1;
|
152
|
+
}
|
153
|
+
|
154
|
+
if (sourceB?.required && !sourceA?.required) {
|
155
|
+
return 1;
|
156
|
+
}
|
157
|
+
|
158
|
+
if (a.attribute < b.attribute) {
|
159
|
+
return -1;
|
160
|
+
}
|
161
|
+
|
162
|
+
if (a.attribute > b.attribute) {
|
163
|
+
return 1;
|
164
|
+
}
|
165
|
+
|
166
|
+
return 0;
|
167
|
+
}
|
168
|
+
|
169
|
+
export function hasRules (rules: ProductUploadRules): boolean {
|
170
|
+
return (
|
171
|
+
Array.isArray(rules?.sections) &&
|
172
|
+
rules.sections.some((section) => section.ruleItems.length > 0)
|
173
|
+
);
|
174
|
+
}
|
175
|
+
|
176
|
+
/**
|
177
|
+
* Checks if two rules are the same
|
178
|
+
* @param a
|
179
|
+
* @param b
|
180
|
+
* @returns
|
181
|
+
*/
|
182
|
+
export function compareRules (
|
183
|
+
a: ProductUploadRules = { sections: [] },
|
184
|
+
b: ProductUploadRules = { sections: [] }
|
185
|
+
): boolean {
|
186
|
+
const matchedSections = [] as number[];
|
187
|
+
const sameRule = (
|
188
|
+
rA: ProductUploadRuleItem,
|
189
|
+
rB: ProductUploadRuleItem
|
190
|
+
): boolean => {
|
191
|
+
return (
|
192
|
+
rA.attribute === rB.attribute &&
|
193
|
+
rA.operator === rB.operator &&
|
194
|
+
compareValues(rA.value, rB.value)
|
195
|
+
);
|
196
|
+
};
|
197
|
+
|
198
|
+
a.sections = a.sections.filter((s) => s.ruleItems.length > 0);
|
199
|
+
b.sections = b.sections.filter((s) => s.ruleItems.length > 0);
|
200
|
+
|
201
|
+
if (a.sections.length !== b.sections.length) {
|
202
|
+
return false;
|
203
|
+
}
|
204
|
+
|
205
|
+
for (let i = 0; i < a.sections.length; i++) {
|
206
|
+
const rulesA = a.sections[i].ruleItems;
|
207
|
+
|
208
|
+
for (let j = 0; j < b.sections.length; j++) {
|
209
|
+
if (matchedSections.includes(j)) {
|
210
|
+
continue;
|
211
|
+
}
|
212
|
+
|
213
|
+
const rulesB = b.sections[j].ruleItems;
|
214
|
+
|
215
|
+
if (rulesA.length !== rulesB.length) {
|
216
|
+
continue;
|
217
|
+
}
|
218
|
+
|
219
|
+
if (
|
220
|
+
rulesA.every((ruleA) => rulesB.some((ruleB) => sameRule(ruleA, ruleB)))
|
221
|
+
) {
|
222
|
+
matchedSections.push(j);
|
223
|
+
break;
|
224
|
+
}
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
return matchedSections.length === a.sections.length;
|
229
|
+
}
|
230
|
+
|
231
|
+
function compareValues (a: any, b: any): boolean {
|
232
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
233
|
+
return a.length === b.length && a.every((i) => b.includes(i));
|
234
|
+
}
|
235
|
+
|
236
|
+
return a === b;
|
237
|
+
}
|
238
|
+
|
239
|
+
export function compareMappings (
|
240
|
+
a: ProductUploadMapping[],
|
241
|
+
b: ProductUploadMapping[]
|
242
|
+
): boolean {
|
243
|
+
const matchedMappings = [] as number[];
|
244
|
+
|
245
|
+
if (a.length !== b.length) {
|
246
|
+
return false;
|
247
|
+
}
|
248
|
+
|
249
|
+
for (let i = 0; i < a.length; i++) {
|
250
|
+
const mappingA = a[i];
|
251
|
+
|
252
|
+
for (let j = 0; j < b.length; j++) {
|
253
|
+
if (matchedMappings.includes(j)) {
|
254
|
+
continue;
|
255
|
+
}
|
256
|
+
|
257
|
+
const mappingB = b[j];
|
258
|
+
|
259
|
+
if (
|
260
|
+
mappingA.attribute === mappingB.attribute &&
|
261
|
+
compareValues(mappingA.value, mappingB.value) &&
|
262
|
+
compareRules(mappingA.rules, mappingB.rules)
|
263
|
+
) {
|
264
|
+
matchedMappings.push(j);
|
265
|
+
break;
|
266
|
+
}
|
267
|
+
}
|
268
|
+
}
|
269
|
+
|
270
|
+
return matchedMappings.length === a.length;
|
271
|
+
}
|
272
|
+
export function optionIsString (option: OptionType): option is string {
|
273
|
+
return typeof option === 'string';
|
274
|
+
}
|
275
|
+
|
276
|
+
export function optionIsItem (option: OptionType): option is OptionTypeItem {
|
277
|
+
return typeof option !== 'string' && 'value' in option;
|
278
|
+
}
|
279
|
+
|
280
|
+
export function optionIsGroup (option: OptionType): option is OptionTypeGroup {
|
281
|
+
return typeof option !== 'string' && 'options' in option;
|
282
|
+
}
|
283
|
+
export function detectProfileChange (
|
284
|
+
a: ProductUploadProfile,
|
285
|
+
b: ProductUploadProfile
|
286
|
+
): { name: boolean; rules: boolean; mappings: boolean; id: boolean } {
|
287
|
+
const changes = {
|
288
|
+
name: false,
|
289
|
+
rules: false,
|
290
|
+
mappings: false,
|
291
|
+
id: false,
|
292
|
+
};
|
293
|
+
|
294
|
+
if (a.name !== b.name) {
|
295
|
+
changes.name = true;
|
296
|
+
}
|
297
|
+
|
298
|
+
if (a.mappings !== b.mappings) {
|
299
|
+
const mappingsA = a.mappings?.filter(
|
300
|
+
(m) =>
|
301
|
+
m.attribute !== 'targetCountry' && m.attribute !== 'contentLanguage'
|
302
|
+
);
|
303
|
+
const mappingsB = b.mappings?.filter(
|
304
|
+
(m) =>
|
305
|
+
m.attribute !== 'targetCountry' && m.attribute !== 'contentLanguage'
|
306
|
+
);
|
307
|
+
changes.mappings = !compareMappings(mappingsA, mappingsB);
|
308
|
+
}
|
309
|
+
|
310
|
+
if (
|
311
|
+
['targetCountry', 'contentLanguage'].some((key) => {
|
312
|
+
const mappingA = a.mappings?.filter((m) => m.attribute === key) ?? [];
|
313
|
+
const mappingB = b.mappings?.filter((m) => m.attribute === key) ?? [];
|
314
|
+
|
315
|
+
return !compareMappings(mappingA, mappingB);
|
316
|
+
})
|
317
|
+
) {
|
318
|
+
changes.id = true;
|
319
|
+
}
|
320
|
+
|
321
|
+
if (!compareRules(a.rules, b.rules)) {
|
322
|
+
changes.rules = true;
|
323
|
+
}
|
324
|
+
|
325
|
+
return changes;
|
326
|
+
}
|
327
|
+
|
328
|
+
export function sanitizeUploadProfile (
|
329
|
+
profile: ProductUploadProfile
|
330
|
+
): ProductUploadProfile {
|
331
|
+
if (profile.rules?.sections) {
|
332
|
+
profile.rules.sections = profile.rules.sections.filter(
|
333
|
+
(section) => section.ruleItems.length > 0
|
334
|
+
);
|
335
|
+
}
|
336
|
+
|
337
|
+
profile.mappings = profile.mappings?.map((mapping) => {
|
338
|
+
if (mapping.rules?.sections) {
|
339
|
+
mapping.rules.sections = mapping.rules.sections.filter(
|
340
|
+
(section) => section.ruleItems.length > 0
|
341
|
+
);
|
342
|
+
}
|
343
|
+
|
344
|
+
return mapping;
|
345
|
+
});
|
346
|
+
|
347
|
+
return profile;
|
348
|
+
}
|
@@ -1,38 +0,0 @@
|
|
1
|
-
name: Release
|
2
|
-
|
3
|
-
permissions:
|
4
|
-
contents: write
|
5
|
-
issues: write
|
6
|
-
pull-requests: write
|
7
|
-
|
8
|
-
on:
|
9
|
-
push:
|
10
|
-
branches:
|
11
|
-
- master
|
12
|
-
jobs:
|
13
|
-
release:
|
14
|
-
name: Release
|
15
|
-
runs-on: ubuntu-latest
|
16
|
-
steps:
|
17
|
-
- name: Checkout
|
18
|
-
uses: actions/checkout@v4
|
19
|
-
with:
|
20
|
-
fetch-depth: 0
|
21
|
-
|
22
|
-
- name: Setup Node.js
|
23
|
-
uses: actions/setup-node@v4
|
24
|
-
with:
|
25
|
-
node-version: "20"
|
26
|
-
|
27
|
-
- name: Install dependencies
|
28
|
-
run: npm ci
|
29
|
-
|
30
|
-
- name: Build
|
31
|
-
run: npm run build
|
32
|
-
|
33
|
-
- name: Release
|
34
|
-
env:
|
35
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
36
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
37
|
-
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
38
|
-
run: npx semantic-release
|