adapty 0.8.4 → 0.8.5
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/README.md +1 -1
- package/bin/dev.js +2 -2
- package/bin/run.js +2 -2
- package/dist/commands/access-levels/create.js +1 -1
- package/dist/commands/access-levels/list.d.ts +1 -1
- package/dist/commands/apps/create.js +9 -6
- package/dist/commands/apps/list.d.ts +1 -1
- package/dist/commands/apps/update.js +7 -4
- package/dist/commands/asa/ad-groups/create.js +9 -7
- package/dist/commands/asa/ad-groups/get.js +3 -2
- package/dist/commands/asa/ad-groups/list.d.ts +1 -1
- package/dist/commands/asa/ad-groups/list.js +1 -1
- package/dist/commands/asa/ad-groups/update.js +22 -12
- package/dist/commands/asa/ads/create.js +7 -5
- package/dist/commands/asa/ads/get.js +3 -2
- package/dist/commands/asa/ads/list.d.ts +1 -1
- package/dist/commands/asa/ads/list.js +1 -1
- package/dist/commands/asa/ads/update.js +9 -5
- package/dist/commands/asa/apps/list.d.ts +1 -1
- package/dist/commands/asa/apps/list.js +1 -1
- package/dist/commands/asa/automations/create.js +9 -6
- package/dist/commands/asa/automations/get.js +3 -2
- package/dist/commands/asa/automations/list.d.ts +1 -1
- package/dist/commands/asa/automations/list.js +1 -1
- package/dist/commands/asa/automations/run.js +5 -3
- package/dist/commands/asa/automations/runs.d.ts +1 -1
- package/dist/commands/asa/automations/runs.js +4 -4
- package/dist/commands/asa/automations/update.js +17 -9
- package/dist/commands/asa/campaigns/bulk-create.js +31 -18
- package/dist/commands/asa/campaigns/bulk-list.js +7 -5
- package/dist/commands/asa/campaigns/bulk-status.js +3 -2
- package/dist/commands/asa/campaigns/create.js +10 -8
- package/dist/commands/asa/campaigns/get.js +3 -2
- package/dist/commands/asa/campaigns/list.d.ts +1 -1
- package/dist/commands/asa/campaigns/list.js +1 -1
- package/dist/commands/asa/campaigns/update.js +25 -15
- package/dist/commands/asa/competitors/summary.js +5 -5
- package/dist/commands/asa/connect.js +5 -3
- package/dist/commands/asa/creatives/list.d.ts +1 -1
- package/dist/commands/asa/creatives/list.js +1 -1
- package/dist/commands/asa/keywords/add.js +13 -10
- package/dist/commands/asa/keywords/list.d.ts +1 -1
- package/dist/commands/asa/keywords/list.js +1 -1
- package/dist/commands/asa/keywords/update.js +15 -10
- package/dist/commands/asa/metrics/index.d.ts +2 -2
- package/dist/commands/asa/metrics/index.js +22 -8
- package/dist/commands/asa/metrics/overview.js +3 -3
- package/dist/commands/asa/negative-keywords/add.js +10 -7
- package/dist/commands/asa/negative-keywords/list.d.ts +1 -1
- package/dist/commands/asa/negative-keywords/list.js +1 -1
- package/dist/commands/asa/orgs/list.d.ts +1 -1
- package/dist/commands/asa/orgs/list.js +1 -1
- package/dist/commands/asa/product-pages/list.d.ts +1 -1
- package/dist/commands/asa/product-pages/list.js +1 -1
- package/dist/commands/asa/product-pages/sync.js +1 -1
- package/dist/commands/asa/search-terms/list.d.ts +1 -1
- package/dist/commands/asa/search-terms/list.js +1 -1
- package/dist/commands/asa/whoami.js +7 -2
- package/dist/commands/auth/login.js +3 -1
- package/dist/commands/flows/config/get.js +2 -2
- package/dist/commands/flows/config/preview.d.ts +2 -2
- package/dist/commands/flows/config/preview.js +6 -4
- package/dist/commands/flows/config/update.js +5 -3
- package/dist/commands/flows/config/validate.js +5 -3
- package/dist/commands/flows/list.d.ts +1 -1
- package/dist/commands/flows/media/upload.js +1 -1
- package/dist/commands/flows/publish.js +5 -5
- package/dist/commands/paywalls/create.js +1 -1
- package/dist/commands/paywalls/list.d.ts +1 -1
- package/dist/commands/paywalls/placements.d.ts +2 -2
- package/dist/commands/paywalls/update.js +1 -1
- package/dist/commands/placements/create.js +17 -12
- package/dist/commands/placements/list.d.ts +1 -1
- package/dist/commands/placements/update.js +20 -15
- package/dist/commands/products/create.js +20 -13
- package/dist/commands/products/list.d.ts +1 -1
- package/dist/commands/products/update.js +1 -1
- package/dist/commands/segments/list.d.ts +1 -1
- package/dist/lib/api-client.d.ts +10 -6
- package/dist/lib/api-client.js +56 -35
- package/dist/lib/api-schemas.d.ts +56 -56
- package/dist/lib/app-url.js +2 -1
- package/dist/lib/asa-client.d.ts +13 -8
- package/dist/lib/asa-client.js +9 -4
- package/dist/lib/asa-flags.d.ts +19 -19
- package/dist/lib/asa-flags.js +38 -25
- package/dist/lib/asa-keyword-action.d.ts +10 -10
- package/dist/lib/asa-keyword-action.js +40 -25
- package/dist/lib/asa-schemas.d.ts +100 -80
- package/dist/lib/auth.js +2 -1
- package/dist/lib/client-from-config.d.ts +1 -1
- package/dist/lib/client-from-config.js +2 -1
- package/dist/lib/config.d.ts +2 -2
- package/dist/lib/confirm.d.ts +4 -4
- package/dist/lib/confirm.js +12 -7
- package/dist/lib/errors.d.ts +18 -18
- package/dist/lib/errors.js +12 -9
- package/dist/lib/flags.d.ts +3 -3
- package/dist/lib/flags.js +2 -1
- package/dist/lib/flow-help.js +11 -6
- package/dist/lib/output.js +23 -12
- package/dist/lib/preview.d.ts +5 -5
- package/dist/lib/preview.js +2 -1
- package/oclif.manifest.json +131 -131
- package/package.json +10 -8
package/dist/lib/asa-client.js
CHANGED
|
@@ -5,13 +5,16 @@ import { buildUserAgent } from './client-from-config.js';
|
|
|
5
5
|
import { AuthRequiredError, NetworkError } from './errors.js';
|
|
6
6
|
export const ASA_API_URL = 'https://api-asa-admin.adapty.io/api/v1/cli';
|
|
7
7
|
export const ASA_API_URL_ENV_VAR = 'ADAPTY_ASA_API_URL';
|
|
8
|
-
export async function createAsaClient(
|
|
8
|
+
export async function createAsaClient(command) {
|
|
9
|
+
const { config } = command;
|
|
9
10
|
const token = await resolveToken(config.configDir);
|
|
10
|
-
if (!token)
|
|
11
|
+
if (!token) {
|
|
11
12
|
throw new AuthRequiredError();
|
|
13
|
+
}
|
|
12
14
|
return new ApiClient({
|
|
13
15
|
defaultBaseUrl: ASA_API_URL,
|
|
14
16
|
errorFormat: 'asa',
|
|
17
|
+
quiet: command.jsonEnabled(),
|
|
15
18
|
token,
|
|
16
19
|
urlEnvVar: ASA_API_URL_ENV_VAR,
|
|
17
20
|
userAgent: buildUserAgent(config),
|
|
@@ -34,13 +37,15 @@ export async function asaWrite(client, method, path, opts = {}) {
|
|
|
34
37
|
return { replayed, result };
|
|
35
38
|
}
|
|
36
39
|
catch (error) {
|
|
37
|
-
if (!(error instanceof NetworkError))
|
|
40
|
+
if (!(error instanceof NetworkError)) {
|
|
38
41
|
throw error;
|
|
42
|
+
}
|
|
39
43
|
const result = await send();
|
|
40
44
|
return { replayed, result };
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
47
|
export function noteReplay(replayed, log) {
|
|
44
|
-
if (replayed)
|
|
48
|
+
if (replayed) {
|
|
45
49
|
log('Already applied earlier — showing the stored result.');
|
|
50
|
+
}
|
|
46
51
|
}
|
package/dist/lib/asa-flags.d.ts
CHANGED
|
@@ -50,25 +50,25 @@ export declare const metricsScopeFlags: {
|
|
|
50
50
|
campaign: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
51
51
|
};
|
|
52
52
|
export declare function metricsScopeBody(flags: {
|
|
53
|
-
'ad-group'?: string[];
|
|
54
|
-
app?: string[];
|
|
55
|
-
campaign?: string[];
|
|
53
|
+
'ad-group'?: string[] | undefined;
|
|
54
|
+
'app'?: string[] | undefined;
|
|
55
|
+
'campaign'?: string[] | undefined;
|
|
56
56
|
}): {
|
|
57
|
-
campaign_id?: string[]
|
|
58
|
-
app_id?: string[]
|
|
59
|
-
ad_group_id?: string[]
|
|
57
|
+
campaign_id?: string[];
|
|
58
|
+
app_id?: string[];
|
|
59
|
+
ad_group_id?: string[];
|
|
60
60
|
};
|
|
61
61
|
export declare const statusFilter: (options: string[]) => {
|
|
62
62
|
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
63
63
|
};
|
|
64
|
-
|
|
65
|
-
'ad-group'?: string[];
|
|
66
|
-
app?: string[];
|
|
67
|
-
campaign?: string[];
|
|
68
|
-
'campaign-group'?: string[];
|
|
69
|
-
search?: string;
|
|
70
|
-
status?: string;
|
|
71
|
-
}
|
|
64
|
+
type ScopeFlags = {
|
|
65
|
+
'ad-group'?: string[] | undefined;
|
|
66
|
+
'app'?: string[] | undefined;
|
|
67
|
+
'campaign'?: string[] | undefined;
|
|
68
|
+
'campaign-group'?: string[] | undefined;
|
|
69
|
+
'search'?: string | undefined;
|
|
70
|
+
'status'?: string | undefined;
|
|
71
|
+
};
|
|
72
72
|
export declare function scopeParams(flags: ScopeFlags): QueryParams;
|
|
73
73
|
export declare const scheduleFlags: {
|
|
74
74
|
'end-time': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -80,8 +80,8 @@ export declare const pricingModelFlag: {
|
|
|
80
80
|
export declare function startOfDayUtc(date: string | undefined): string | undefined;
|
|
81
81
|
export declare function todayUtc(): string;
|
|
82
82
|
export declare function periodParams(flags: {
|
|
83
|
-
'date-from'?: string;
|
|
84
|
-
'date-to'?: string;
|
|
83
|
+
'date-from'?: string | undefined;
|
|
84
|
+
'date-to'?: string | undefined;
|
|
85
85
|
}): Record<string, string>;
|
|
86
86
|
export declare function moneyFlag(description: string, opts?: {
|
|
87
87
|
required?: boolean;
|
|
@@ -118,17 +118,17 @@ export declare const invoiceFlags: {
|
|
|
118
118
|
'invoice-contact-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
119
119
|
'invoice-order-number': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
120
120
|
};
|
|
121
|
-
export declare function locInvoiceDetails(flags: Partial<Record<InvoiceFlagName, string>>): Record<keyof AsaLocInvoiceDetails, string> | undefined;
|
|
121
|
+
export declare function locInvoiceDetails(flags: Partial<Record<InvoiceFlagName, string | undefined>>): Record<keyof AsaLocInvoiceDetails, string> | undefined;
|
|
122
122
|
export declare function reportServingState(campaign: null | {
|
|
123
123
|
internal_id: string;
|
|
124
124
|
serving_state_reasons?: null | string[];
|
|
125
125
|
serving_status?: null | string;
|
|
126
126
|
}, warn: (msg: string) => void): void;
|
|
127
|
-
|
|
127
|
+
type BulkOutcome = {
|
|
128
128
|
applied: unknown[];
|
|
129
129
|
errors: AsaMutationError[];
|
|
130
130
|
isValidationFailure: boolean;
|
|
131
131
|
kind: string;
|
|
132
|
-
}
|
|
132
|
+
};
|
|
133
133
|
export declare function reportBulkOutcome({ applied, errors, isValidationFailure, kind }: BulkOutcome, log: (msg: string) => void): void;
|
|
134
134
|
export {};
|
package/dist/lib/asa-flags.js
CHANGED
|
@@ -9,7 +9,7 @@ export const MAX_BY_DAYS = 16;
|
|
|
9
9
|
export const ASA_METRIC_ENTITIES = ['ad', 'ad-group', 'campaign', 'keyword'];
|
|
10
10
|
export const ASA_GROUP_BY_DIMENSIONS = ['country', 'day', 'month', 'quarter', 'week', 'year'];
|
|
11
11
|
export const asaPaginationFlags = {
|
|
12
|
-
page: Flags.integer({
|
|
12
|
+
'page': Flags.integer({
|
|
13
13
|
default: 1,
|
|
14
14
|
description: 'Page number',
|
|
15
15
|
min: 1,
|
|
@@ -27,9 +27,11 @@ export const byDaysFlag = {
|
|
|
27
27
|
multiple: true,
|
|
28
28
|
}),
|
|
29
29
|
};
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/require-await -- FIXME if you see this
|
|
30
31
|
export async function parseDate(input) {
|
|
31
|
-
if (!DATE_REGEX.test(input))
|
|
32
|
+
if (!DATE_REGEX.test(input)) {
|
|
32
33
|
throw new Error('Dates must be written as YYYY-MM-DD.');
|
|
34
|
+
}
|
|
33
35
|
return input;
|
|
34
36
|
}
|
|
35
37
|
export const periodFlags = {
|
|
@@ -42,9 +44,11 @@ export const periodFlags = {
|
|
|
42
44
|
parse: parseDate,
|
|
43
45
|
}),
|
|
44
46
|
};
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/require-await -- FIXME if you see this
|
|
45
48
|
async function parseId(input) {
|
|
46
|
-
if (!isValidUuid(input))
|
|
49
|
+
if (!isValidUuid(input)) {
|
|
47
50
|
throw new Error('Ids are the UUIDs printed by the matching list command.');
|
|
51
|
+
}
|
|
48
52
|
return input;
|
|
49
53
|
}
|
|
50
54
|
function idFilter(entity) {
|
|
@@ -52,7 +56,7 @@ function idFilter(entity) {
|
|
|
52
56
|
}
|
|
53
57
|
const searchFilter = { search: Flags.string({ description: 'Case-insensitive substring match on the name' }) };
|
|
54
58
|
export const assetScopeFlags = {
|
|
55
|
-
app: idFilter('app'),
|
|
59
|
+
'app': idFilter('app'),
|
|
56
60
|
'campaign-group': idFilter('campaign group'),
|
|
57
61
|
};
|
|
58
62
|
export const orgScopeFlags = { ...assetScopeFlags, ...searchFilter };
|
|
@@ -60,14 +64,14 @@ export const campaignScopeFlags = { ...orgScopeFlags, campaign: idFilter('campai
|
|
|
60
64
|
export const adGroupScopeFlags = { ...campaignScopeFlags, 'ad-group': idFilter('ad group') };
|
|
61
65
|
export const adScopeFlags = {
|
|
62
66
|
'ad-group': idFilter('ad group'),
|
|
63
|
-
campaign: idFilter('campaign'),
|
|
67
|
+
'campaign': idFilter('campaign'),
|
|
64
68
|
'campaign-group': idFilter('campaign group'),
|
|
65
69
|
...searchFilter,
|
|
66
70
|
};
|
|
67
71
|
export const metricsScopeFlags = {
|
|
68
72
|
'ad-group': idFilter('ad group'),
|
|
69
|
-
app: idFilter('app'),
|
|
70
|
-
campaign: idFilter('campaign'),
|
|
73
|
+
'app': idFilter('app'),
|
|
74
|
+
'campaign': idFilter('campaign'),
|
|
71
75
|
};
|
|
72
76
|
export function metricsScopeBody(flags) {
|
|
73
77
|
return {
|
|
@@ -108,22 +112,26 @@ export function todayUtc() {
|
|
|
108
112
|
}
|
|
109
113
|
export function periodParams(flags) {
|
|
110
114
|
const params = {};
|
|
111
|
-
if (flags['date-from'])
|
|
115
|
+
if (flags['date-from']) {
|
|
112
116
|
params.date_from = flags['date-from'];
|
|
113
|
-
|
|
117
|
+
}
|
|
118
|
+
if (flags['date-to']) {
|
|
114
119
|
params.date_to = flags['date-to'];
|
|
120
|
+
}
|
|
115
121
|
return params;
|
|
116
122
|
}
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/require-await -- FIXME if you see this
|
|
117
124
|
async function parseMoney(input) {
|
|
118
|
-
if (!MONEY_REGEX.test(input))
|
|
125
|
+
if (!MONEY_REGEX.test(input)) {
|
|
119
126
|
throw new Error('Amounts must be plain numbers, e.g. 50 or 12.50.');
|
|
127
|
+
}
|
|
120
128
|
return input;
|
|
121
129
|
}
|
|
122
130
|
export function moneyFlag(description, opts = {}) {
|
|
123
131
|
return Flags.string({
|
|
124
132
|
description: `${description} (amount, e.g. 50 or 12.50)`,
|
|
125
133
|
parse: parseMoney,
|
|
126
|
-
required: opts.required,
|
|
134
|
+
...(opts.required === undefined ? {} : { required: opts.required }),
|
|
127
135
|
});
|
|
128
136
|
}
|
|
129
137
|
export const currencyFlag = {
|
|
@@ -135,8 +143,8 @@ export const currencyFlag = {
|
|
|
135
143
|
// --pause-original is targeting-keyword only. See lib/asa-keyword-action.ts.
|
|
136
144
|
export const addKeywordActionFlags = {
|
|
137
145
|
'cpt-bid': Flags.string({
|
|
138
|
-
description: 'cpt_bid.value: the bid itself with --cpt-bid-type set_to, or a percent markup on the entity bid with '
|
|
139
|
-
'search_term_current_cpt / keyword_current_bid; not accepted with ad_group_default_bid',
|
|
146
|
+
description: 'cpt_bid.value: the bid itself with --cpt-bid-type set_to, or a percent markup on the entity bid with '
|
|
147
|
+
+ 'search_term_current_cpt / keyword_current_bid; not accepted with ad_group_default_bid',
|
|
140
148
|
parse: parseMoney,
|
|
141
149
|
}),
|
|
142
150
|
'cpt-bid-type': Flags.string({
|
|
@@ -147,7 +155,7 @@ export const addKeywordActionFlags = {
|
|
|
147
155
|
description: 'Match type of the created keyword; the API has no default',
|
|
148
156
|
options: KEYWORD_MATCH_TYPES,
|
|
149
157
|
}),
|
|
150
|
-
negate: Flags.string({
|
|
158
|
+
'negate': Flags.string({
|
|
151
159
|
description: 'Also add the search term as a negative keyword at this level (search-term rules)',
|
|
152
160
|
exclusive: ['no-negate'],
|
|
153
161
|
options: NEGATE_TYPES,
|
|
@@ -198,32 +206,37 @@ export const invoiceFlags = {
|
|
|
198
206
|
};
|
|
199
207
|
export function locInvoiceDetails(flags) {
|
|
200
208
|
const names = Object.keys(INVOICE_FIELDS);
|
|
201
|
-
const missing = names.filter(
|
|
202
|
-
if (missing.length === names.length)
|
|
209
|
+
const missing = names.filter(name => flags[name] === undefined);
|
|
210
|
+
if (missing.length === names.length) {
|
|
203
211
|
return undefined;
|
|
212
|
+
}
|
|
204
213
|
if (missing.length > 0) {
|
|
205
|
-
throw new Error(`Invoicing Options need all five flags; missing: ${missing.map(
|
|
214
|
+
throw new Error(`Invoicing Options need all five flags; missing: ${missing.map(name => `--${name}`).join(', ')}`);
|
|
206
215
|
}
|
|
207
|
-
return Object.fromEntries(names.map(
|
|
216
|
+
return Object.fromEntries(names.map(name => [INVOICE_FIELDS[name], flags[name]]));
|
|
208
217
|
}
|
|
209
218
|
const SERVING_HINTS = {
|
|
210
|
-
AUTOMATED_KEYWORDS_REQUIRED_AD_GROUP_MISSING:
|
|
211
|
-
MISSING_BO_OR_INVOICING_FIELDS:
|
|
219
|
+
AUTOMATED_KEYWORDS_REQUIRED_AD_GROUP_MISSING: campaignId => `Max Conversions campaign needs an automated ad group: adapty asa ad-groups create --campaign ${campaignId} --name "Automated Max Conv" --automated`,
|
|
220
|
+
MISSING_BO_OR_INVOICING_FIELDS: campaignId => `This organization bills by line of credit — add Invoicing Options: adapty asa campaigns update ${campaignId} --invoice-advertiser ... --invoice-order-number ... --invoice-contact-name ... --invoice-contact-email ... --invoice-billing-email ...`,
|
|
212
221
|
};
|
|
213
222
|
export function reportServingState(campaign, warn) {
|
|
214
|
-
if (campaign?.serving_status !== 'NOT_RUNNING' || !campaign.serving_state_reasons?.length)
|
|
223
|
+
if (campaign?.serving_status !== 'NOT_RUNNING' || !campaign.serving_state_reasons?.length) {
|
|
215
224
|
return;
|
|
225
|
+
}
|
|
216
226
|
const reasons = campaign.serving_state_reasons;
|
|
217
|
-
if (reasons.length === 1 && reasons[0] === 'PAUSED_BY_USER')
|
|
227
|
+
if (reasons.length === 1 && reasons[0] === 'PAUSED_BY_USER') {
|
|
218
228
|
return;
|
|
229
|
+
}
|
|
219
230
|
for (const reason of reasons) {
|
|
220
231
|
const hint = SERVING_HINTS[reason];
|
|
221
|
-
if (hint)
|
|
232
|
+
if (hint) {
|
|
222
233
|
warn(hint(campaign.internal_id));
|
|
234
|
+
}
|
|
223
235
|
}
|
|
224
|
-
const other = reasons.filter(
|
|
225
|
-
if (other.length > 0)
|
|
236
|
+
const other = reasons.filter(reason => !SERVING_HINTS[reason]);
|
|
237
|
+
if (other.length > 0) {
|
|
226
238
|
warn(`Not serving: ${other.join(', ')}`);
|
|
239
|
+
}
|
|
227
240
|
}
|
|
228
241
|
export function reportBulkOutcome({ applied, errors, isValidationFailure, kind }, log) {
|
|
229
242
|
if (isValidationFailure) {
|
|
@@ -2,16 +2,16 @@ export declare const ADD_AS_KEYWORD_ACTION = "add-as-keyword-to";
|
|
|
2
2
|
export declare const CPT_BID_TYPES: string[];
|
|
3
3
|
export declare const KEYWORD_MATCH_TYPES: string[];
|
|
4
4
|
export declare const NEGATE_TYPES: string[];
|
|
5
|
-
export
|
|
6
|
-
'cpt-bid'?: string;
|
|
7
|
-
'cpt-bid-type'?: string;
|
|
8
|
-
'match-type'?: string;
|
|
9
|
-
negate?: string;
|
|
10
|
-
'no-negate'?: boolean;
|
|
11
|
-
'pause-original'?: boolean;
|
|
12
|
-
'skip-enable-duplicates'?: boolean;
|
|
13
|
-
'target-ad-group'?: string[];
|
|
14
|
-
}
|
|
5
|
+
export type AddKeywordActionFlags = {
|
|
6
|
+
'cpt-bid'?: string | undefined;
|
|
7
|
+
'cpt-bid-type'?: string | undefined;
|
|
8
|
+
'match-type'?: string | undefined;
|
|
9
|
+
'negate'?: string | undefined;
|
|
10
|
+
'no-negate'?: boolean | undefined;
|
|
11
|
+
'pause-original'?: boolean | undefined;
|
|
12
|
+
'skip-enable-duplicates'?: boolean | undefined;
|
|
13
|
+
'target-ad-group'?: string[] | undefined;
|
|
14
|
+
};
|
|
15
15
|
export declare function hasAddKeywordActionFlags(flags: AddKeywordActionFlags): boolean;
|
|
16
16
|
export declare function buildAddKeywordParams(operateWith: unknown, stored: unknown, flags: AddKeywordActionFlags): Record<string, unknown>;
|
|
17
17
|
export declare function requireSingleAction(actions: unknown): Record<string, unknown>;
|
|
@@ -29,15 +29,17 @@ function asRecord(value) {
|
|
|
29
29
|
}
|
|
30
30
|
function isFlagSet(flags, name) {
|
|
31
31
|
const value = flags[name];
|
|
32
|
-
if (value === undefined)
|
|
32
|
+
if (value === undefined) {
|
|
33
33
|
return false;
|
|
34
|
-
|
|
34
|
+
}
|
|
35
|
+
if (Array.isArray(value)) {
|
|
35
36
|
return value.length > 0;
|
|
37
|
+
}
|
|
36
38
|
// --no-negate has no negated form of its own, so only a true means it was passed.
|
|
37
39
|
return name === 'no-negate' ? value === true : true;
|
|
38
40
|
}
|
|
39
41
|
export function hasAddKeywordActionFlags(flags) {
|
|
40
|
-
return ALL_FLAGS.some(
|
|
42
|
+
return ALL_FLAGS.some(name => isFlagSet(flags, name));
|
|
41
43
|
}
|
|
42
44
|
// Params are rebuilt, not patched: a rule may store the add-as-negative-keyword shape
|
|
43
45
|
// ({target_type, ids}) under an add-as-keyword-to action, and the API picks the params variant by
|
|
@@ -64,46 +66,54 @@ function storedBoolean(value) {
|
|
|
64
66
|
}
|
|
65
67
|
function storedNegate(params) {
|
|
66
68
|
const { enabled, type } = asRecord(params.negate);
|
|
67
|
-
if (typeof enabled !== 'boolean')
|
|
69
|
+
if (typeof enabled !== 'boolean') {
|
|
68
70
|
return undefined;
|
|
69
|
-
|
|
71
|
+
}
|
|
72
|
+
if (type === null || type === undefined) {
|
|
70
73
|
return { enabled, type: null };
|
|
74
|
+
}
|
|
71
75
|
return typeof type === 'string' && NEGATE_TYPES.includes(type) ? { enabled, type } : undefined;
|
|
72
76
|
}
|
|
73
77
|
function resolveNegate(flags, params) {
|
|
74
|
-
if (flags.negate !== undefined)
|
|
78
|
+
if (flags.negate !== undefined) {
|
|
75
79
|
return { enabled: true, type: flags.negate };
|
|
76
|
-
|
|
80
|
+
}
|
|
81
|
+
if (flags['no-negate'] === true) {
|
|
77
82
|
return { enabled: false, type: null };
|
|
83
|
+
}
|
|
78
84
|
return storedNegate(params) ?? { enabled: false, type: null };
|
|
79
85
|
}
|
|
80
86
|
function resolveCptBidValue(cptBidType, bid, params) {
|
|
81
|
-
if (cptBidType === 'set_to')
|
|
87
|
+
if (cptBidType === 'set_to') {
|
|
82
88
|
return bid === undefined ? storedCptBidValue(params) : Number(bid);
|
|
89
|
+
}
|
|
83
90
|
if (cptBidType !== undefined && BID_TYPES_READING_MARKUP.has(cptBidType)) {
|
|
84
91
|
return bid === undefined ? (storedCptBidValue(params) ?? null) : Number(bid);
|
|
85
92
|
}
|
|
86
93
|
return null;
|
|
87
94
|
}
|
|
88
95
|
function isSearchTermRule(operateWith) {
|
|
89
|
-
if (operateWith === SEARCH_TERM)
|
|
96
|
+
if (operateWith === SEARCH_TERM) {
|
|
90
97
|
return true;
|
|
91
|
-
|
|
98
|
+
}
|
|
99
|
+
if (operateWith === TARGETING_KEYWORD) {
|
|
92
100
|
return false;
|
|
101
|
+
}
|
|
93
102
|
const shown = typeof operateWith === 'string' && operateWith.length > 0 ? `"${operateWith}"` : 'missing';
|
|
94
103
|
throw new Error(`${ADD_AS_KEYWORD_ACTION} runs on a "${SEARCH_TERM}" or "${TARGETING_KEYWORD}" rule only; this rule's operate_with is ${shown}.`);
|
|
95
104
|
}
|
|
96
105
|
function rejectForeignFlags(searchTerm, flags) {
|
|
97
|
-
const foreign = (searchTerm ? TARGETING_KEYWORD_FLAGS : SEARCH_TERM_FLAGS).filter(
|
|
98
|
-
if (foreign.length === 0)
|
|
106
|
+
const foreign = (searchTerm ? TARGETING_KEYWORD_FLAGS : SEARCH_TERM_FLAGS).filter(name => isFlagSet(flags, name));
|
|
107
|
+
if (foreign.length === 0) {
|
|
99
108
|
return;
|
|
109
|
+
}
|
|
100
110
|
const applicable = [...SHARED_FLAGS, ...(searchTerm ? SEARCH_TERM_FLAGS : TARGETING_KEYWORD_FLAGS)];
|
|
101
|
-
throw new Error(`${foreign.map(
|
|
102
|
-
`operate_with "${searchTerm ? SEARCH_TERM : TARGETING_KEYWORD}" rule, and the API drops params it cannot `
|
|
103
|
-
`place. Applicable action flags: ${applicable.map(
|
|
111
|
+
throw new Error(`${foreign.map(name => dashed(name)).join(', ')} ${foreign.length === 1 ? 'has' : 'have'} no place in the params of an `
|
|
112
|
+
+ `operate_with "${searchTerm ? SEARCH_TERM : TARGETING_KEYWORD}" rule, and the API drops params it cannot `
|
|
113
|
+
+ `place. Applicable action flags: ${applicable.map(name => dashed(name)).join(', ')}.`);
|
|
104
114
|
}
|
|
105
115
|
function missingFlagsError(missing) {
|
|
106
|
-
const lines = missing.map(
|
|
116
|
+
const lines = missing.map(name => ` ${dashed(name)} — ${MISSING_REASONS[name]}`);
|
|
107
117
|
return new Error(`The ${ADD_AS_KEYWORD_ACTION} params are incomplete. Pass:\n${lines.join('\n')}`);
|
|
108
118
|
}
|
|
109
119
|
export function buildAddKeywordParams(operateWith, stored, flags) {
|
|
@@ -112,23 +122,28 @@ export function buildAddKeywordParams(operateWith, stored, flags) {
|
|
|
112
122
|
const params = asRecord(stored);
|
|
113
123
|
const missing = [];
|
|
114
124
|
const internalIds = flags['target-ad-group'] ?? storedInternalIds(params);
|
|
115
|
-
if (internalIds.length === 0)
|
|
125
|
+
if (internalIds.length === 0) {
|
|
116
126
|
missing.push('target-ad-group');
|
|
127
|
+
}
|
|
117
128
|
const matchType = flags['match-type'] ?? storedMatchType(params);
|
|
118
|
-
if (matchType === undefined)
|
|
129
|
+
if (matchType === undefined) {
|
|
119
130
|
missing.push('match-type');
|
|
131
|
+
}
|
|
120
132
|
const cptBidType = flags['cpt-bid-type'] ?? storedCptBidType(params);
|
|
121
|
-
if (cptBidType === undefined)
|
|
133
|
+
if (cptBidType === undefined) {
|
|
122
134
|
missing.push('cpt-bid-type');
|
|
135
|
+
}
|
|
123
136
|
const bid = flags['cpt-bid'];
|
|
124
137
|
if (bid !== undefined && cptBidType === BID_TYPE_IGNORING_VALUE) {
|
|
125
138
|
throw new Error(`--cpt-bid has no meaning with --cpt-bid-type ${BID_TYPE_IGNORING_VALUE}; that type copies the target ad group's default bid.`);
|
|
126
139
|
}
|
|
127
140
|
const cptBidValue = resolveCptBidValue(cptBidType, bid, params);
|
|
128
|
-
if (cptBidValue === undefined)
|
|
141
|
+
if (cptBidValue === undefined) {
|
|
129
142
|
missing.push('cpt-bid');
|
|
130
|
-
|
|
143
|
+
}
|
|
144
|
+
if (missing.length > 0) {
|
|
131
145
|
throw missingFlagsError(missing);
|
|
146
|
+
}
|
|
132
147
|
const built = {
|
|
133
148
|
cpt_bid: { type: cptBidType, value: cptBidValue ?? null },
|
|
134
149
|
match_type: matchType,
|
|
@@ -136,12 +151,12 @@ export function buildAddKeywordParams(operateWith, stored, flags) {
|
|
|
136
151
|
};
|
|
137
152
|
if (searchTerm) {
|
|
138
153
|
built.negate = resolveNegate(flags, params);
|
|
139
|
-
built.skip_enable_duplicate_keywords
|
|
140
|
-
flags['skip-enable-duplicates'] ?? storedBoolean(params.skip_enable_duplicate_keywords) ?? false;
|
|
154
|
+
built.skip_enable_duplicate_keywords
|
|
155
|
+
= flags['skip-enable-duplicates'] ?? storedBoolean(params.skip_enable_duplicate_keywords) ?? false;
|
|
141
156
|
}
|
|
142
157
|
else {
|
|
143
|
-
built.pause_in_original_ad_group
|
|
144
|
-
flags['pause-original'] ?? storedBoolean(params.pause_in_original_ad_group) ?? false;
|
|
158
|
+
built.pause_in_original_ad_group
|
|
159
|
+
= flags['pause-original'] ?? storedBoolean(params.pause_in_original_ad_group) ?? false;
|
|
145
160
|
}
|
|
146
161
|
return built;
|
|
147
162
|
}
|