feed-common 1.77.0 → 1.77.2
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/.vscode/settings.json +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/constants/profile.constants.d.ts +14 -1
- package/dist/constants/profile.constants.d.ts.map +1 -1
- package/dist/constants/profile.constants.js +40 -1
- package/dist/constants/profile.constants.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/sources/country.source.d.ts +5 -0
- package/dist/sources/country.source.d.ts.map +1 -0
- package/dist/sources/country.source.js +78 -0
- package/dist/sources/country.source.js.map +1 -0
- package/dist/sources/currency.source.d.ts.map +1 -1
- package/dist/sources/currency.source.js +0 -1
- package/dist/sources/currency.source.js.map +1 -1
- package/dist/sources/ebay-gallery-type.source.d.ts +5 -0
- package/dist/sources/ebay-gallery-type.source.d.ts.map +1 -0
- package/dist/sources/ebay-gallery-type.source.js +5 -0
- package/dist/sources/ebay-gallery-type.source.js.map +1 -0
- package/dist/sources/ebay-inventory-tracking.source.d.ts +3 -0
- package/dist/sources/ebay-inventory-tracking.source.d.ts.map +1 -0
- package/dist/sources/ebay-inventory-tracking.source.js +11 -0
- package/dist/sources/ebay-inventory-tracking.source.js.map +1 -0
- package/dist/sources/ebay-listing-type.source.d.ts +5 -0
- package/dist/sources/ebay-listing-type.source.d.ts.map +1 -0
- package/dist/sources/ebay-listing-type.source.js +5 -0
- package/dist/sources/ebay-listing-type.source.js.map +1 -0
- package/dist/sources/ebay-yes-no.source.d.ts +5 -0
- package/dist/sources/ebay-yes-no.source.d.ts.map +1 -0
- package/dist/sources/ebay-yes-no.source.js +2 -0
- package/dist/sources/ebay-yes-no.source.js.map +1 -0
- package/dist/sources/google-availability.source.d.ts +5 -0
- package/dist/sources/google-availability.source.d.ts.map +1 -0
- package/dist/sources/google-availability.source.js +7 -0
- package/dist/sources/google-availability.source.js.map +1 -0
- package/dist/sources/language.source.d.ts +5 -0
- package/dist/sources/language.source.d.ts.map +1 -0
- package/dist/sources/language.source.js +31 -0
- package/dist/sources/language.source.js.map +1 -0
- package/dist/utils/feed-templates/{index.d.ts → common-schemas.d.ts} +24 -29
- package/dist/utils/feed-templates/common-schemas.d.ts.map +1 -0
- package/dist/utils/feed-templates/{index.js → common-schemas.js} +668 -173
- package/dist/utils/feed-templates/common-schemas.js.map +1 -0
- package/dist/utils/feed-templates/common.d.ts +20 -0
- package/dist/utils/feed-templates/common.d.ts.map +1 -0
- package/dist/utils/feed-templates/common.js +60 -0
- package/dist/utils/feed-templates/common.js.map +1 -0
- package/dist/utils/feed-templates/ebay.template.d.ts +2 -2
- package/dist/utils/feed-templates/ebay.template.d.ts.map +1 -1
- package/dist/utils/feed-templates/ebay.template.js +190 -3
- package/dist/utils/feed-templates/ebay.template.js.map +1 -1
- package/dist/utils/feed-templates/facebook.template.d.ts.map +1 -1
- package/dist/utils/feed-templates/facebook.template.js +2 -1
- package/dist/utils/feed-templates/facebook.template.js.map +1 -1
- package/dist/utils/feed-templates/google-api.template.d.ts.map +1 -1
- package/dist/utils/feed-templates/google-api.template.js +19 -7
- package/dist/utils/feed-templates/google-api.template.js.map +1 -1
- package/dist/utils/feed-templates/google.template.d.ts.map +1 -1
- package/dist/utils/feed-templates/google.template.js +7 -15
- package/dist/utils/feed-templates/google.template.js.map +1 -1
- package/dist/utils/feed-templates/microsoft.template.d.ts.map +1 -1
- package/dist/utils/feed-templates/microsoft.template.js +11 -7
- package/dist/utils/feed-templates/microsoft.template.js.map +1 -1
- package/dist/utils/feed-templates/template-utils.d.ts +4 -0
- package/dist/utils/feed-templates/template-utils.d.ts.map +1 -0
- package/dist/utils/feed-templates/template-utils.js +29 -0
- package/dist/utils/feed-templates/template-utils.js.map +1 -0
- package/dist/utils/feed-templates/tiktok.template.d.ts.map +1 -1
- package/dist/utils/feed-templates/tiktok.template.js +2 -1
- package/dist/utils/feed-templates/tiktok.template.js.map +1 -1
- package/dist/utils/profile.d.ts +2 -1
- package/dist/utils/profile.d.ts.map +1 -1
- package/dist/utils/profile.js +82 -1
- package/dist/utils/profile.js.map +1 -1
- package/dist/utils/utils.d.ts +2 -0
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +23 -3
- package/dist/utils/utils.js.map +1 -1
- package/package.json +5 -4
- package/src/constants/profile.constants.ts +40 -1
- package/src/index.ts +11 -1
- package/src/sources/country.source.ts +77 -0
- package/src/sources/currency.source.ts +0 -1
- package/src/sources/ebay-gallery-type.source.ts +4 -0
- package/src/sources/ebay-inventory-tracking.source.ts +12 -0
- package/src/sources/ebay-listing-type.source.ts +4 -0
- package/src/sources/ebay-yes-no.source.ts +1 -0
- package/src/sources/google-availability.source.ts +6 -0
- package/src/sources/language.source.ts +30 -0
- package/src/utils/feed-templates/common-schemas.ts +1014 -0
- package/src/utils/feed-templates/common.ts +82 -0
- package/src/utils/feed-templates/ebay.template.ts +216 -5
- package/src/utils/feed-templates/facebook.template.ts +2 -1
- package/src/utils/feed-templates/google-api.template.ts +54 -29
- package/src/utils/feed-templates/google.template.ts +46 -52
- package/src/utils/feed-templates/microsoft.template.ts +26 -21
- package/src/utils/feed-templates/template-utils.ts +30 -0
- package/src/utils/feed-templates/tiktok.template.ts +4 -2
- package/src/utils/profile.ts +116 -2
- package/src/utils/utils.ts +25 -5
- package/tests/company.spec.ts +39 -39
- package/tests/profile-utils.spec.ts +38 -0
- package/tests/validator.spec.ts +1 -5
- package/dist/utils/feed-templates/index.d.ts.map +0 -1
- package/dist/utils/feed-templates/index.js.map +0 -1
- package/src/utils/feed-templates/index.ts +0 -511
@@ -0,0 +1,82 @@
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2
|
+
import { resolveSourceValue } from '../profile.js';
|
3
|
+
import { dimensionRegex } from './common-schemas.js';
|
4
|
+
|
5
|
+
export function splitByCommaAndTrim(str: string): string[] {
|
6
|
+
return typeof str === 'string'
|
7
|
+
? str
|
8
|
+
.split(',')
|
9
|
+
.map(s => s.trim())
|
10
|
+
.filter(Boolean)
|
11
|
+
: [];
|
12
|
+
}
|
13
|
+
|
14
|
+
export function parseDimensionString({
|
15
|
+
dimension,
|
16
|
+
min = 0,
|
17
|
+
max = 1000000,
|
18
|
+
}: {
|
19
|
+
dimension: string;
|
20
|
+
min?: number;
|
21
|
+
max?: number;
|
22
|
+
}): {
|
23
|
+
value: string;
|
24
|
+
unit: string;
|
25
|
+
} {
|
26
|
+
const match = dimensionRegex.exec(dimension);
|
27
|
+
|
28
|
+
if (!match) {
|
29
|
+
throw new Error('Invalid format');
|
30
|
+
} else if (
|
31
|
+
!Number.isFinite(Number(match.groups?.value)) ||
|
32
|
+
Number(match.groups?.value) < min ||
|
33
|
+
Number(match.groups?.value) > max
|
34
|
+
) {
|
35
|
+
throw new Error(`Value should be between ${min} and ${max}`);
|
36
|
+
}
|
37
|
+
|
38
|
+
return { value: match.groups?.value as string, unit: match.groups?.unit as string };
|
39
|
+
}
|
40
|
+
|
41
|
+
export function convertToBoolean(value: string): boolean {
|
42
|
+
if (value === 'true') {
|
43
|
+
return true;
|
44
|
+
}
|
45
|
+
|
46
|
+
if (value === 'false') {
|
47
|
+
return false;
|
48
|
+
}
|
49
|
+
|
50
|
+
throw new Error('Invalid boolean value');
|
51
|
+
}
|
52
|
+
|
53
|
+
export function castToArray(value: string): string[] {
|
54
|
+
return Array.isArray(value) ? value : [value];
|
55
|
+
}
|
56
|
+
|
57
|
+
export function castToInt(value: string): number {
|
58
|
+
return typeof value === 'string' ? parseInt(value, 10) : value;
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Create a macro string for use in feed templates
|
63
|
+
* @param source
|
64
|
+
* @param value
|
65
|
+
* @returns
|
66
|
+
*/
|
67
|
+
export function makeMacroString(source: string, value?: string[]): string {
|
68
|
+
if (value) {
|
69
|
+
if (source.startsWith('source.')) {
|
70
|
+
const resolved = resolveSourceValue(source)?.filter(v => value.includes(v.value));
|
71
|
+
|
72
|
+
if (resolved?.length) {
|
73
|
+
return `{{${source}:${JSON.stringify(resolved)}}}`;
|
74
|
+
}
|
75
|
+
} else {
|
76
|
+
// action
|
77
|
+
return `{{${source}:${value.join(',')}}}`;
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
return source.startsWith('source.') ? `{{${source}:}}` : `{{${source}}}`;
|
82
|
+
}
|
@@ -1,7 +1,218 @@
|
|
1
|
-
import {
|
1
|
+
import { z } from 'zod';
|
2
|
+
import { makeMacroString } from './common';
|
3
|
+
import { validateIfNoMacro } from './common-schemas';
|
4
|
+
import {
|
5
|
+
ACTION_ARRAY_FIRST_ITEMS,
|
6
|
+
SOURCE_EBAY_GALLERY_TYPE,
|
7
|
+
SOURCE_EBAY_INVENTORY_TRACKING,
|
8
|
+
SOURCE_EBAY_LISTING_TYPE,
|
9
|
+
SOURCE_EBAY_YES_NO,
|
10
|
+
SOURCE_SHOPIFY_DESCRIPTION_HTML,
|
11
|
+
SOURCE_SHOPIFY_IMAGE,
|
12
|
+
SOURCE_SHOPIFY_IMAGES,
|
13
|
+
SOURCE_SHOPIFY_SHOP_COUNTRY,
|
14
|
+
SOURCE_SHOPIFY_SHOP_CURRENCY,
|
15
|
+
} from '../../constants/profile.constants';
|
16
|
+
import { SelectItem, XmlFeedTemplateType } from '../../types/profile.types';
|
17
|
+
import { resolveSourceValue } from '../profile';
|
18
|
+
import { countrySchema, currencySchema, oneOfOptionalSchema } from './common-schemas';
|
2
19
|
|
3
|
-
|
20
|
+
const yesNoSchema = z.coerce.string().refine(
|
21
|
+
v =>
|
22
|
+
resolveSourceValue(SOURCE_EBAY_YES_NO)
|
23
|
+
?.map(r => r.value)
|
24
|
+
.includes(v),
|
25
|
+
{ message: 'Value must be 0 or 1' }
|
26
|
+
);
|
27
|
+
|
28
|
+
export const eBayFeedTemplate: XmlFeedTemplateType = {
|
4
29
|
formats: [],
|
5
|
-
documentation: '',
|
6
|
-
mapping: [
|
7
|
-
|
30
|
+
documentation: 'https://developer.ebay.com/devzone/xml/docs/reference/ebay/AddFixedPriceItem.html',
|
31
|
+
mapping: [
|
32
|
+
{
|
33
|
+
attribute: 'AutoPay',
|
34
|
+
label: 'AutoPay',
|
35
|
+
description: 'Indicates whether the seller requires immediate payment for the item',
|
36
|
+
required: false,
|
37
|
+
type: 'macro-input',
|
38
|
+
defaultValue: makeMacroString(SOURCE_EBAY_YES_NO, ['1']),
|
39
|
+
rules: { sections: [] },
|
40
|
+
baseMode: false,
|
41
|
+
validator: v => validateIfNoMacro(v, yesNoSchema),
|
42
|
+
fixedValue: true,
|
43
|
+
singleChoice: true,
|
44
|
+
},
|
45
|
+
{
|
46
|
+
attribute: 'BestOfferEnabled',
|
47
|
+
label: 'Best Offer Enabled',
|
48
|
+
description: 'Indicates whether the seller is accepting Best Offers for the item',
|
49
|
+
required: false,
|
50
|
+
type: 'macro-input',
|
51
|
+
defaultValue: makeMacroString(SOURCE_EBAY_YES_NO, ['0']),
|
52
|
+
rules: { sections: [] },
|
53
|
+
baseMode: false,
|
54
|
+
validator: v => validateIfNoMacro(v, yesNoSchema),
|
55
|
+
fixedValue: true,
|
56
|
+
singleChoice: true,
|
57
|
+
},
|
58
|
+
{
|
59
|
+
attribute: 'CategoryMappingAllowed',
|
60
|
+
label: 'Category Mapping Allowed',
|
61
|
+
description:
|
62
|
+
'This boolean field controls whether or not eBay automatically maps outdated Category ID(s) specified in a PrimaryCategory and/or SecondaryCategory container(s) to the updated Category ID(s) no longer exists in the current category structure',
|
63
|
+
required: false,
|
64
|
+
type: 'macro-input',
|
65
|
+
defaultValue: makeMacroString(SOURCE_EBAY_YES_NO, ['1']),
|
66
|
+
rules: { sections: [] },
|
67
|
+
baseMode: false,
|
68
|
+
validator: v => validateIfNoMacro(v, yesNoSchema),
|
69
|
+
fixedValue: true,
|
70
|
+
singleChoice: true,
|
71
|
+
},
|
72
|
+
{
|
73
|
+
attribute: 'ConditionID',
|
74
|
+
label: 'Condition',
|
75
|
+
description: 'The condition of the item. For example, New, Used, or Refurbished',
|
76
|
+
required: false,
|
77
|
+
type: 'macro-input',
|
78
|
+
defaultValue: '',
|
79
|
+
rules: { sections: [] },
|
80
|
+
baseMode: false,
|
81
|
+
singleChoice: true,
|
82
|
+
},
|
83
|
+
{
|
84
|
+
attribute: 'Country',
|
85
|
+
label: 'Country',
|
86
|
+
description: 'The country where the item is located',
|
87
|
+
required: true,
|
88
|
+
type: 'macro-input',
|
89
|
+
defaultValue: makeMacroString(SOURCE_SHOPIFY_SHOP_COUNTRY),
|
90
|
+
rules: { sections: [] },
|
91
|
+
validator: v => validateIfNoMacro(v, countrySchema),
|
92
|
+
baseMode: true,
|
93
|
+
singleChoice: true,
|
94
|
+
},
|
95
|
+
{
|
96
|
+
attribute: 'Currency',
|
97
|
+
label: 'Currency',
|
98
|
+
description: 'The currency of the item price',
|
99
|
+
required: true,
|
100
|
+
type: 'macro-input',
|
101
|
+
defaultValue: makeMacroString(SOURCE_SHOPIFY_SHOP_CURRENCY),
|
102
|
+
rules: { sections: [] },
|
103
|
+
validator: v => validateIfNoMacro(v, currencySchema),
|
104
|
+
baseMode: true,
|
105
|
+
singleChoice: true,
|
106
|
+
},
|
107
|
+
{
|
108
|
+
attribute: 'Description',
|
109
|
+
label: 'Description',
|
110
|
+
description: 'The description of the item',
|
111
|
+
required: true,
|
112
|
+
type: 'macro-input',
|
113
|
+
defaultValue: makeMacroString(SOURCE_SHOPIFY_DESCRIPTION_HTML),
|
114
|
+
rules: { sections: [] },
|
115
|
+
baseMode: true,
|
116
|
+
validator: v => validateIfNoMacro(v, z.string().max(500000)),
|
117
|
+
singleChoice: true,
|
118
|
+
},
|
119
|
+
{
|
120
|
+
attribute: 'DiscountPrice',
|
121
|
+
label: 'Description',
|
122
|
+
description: 'The discount price of the item',
|
123
|
+
required: false,
|
124
|
+
type: 'macro-input',
|
125
|
+
defaultValue: makeMacroString(SOURCE_SHOPIFY_DESCRIPTION_HTML),
|
126
|
+
rules: { sections: [] },
|
127
|
+
baseMode: false,
|
128
|
+
validator: v => validateIfNoMacro(v, z.coerce.number().positive()),
|
129
|
+
singleChoice: true,
|
130
|
+
},
|
131
|
+
{
|
132
|
+
attribute: 'DispatchTimeMax',
|
133
|
+
label: 'Dispatch Time Max',
|
134
|
+
description:
|
135
|
+
'The maximum number of business days the seller commits to for shipping an item to a buyer after receiving a cleared payment',
|
136
|
+
required: false,
|
137
|
+
type: 'macro-input',
|
138
|
+
defaultValue: '',
|
139
|
+
rules: { sections: [] },
|
140
|
+
baseMode: false,
|
141
|
+
validator: v => validateIfNoMacro(v, z.coerce.number().gte(0)),
|
142
|
+
singleChoice: true,
|
143
|
+
},
|
144
|
+
{
|
145
|
+
attribute: 'InventoryTrackingMethod',
|
146
|
+
label: 'Inventory Tracking Method',
|
147
|
+
description: 'The method used to track the inventory level of the item',
|
148
|
+
required: false,
|
149
|
+
type: 'macro-input',
|
150
|
+
defaultValue: makeMacroString(SOURCE_EBAY_INVENTORY_TRACKING),
|
151
|
+
rules: { sections: [] },
|
152
|
+
baseMode: false,
|
153
|
+
validator: v =>
|
154
|
+
validateIfNoMacro(
|
155
|
+
v,
|
156
|
+
oneOfOptionalSchema(resolveSourceValue(SOURCE_EBAY_INVENTORY_TRACKING) as SelectItem[])
|
157
|
+
),
|
158
|
+
singleChoice: true,
|
159
|
+
fixedValue: true,
|
160
|
+
},
|
161
|
+
// TODO: add compatibility list items
|
162
|
+
// TODO: add item specifics
|
163
|
+
{
|
164
|
+
attribute: 'ListingDuration',
|
165
|
+
label: 'Listing Duration',
|
166
|
+
description: 'The length of time the listing will be active',
|
167
|
+
required: false,
|
168
|
+
type: 'macro-input',
|
169
|
+
defaultValue: '', // TODO: add source
|
170
|
+
rules: { sections: [] },
|
171
|
+
baseMode: false,
|
172
|
+
fixedValue: true,
|
173
|
+
singleChoice: true,
|
174
|
+
},
|
175
|
+
{
|
176
|
+
attribute: 'ListingType',
|
177
|
+
label: 'Listing Type',
|
178
|
+
description: 'The type of listing',
|
179
|
+
required: true,
|
180
|
+
type: 'macro-input',
|
181
|
+
defaultValue: makeMacroString(SOURCE_EBAY_LISTING_TYPE, ['FixedPriceItem']),
|
182
|
+
rules: { sections: [] },
|
183
|
+
baseMode: true,
|
184
|
+
fixedValue: true,
|
185
|
+
singleChoice: true,
|
186
|
+
},
|
187
|
+
{
|
188
|
+
attribute: 'GalleryType',
|
189
|
+
label: 'Gallery Type',
|
190
|
+
description: 'The type of gallery image to use for the listing. Waring: Plus type implies additional fee',
|
191
|
+
required: false,
|
192
|
+
type: 'macro-input',
|
193
|
+
defaultValue: makeMacroString(SOURCE_EBAY_GALLERY_TYPE, ['Gallery']),
|
194
|
+
rules: { sections: [] },
|
195
|
+
baseMode: false,
|
196
|
+
fixedValue: true,
|
197
|
+
singleChoice: true,
|
198
|
+
},
|
199
|
+
// TODO: set PictureSource to Vendor
|
200
|
+
{
|
201
|
+
attribute: 'PictureURL',
|
202
|
+
label: 'Picture URL',
|
203
|
+
description:
|
204
|
+
'Contains the URL for a picture of the item. You can pass in up to 24 picture URLs for each listing. The Gallery image will be the first PictureURL in the list',
|
205
|
+
required: false,
|
206
|
+
type: 'macro-input',
|
207
|
+
defaultValue:
|
208
|
+
makeMacroString(SOURCE_SHOPIFY_IMAGE) +
|
209
|
+
',' +
|
210
|
+
makeMacroString(SOURCE_SHOPIFY_IMAGES) +
|
211
|
+
makeMacroString(ACTION_ARRAY_FIRST_ITEMS, ['24']),
|
212
|
+
rules: { sections: [] },
|
213
|
+
baseMode: false,
|
214
|
+
fixedValue: true,
|
215
|
+
singleChoice: true,
|
216
|
+
},
|
217
|
+
],
|
218
|
+
};
|
@@ -2,7 +2,8 @@
|
|
2
2
|
/* eslint-disable quotes */
|
3
3
|
import * as z from 'zod';
|
4
4
|
import { ulid } from 'ulid';
|
5
|
-
import {
|
5
|
+
import { validate, validateIfNoMacro } from './common-schemas.js';
|
6
|
+
import { dateRangeSchema, gtinSchema, measureUnitsSchema, priceSchema, salePriceSchema } from './common-schemas.js';
|
6
7
|
import { XmlFeedTemplateType } from '../../types/profile.types.js';
|
7
8
|
import { XmlFeedFormat } from '../../constants/profile.constants.js';
|
8
9
|
|
@@ -1,11 +1,20 @@
|
|
1
1
|
/* eslint-disable quotes */
|
2
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
3
3
|
/* eslint-disable max-len */
|
4
|
-
import {
|
4
|
+
import { validateIfNoMacro } from './common-schemas.js';
|
5
|
+
import {
|
6
|
+
countrySchema,
|
7
|
+
dateRangeSchema,
|
8
|
+
gtinSchema,
|
9
|
+
languageSchema,
|
10
|
+
measureUnitsSchema,
|
11
|
+
priceSchema,
|
12
|
+
} from './common-schemas.js';
|
5
13
|
import { SOURCE_GOOGLE_PRODUCT_HIGHLIGHT } from '../../constants/profile.constants.js';
|
6
14
|
import { ProductUploadMapSource, XmlFeedTemplateType } from '../../types/profile.types.js';
|
7
15
|
import { z } from 'zod';
|
8
16
|
import { ulid } from 'ulid';
|
17
|
+
import { getSourceValueSeparator } from '../utils.js';
|
9
18
|
|
10
19
|
const availabilityMapping: ProductUploadMapSource = {
|
11
20
|
attribute: 'availability',
|
@@ -54,13 +63,18 @@ export const googleApiTemplate: XmlFeedTemplateType = {
|
|
54
63
|
{
|
55
64
|
attribute: 'contentLanguage',
|
56
65
|
label: 'Content language',
|
57
|
-
description:
|
66
|
+
description:
|
67
|
+
'The two-letter ISO 639-1 language code for the item. Use source Languages macro to set the language',
|
58
68
|
required: true,
|
59
69
|
type: 'macro-input',
|
60
70
|
defaultValue: '{{source.language:}}',
|
61
71
|
rules: { sections: [] },
|
62
72
|
baseMode: true,
|
63
|
-
validator: (v: string | number) =>
|
73
|
+
validator: (v: string | number) =>
|
74
|
+
validateIfNoMacro(v, [
|
75
|
+
languageSchema,
|
76
|
+
z.string().min(2, { message: 'Language should be 2 characters long' }),
|
77
|
+
]),
|
64
78
|
singleChoice: true,
|
65
79
|
fixedValue: true,
|
66
80
|
},
|
@@ -72,7 +86,11 @@ export const googleApiTemplate: XmlFeedTemplateType = {
|
|
72
86
|
type: 'macro-input',
|
73
87
|
defaultValue: '{{source.country:}}',
|
74
88
|
rules: { sections: [] },
|
75
|
-
validator: (v: string | number) =>
|
89
|
+
validator: (v: string | number) =>
|
90
|
+
validateIfNoMacro(v, [
|
91
|
+
countrySchema,
|
92
|
+
z.string().min(2, { message: 'Country code should be 2 characters long' }),
|
93
|
+
]),
|
76
94
|
baseMode: true,
|
77
95
|
singleChoice: true,
|
78
96
|
fixedValue: true,
|
@@ -238,8 +256,10 @@ export const googleApiTemplate: XmlFeedTemplateType = {
|
|
238
256
|
v,
|
239
257
|
z
|
240
258
|
.string()
|
241
|
-
.refine(v => !v || z.string().datetime({ offset: true }).safeParse(v).success, {
|
242
|
-
|
259
|
+
.refine(v => !v || z.string().datetime({ offset: true }).safeParse(v).success, {
|
260
|
+
message: 'invalid date',
|
261
|
+
})
|
262
|
+
.refine(v => !v || new Date(v).getTime() < 1000 * 60 * 60 * 24 * 365 + new Date().getTime(), {
|
243
263
|
message: 'The date should be up to one year in the future',
|
244
264
|
})
|
245
265
|
),
|
@@ -270,8 +290,10 @@ export const googleApiTemplate: XmlFeedTemplateType = {
|
|
270
290
|
v,
|
271
291
|
z
|
272
292
|
.string()
|
273
|
-
.refine(v => !v || z.string().datetime({ offset: true }).safeParse(v).success, {
|
274
|
-
|
293
|
+
.refine(v => !v || z.string().datetime({ offset: true }).safeParse(v).success, {
|
294
|
+
message: 'invalid date',
|
295
|
+
})
|
296
|
+
.refine(v => !v || new Date(v).getTime() < 1000 * 60 * 60 * 24 * 30 + new Date().getTime(), {
|
275
297
|
message: 'The date should be less than 30 days in the future',
|
276
298
|
})
|
277
299
|
),
|
@@ -798,30 +820,33 @@ export const googleApiTemplate: XmlFeedTemplateType = {
|
|
798
820
|
'The most relevant highlights of your products. Max 150 characters. Example: Supports thousands of apps, including Netflix, YouTube, and HBO Max',
|
799
821
|
required: false,
|
800
822
|
type: 'macro-input',
|
801
|
-
validator: (v: string | number) =>
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
823
|
+
validator: (v: string | number) =>
|
824
|
+
validateIfNoMacro(
|
825
|
+
v,
|
826
|
+
z.string().superRefine((v, ctx) => {
|
827
|
+
if (!v) {
|
828
|
+
return true;
|
829
|
+
}
|
807
830
|
|
808
|
-
|
809
|
-
ctx.addIssue({
|
810
|
-
code: z.ZodIssueCode.custom,
|
811
|
-
message: 'Minimum 2 values required',
|
812
|
-
});
|
813
|
-
return false;
|
814
|
-
}
|
831
|
+
const values = v.split(getSourceValueSeparator(SOURCE_GOOGLE_PRODUCT_HIGHLIGHT));
|
815
832
|
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
833
|
+
if (values.length < 2) {
|
834
|
+
ctx.addIssue({
|
835
|
+
code: z.ZodIssueCode.custom,
|
836
|
+
message: 'Minimum 2 values required',
|
837
|
+
});
|
838
|
+
return false;
|
839
|
+
}
|
823
840
|
|
824
|
-
|
841
|
+
if (values.some(val => val.length > 150)) {
|
842
|
+
ctx.addIssue({
|
843
|
+
code: z.ZodIssueCode.custom,
|
844
|
+
message: 'Max 150 characters per value',
|
845
|
+
});
|
846
|
+
return false;
|
847
|
+
}
|
848
|
+
})
|
849
|
+
),
|
825
850
|
defaultValue: '{{source.google_product_highlight:}}',
|
826
851
|
rules: { sections: [] },
|
827
852
|
fixedValue: true,
|
@@ -1,22 +1,22 @@
|
|
1
1
|
/* eslint-disable quotes */
|
2
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
3
3
|
/* eslint-disable max-len */
|
4
|
+
import { resolveSourceMacro } from '../utils.js';
|
5
|
+
import { validateIfNoMacro } from './common-schemas.js';
|
4
6
|
import {
|
5
7
|
countrySchema,
|
6
8
|
dateRangeSchema,
|
7
9
|
energyEfficiencyClassSchema,
|
8
|
-
getSourceValueSeparator,
|
9
10
|
gtinSchema,
|
10
11
|
maxEnergyEfficiencyClassSchema,
|
11
12
|
measureUnitsSchema,
|
12
13
|
minEnergyEfficiencyClassSchema,
|
13
14
|
priceSchema,
|
14
15
|
requiredIfOtherPresentSchema,
|
15
|
-
resolveSourceMacro,
|
16
16
|
salePriceSchema,
|
17
17
|
sameUnitScheme,
|
18
|
-
|
19
|
-
} from '
|
18
|
+
} from './common-schemas.js';
|
19
|
+
import { getSourceValueSeparator } from '../utils.js';
|
20
20
|
import { SOURCE_GOOGLE_PRODUCT_HIGHLIGHT, XmlFeedFormat } from '../../constants/profile.constants.js';
|
21
21
|
import { ProductUploadMapSource, XmlFeedTemplateType } from '../../types/profile.types.js';
|
22
22
|
import { z } from 'zod';
|
@@ -39,18 +39,6 @@ const availabilityMapping: ProductUploadMapSource = {
|
|
39
39
|
fixedValue: true,
|
40
40
|
};
|
41
41
|
|
42
|
-
const customLabelMapping: ProductUploadMapSource = {
|
43
|
-
attribute: '',
|
44
|
-
label: '',
|
45
|
-
description:
|
46
|
-
'Label that you assign to a product to help organize bidding and reporting in Shopping campaigns. Max 100 characters. Example: Seasonal',
|
47
|
-
required: false,
|
48
|
-
type: 'macro-input' as any,
|
49
|
-
validator: (v: string | number) => validateIfNoMacro(v, z.string().max(100)),
|
50
|
-
rules: { sections: [] },
|
51
|
-
defaultValue: '',
|
52
|
-
};
|
53
|
-
|
54
42
|
export const googleFeedTemplate: XmlFeedTemplateType = {
|
55
43
|
formats: [XmlFeedFormat.XML],
|
56
44
|
documentation: 'https://support.google.com/merchants/answer/7052112',
|
@@ -124,21 +112,20 @@ export const googleFeedTemplate: XmlFeedTemplateType = {
|
|
124
112
|
validator: (v: string | number) =>
|
125
113
|
validateIfNoMacro(
|
126
114
|
v,
|
127
|
-
z
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
}
|
115
|
+
z.string().superRefine((v, ctx) => {
|
116
|
+
if (!v) {
|
117
|
+
return;
|
118
|
+
}
|
132
119
|
|
133
|
-
|
120
|
+
const urls = v.split(getSourceValueSeparator());
|
134
121
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
122
|
+
if (urls.some(url => !z.string().url().safeParse(url).success)) {
|
123
|
+
ctx.addIssue({
|
124
|
+
code: z.ZodIssueCode.custom,
|
125
|
+
message: 'invalid URL',
|
126
|
+
});
|
127
|
+
}
|
128
|
+
})
|
142
129
|
),
|
143
130
|
defaultValue: '',
|
144
131
|
rules: { sections: [] },
|
@@ -898,30 +885,33 @@ export const googleFeedTemplate: XmlFeedTemplateType = {
|
|
898
885
|
'The most relevant highlights of your products. Max 150 characters. Example: Supports thousands of apps, including Netflix, YouTube, and HBO Max',
|
899
886
|
required: false,
|
900
887
|
type: 'macro-input',
|
901
|
-
validator: (v: string | number) =>
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
888
|
+
validator: (v: string | number) =>
|
889
|
+
validateIfNoMacro(
|
890
|
+
v,
|
891
|
+
z.string().superRefine((v, ctx) => {
|
892
|
+
if (!v) {
|
893
|
+
return true;
|
894
|
+
}
|
907
895
|
|
908
|
-
|
909
|
-
ctx.addIssue({
|
910
|
-
code: z.ZodIssueCode.custom,
|
911
|
-
message: 'Minimum 2 values required',
|
912
|
-
});
|
913
|
-
return false;
|
914
|
-
}
|
896
|
+
const values = v.split(getSourceValueSeparator(SOURCE_GOOGLE_PRODUCT_HIGHLIGHT));
|
915
897
|
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
898
|
+
if (values.length < 2) {
|
899
|
+
ctx.addIssue({
|
900
|
+
code: z.ZodIssueCode.custom,
|
901
|
+
message: 'Minimum 2 values required',
|
902
|
+
});
|
903
|
+
return false;
|
904
|
+
}
|
923
905
|
|
924
|
-
|
906
|
+
if (values.some(val => val.length > 150)) {
|
907
|
+
ctx.addIssue({
|
908
|
+
code: z.ZodIssueCode.custom,
|
909
|
+
message: 'Max 150 characters per value',
|
910
|
+
});
|
911
|
+
return false;
|
912
|
+
}
|
913
|
+
})
|
914
|
+
),
|
925
915
|
defaultValue: '{{source.google_product_highlight:}}',
|
926
916
|
rules: { sections: [] },
|
927
917
|
fixedValue: true,
|
@@ -1149,8 +1139,12 @@ export const googleFeedTemplate: XmlFeedTemplateType = {
|
|
1149
1139
|
if (!v) {
|
1150
1140
|
return true;
|
1151
1141
|
}
|
1152
|
-
const installment = getSourceTagValueAndStripTags(
|
1153
|
-
|
1142
|
+
const installment = getSourceTagValueAndStripTags(
|
1143
|
+
(values.find(v => v.attribute === 'installment')?.value as string) ?? ''
|
1144
|
+
)?.value;
|
1145
|
+
const subscription = getSourceTagValueAndStripTags(
|
1146
|
+
(values.find(v => v.attribute === 'subscription_cost')?.value as string) ?? ''
|
1147
|
+
)?.value;
|
1154
1148
|
const shippingValue = v.split(',').map(val => val.trim());
|
1155
1149
|
const shipToSameCountry = new Set(shippingValue.map(v => v.split(':')[0])).size === 1;
|
1156
1150
|
|