adapty 0.3.0-beta.0 → 0.4.0
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 +134 -5
- package/dist/commands/asa/ad-groups/create.d.ts +22 -0
- package/dist/commands/asa/ad-groups/create.js +54 -0
- package/dist/commands/asa/ad-groups/get.d.ts +11 -0
- package/dist/commands/asa/ad-groups/get.js +21 -0
- package/dist/commands/asa/ad-groups/list.d.ts +18 -0
- package/dist/commands/asa/ad-groups/list.js +24 -0
- package/dist/commands/asa/ad-groups/update.d.ts +23 -0
- package/dist/commands/asa/ad-groups/update.js +62 -0
- package/dist/commands/asa/ads/create.d.ts +16 -0
- package/dist/commands/asa/ads/create.js +41 -0
- package/dist/commands/asa/ads/get.d.ts +11 -0
- package/dist/commands/asa/ads/get.js +21 -0
- package/dist/commands/asa/ads/list.d.ts +18 -0
- package/dist/commands/asa/ads/list.js +24 -0
- package/dist/commands/asa/ads/update.d.ts +17 -0
- package/dist/commands/asa/ads/update.js +44 -0
- package/dist/commands/asa/apps/list.d.ts +13 -0
- package/dist/commands/asa/apps/list.js +18 -0
- package/dist/commands/asa/automations/create.d.ts +16 -0
- package/dist/commands/asa/automations/create.js +60 -0
- package/dist/commands/asa/automations/get.d.ts +11 -0
- package/dist/commands/asa/automations/get.js +21 -0
- package/dist/commands/asa/automations/list.d.ts +13 -0
- package/dist/commands/asa/automations/list.js +18 -0
- package/dist/commands/asa/automations/run.d.ts +16 -0
- package/dist/commands/asa/automations/run.js +41 -0
- package/dist/commands/asa/automations/runs.d.ts +16 -0
- package/dist/commands/asa/automations/runs.js +24 -0
- package/dist/commands/asa/automations/update.d.ts +21 -0
- package/dist/commands/asa/automations/update.js +73 -0
- package/dist/commands/asa/campaigns/create.d.ts +25 -0
- package/dist/commands/asa/campaigns/create.js +67 -0
- package/dist/commands/asa/campaigns/get.d.ts +11 -0
- package/dist/commands/asa/campaigns/get.js +21 -0
- package/dist/commands/asa/campaigns/list.d.ts +17 -0
- package/dist/commands/asa/campaigns/list.js +24 -0
- package/dist/commands/asa/campaigns/update.d.ts +23 -0
- package/dist/commands/asa/campaigns/update.js +66 -0
- package/dist/commands/asa/competitors/summary.d.ts +11 -0
- package/dist/commands/asa/competitors/summary.js +56 -0
- package/dist/commands/asa/connect.d.ts +12 -0
- package/dist/commands/asa/connect.js +39 -0
- package/dist/commands/asa/creatives/list.d.ts +15 -0
- package/dist/commands/asa/creatives/list.js +24 -0
- package/dist/commands/asa/keywords/add.d.ts +20 -0
- package/dist/commands/asa/keywords/add.js +73 -0
- package/dist/commands/asa/keywords/list.d.ts +19 -0
- package/dist/commands/asa/keywords/list.js +24 -0
- package/dist/commands/asa/keywords/update.d.ts +21 -0
- package/dist/commands/asa/keywords/update.js +64 -0
- package/dist/commands/asa/metrics/index.d.ts +21 -0
- package/dist/commands/asa/metrics/index.js +68 -0
- package/dist/commands/asa/metrics/overview.d.ts +15 -0
- package/dist/commands/asa/metrics/overview.js +51 -0
- package/dist/commands/asa/negative-keywords/add.d.ts +18 -0
- package/dist/commands/asa/negative-keywords/add.js +70 -0
- package/dist/commands/asa/negative-keywords/list.d.ts +19 -0
- package/dist/commands/asa/negative-keywords/list.js +29 -0
- package/dist/commands/asa/orgs/list.d.ts +13 -0
- package/dist/commands/asa/orgs/list.js +18 -0
- package/dist/commands/asa/product-pages/list.d.ts +15 -0
- package/dist/commands/asa/product-pages/list.js +24 -0
- package/dist/commands/asa/product-pages/sync.d.ts +13 -0
- package/dist/commands/asa/product-pages/sync.js +28 -0
- package/dist/commands/asa/search-terms/list.d.ts +20 -0
- package/dist/commands/asa/search-terms/list.js +25 -0
- package/dist/commands/asa/whoami.d.ts +8 -0
- package/dist/commands/asa/whoami.js +22 -0
- package/dist/lib/api-client.d.ts +15 -3
- package/dist/lib/api-client.js +49 -17
- package/dist/lib/asa-client.d.ts +16 -0
- package/dist/lib/asa-client.js +46 -0
- package/dist/lib/asa-confirm.d.ts +18 -0
- package/dist/lib/asa-confirm.js +41 -0
- package/dist/lib/asa-flags.d.ts +88 -0
- package/dist/lib/asa-flags.js +137 -0
- package/dist/lib/asa-schemas.d.ts +287 -0
- package/dist/lib/asa-schemas.js +1 -0
- package/dist/lib/errors.d.ts +25 -2
- package/dist/lib/errors.js +48 -7
- package/dist/lib/output.js +45 -15
- package/oclif.manifest.json +2940 -295
- package/package.json +40 -1
package/README.md
CHANGED
|
@@ -86,6 +86,131 @@ adapty access-levels create --app UUID [flags]
|
|
|
86
86
|
adapty access-levels update --app UUID ACCESS_LEVEL_ID [flags]
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
### Apple Search Ads
|
|
90
|
+
|
|
91
|
+
Apple Search Ads commands live under `adapty asa` and talk to the ASA service rather than the Developer
|
|
92
|
+
API. They take **no `--app`**: the scope is the company behind your token. A connected Apple Ads account and
|
|
93
|
+
an active Ads Manager subscription are required — `adapty asa whoami` tells you where you stand.
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
adapty asa whoami # company, how access was granted, Apple connection state
|
|
97
|
+
adapty asa connect [--no-wait] # link an Apple Search Ads account
|
|
98
|
+
adapty asa apps list # apps promoted in Apple Search Ads
|
|
99
|
+
adapty asa orgs list # Apple Search Ads organizations
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Every list takes scope filters, and they narrow the query rather than the printed page — `asa keywords list`
|
|
103
|
+
unfiltered pages through the whole account, while one ad group is a handful of rows, so scope the read:
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
adapty asa campaigns list --app APP_UUID --status PAUSED
|
|
107
|
+
adapty asa ad-groups list --campaign CAMPAIGN_UUID
|
|
108
|
+
adapty asa keywords list --ad-group AD_GROUP_UUID --status ACTIVE
|
|
109
|
+
adapty asa keywords list --ad-group AD_GROUP_UUID --ad-group OTHER_UUID # repeatable
|
|
110
|
+
adapty asa creatives list --app APP_UUID
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`--campaign-group`, `--app`, `--campaign`, `--ad-group` are repeatable and take the UUIDs printed by the
|
|
114
|
+
matching list command; `--search` matches names case-insensitively. Each list accepts only the filters that
|
|
115
|
+
make sense for it: `--ad-group` starts at keywords, negative keywords, search terms and ads, `--status` is
|
|
116
|
+
`ENABLED`/`PAUSED` everywhere except keywords, which are `ACTIVE`/`PAUSED`, and `asa ads list` has no `--app`
|
|
117
|
+
because ads hang off ad groups. An id belonging to another company simply matches nothing.
|
|
118
|
+
|
|
119
|
+
Campaign structure. These lists return metadata only — numbers come from `asa metrics`, and only
|
|
120
|
+
`asa search-terms list` takes `--date-from` / `--date-to` (default: today):
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
adapty asa campaigns list
|
|
124
|
+
adapty asa campaigns get CAMPAIGN_ID
|
|
125
|
+
adapty asa campaigns create --org UUID --name "Winter push" --adam-id 123456 --country US --daily-budget 50
|
|
126
|
+
adapty asa campaigns update CAMPAIGN_ID [--status PAUSED] [--daily-budget 80] [--country US]
|
|
127
|
+
|
|
128
|
+
adapty asa ad-groups list
|
|
129
|
+
adapty asa ad-groups get AD_GROUP_ID
|
|
130
|
+
adapty asa ad-groups create --campaign UUID --name "Brand terms" --default-bid 1.20
|
|
131
|
+
adapty asa ad-groups update AD_GROUP_ID [--default-bid 1.50] [--status PAUSED]
|
|
132
|
+
|
|
133
|
+
adapty asa ads list
|
|
134
|
+
adapty asa ads get AD_ID
|
|
135
|
+
adapty asa ads create --ad-group UUID --creative-id 4321 --name "Summer ad"
|
|
136
|
+
adapty asa ads update AD_ID [--name "..."] [--status PAUSED]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Keywords are always applied as a batch, at most 100 per call, and a partial rejection is reported per item:
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
adapty asa keywords list
|
|
143
|
+
adapty asa keywords add --ad-group UUID --text "running shoes" --text "trail shoes" [--bid 1.20] [--match-type EXACT]
|
|
144
|
+
adapty asa keywords add --ad-group UUID --from-file keywords.txt
|
|
145
|
+
adapty asa keywords update KEYWORD_ID [KEYWORD_ID...] [--bid 2.00] [--status PAUSED]
|
|
146
|
+
|
|
147
|
+
adapty asa negative-keywords list
|
|
148
|
+
adapty asa negative-keywords add --ad-group UUID --text free
|
|
149
|
+
adapty asa negative-keywords add --campaign UUID [--all-ad-groups] --text free
|
|
150
|
+
|
|
151
|
+
adapty asa search-terms list [--date-from ... --date-to ...]
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Product pages and rule-based automations:
|
|
155
|
+
|
|
156
|
+
```sh
|
|
157
|
+
adapty asa product-pages list
|
|
158
|
+
adapty asa product-pages sync [--adam-id 123456]
|
|
159
|
+
|
|
160
|
+
adapty asa automations list
|
|
161
|
+
adapty asa automations get AUTOMATION_ID
|
|
162
|
+
adapty asa automations create --file rule.json [--run-now]
|
|
163
|
+
adapty asa automations update AUTOMATION_ID [--stop] [--start] [--name "..."] [--file rule.json]
|
|
164
|
+
adapty asa automations run AUTOMATION_ID [--dry-run]
|
|
165
|
+
adapty asa automations runs AUTOMATION_ID
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Metrics take an entity level, a period and an optional metric selection. Rows come back one per entity,
|
|
169
|
+
aggregated and sorted server-side, so a top-N or a breakdown is a single call — use `--order-by` with a small
|
|
170
|
+
`--page-size` for rankings, `metrics overview` for account totals and time series, and one big page (up to
|
|
171
|
+
1000 rows) when you genuinely need every row; never sum pages client-side:
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
adapty asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31
|
|
175
|
+
adapty asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --order-by spend --page-size 5
|
|
176
|
+
adapty asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --group-by country --page-size 1000
|
|
177
|
+
adapty asa metrics --entity keyword --date-from 2026-07-01 --date-to 2026-07-31 --metric spend --metric roas
|
|
178
|
+
adapty asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --metric roas --by-days 7 --by-days 90
|
|
179
|
+
adapty asa metrics overview --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 [--period-unit week]
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
There is no `ltv` metric: lifetime value is a cohort metric read at a renewal window, so `--by-days` is how you
|
|
183
|
+
ask for day-7 or day-90 values — on either route, up to 16 windows per call. `--order-by-day` ranks the rows by
|
|
184
|
+
one of those windows, which is how you get the top campaigns by day-90 ROAS in a single call.
|
|
185
|
+
|
|
186
|
+
Competitor summary takes 1–5 Apple App Store IDs and covers the last full month across every country — there
|
|
187
|
+
are no period or country flags on purpose. The first call on a cold cache can take tens of seconds:
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
adapty asa competitors summary --app-ids 111111111,2222222
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Writes go straight to Apple and take seconds, so every writing command first prints the exact request body and
|
|
194
|
+
asks for a yes. `--yes` skips the question for scripts; in a pipe or under `--json` the command refuses instead
|
|
195
|
+
of waiting for input that will never come. There is no undo — the CLI has no delete.
|
|
196
|
+
|
|
197
|
+
Every write also carries an idempotency key. The CLI generates one per invocation and retries once on a
|
|
198
|
+
network error, so a request that died on the wire is never applied twice. Pass `--idempotency-key` to pin the
|
|
199
|
+
key yourself: re-running a script with the same key within 24 hours replays the stored result — the CLI prints
|
|
200
|
+
"Already applied earlier — showing the stored result." — instead of creating a second entity. The same key
|
|
201
|
+
with a different body is rejected (`422 cli_idempotency_key_reuse`), and a concurrent duplicate answers
|
|
202
|
+
`409 cli_idempotency_in_progress`.
|
|
203
|
+
|
|
204
|
+
Analytics is rate limited per company: the metrics routes get 5 calls a minute (at most 2 in any 10 seconds)
|
|
205
|
+
and share a pool of two concurrent queries with the search-terms list — a busy pool answers
|
|
206
|
+
`429 cli_analytics_busy`, an exhausted window `429 cli_rate_limit_exceeded`, both with the exact wait in
|
|
207
|
+
`Retry-After`. The CLI absorbs a single 429 on its own — it waits the announced `Retry-After` (up to 60
|
|
208
|
+
seconds; cool-downs are never waited out) and retries once — so a 429 that reaches you means the retry failed
|
|
209
|
+
too. A burst of 429s puts the token into an escalating cool-down (`cli_cooldown_active`, 5 minutes →
|
|
210
|
+
30 minutes → 3 hours); retries during the pause don't extend it, but the cure is fixing the failing request,
|
|
211
|
+
not waiting out the pause in a loop. An automation run is queued rather than awaited: `run` prints a run ID
|
|
212
|
+
and the outcome shows up in `adapty asa automations runs`.
|
|
213
|
+
|
|
89
214
|
### Global Flags
|
|
90
215
|
|
|
91
216
|
| Flag | Description |
|
|
@@ -93,14 +218,18 @@ adapty access-levels update --app UUID ACCESS_LEVEL_ID [flags]
|
|
|
93
218
|
| `--json` | Output as JSON |
|
|
94
219
|
| `--help` | Show help |
|
|
95
220
|
| `--page` | Page number (default: 1) |
|
|
96
|
-
| `--page-size` | Items per page (default: 20, max: 100) |
|
|
221
|
+
| `--page-size` | Items per page (default: 20, max: 100; `asa` commands: default 100, max 1000) |
|
|
97
222
|
|
|
98
223
|
## Environment Variables
|
|
99
224
|
|
|
100
|
-
| Variable
|
|
101
|
-
|
|
|
102
|
-
| `ADAPTY_TOKEN`
|
|
103
|
-
| `ADAPTY_API_URL`
|
|
225
|
+
| Variable | Description |
|
|
226
|
+
| -------------------- | --------------------------------------------------------------------------------------- |
|
|
227
|
+
| `ADAPTY_TOKEN` | Override stored auth token |
|
|
228
|
+
| `ADAPTY_API_URL` | Override Developer API base URL (default: `https://api-admin.adapty.io/api/v1/developer`) |
|
|
229
|
+
| `ADAPTY_ASA_API_URL` | Override Apple Search Ads base URL (default: `https://api-asa-admin.adapty.io/api/v1/cli`) |
|
|
230
|
+
|
|
231
|
+
The two API URLs are independent: pointing `ADAPTY_API_URL` at a staging host leaves `adapty asa` on the ASA
|
|
232
|
+
default, and the other way round.
|
|
104
233
|
|
|
105
234
|
## Claude Code Skill
|
|
106
235
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { AsaAdGroupMutationDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
+
export default class AsaAdGroupsCreate extends Command {
|
|
4
|
+
static description: string;
|
|
5
|
+
static enableJsonFlag: boolean;
|
|
6
|
+
static examples: string[];
|
|
7
|
+
static flags: {
|
|
8
|
+
'automated-keywords': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
campaign: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'cpa-goal': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'default-bid': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
'pricing-model': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
'end-time': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
'start-time': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
currency: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
};
|
|
21
|
+
run(): Promise<AsaAdGroupMutationDTO>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
|
|
3
|
+
import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
|
|
4
|
+
import { currencyFlag, idempotencyFlags, money, moneyFlag, pricingModelFlag, scheduleFlags, startOfDayUtc, todayUtc, } from '../../../lib/asa-flags.js';
|
|
5
|
+
import { isValidUuid } from '../../../lib/flags.js';
|
|
6
|
+
import { printResponse } from '../../../lib/output.js';
|
|
7
|
+
export default class AsaAdGroupsCreate extends Command {
|
|
8
|
+
static description = 'Create an ad group inside a campaign';
|
|
9
|
+
static enableJsonFlag = true;
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> asa ad-groups create --campaign UUID --name "Brand terms" --default-bid 1.20',
|
|
12
|
+
'<%= config.bin %> asa ad-groups create --campaign UUID --name "Brand terms" --default-bid 1.20 --start-time 2026-09-01 --pricing-model CPM',
|
|
13
|
+
];
|
|
14
|
+
static flags = {
|
|
15
|
+
...currencyFlag,
|
|
16
|
+
...scheduleFlags,
|
|
17
|
+
...pricingModelFlag,
|
|
18
|
+
...confirmFlags,
|
|
19
|
+
...idempotencyFlags,
|
|
20
|
+
'automated-keywords': Flags.boolean({ allowNo: true, description: 'Let Apple add keywords automatically' }),
|
|
21
|
+
campaign: Flags.string({ description: 'Campaign ID (UUID)', required: true }),
|
|
22
|
+
'cpa-goal': moneyFlag('CPA goal'),
|
|
23
|
+
'default-bid': moneyFlag('Default bid', { required: true }),
|
|
24
|
+
name: Flags.string({ description: 'Ad group name', required: true }),
|
|
25
|
+
status: Flags.string({ description: 'Initial status', options: ['ENABLED', 'PAUSED'] }),
|
|
26
|
+
};
|
|
27
|
+
async run() {
|
|
28
|
+
const { flags } = await this.parse(AsaAdGroupsCreate);
|
|
29
|
+
if (!isValidUuid(flags.campaign))
|
|
30
|
+
this.error('Invalid campaign ID format.', { exit: 2 });
|
|
31
|
+
const body = {
|
|
32
|
+
automated_keywords_opt_in: flags['automated-keywords'],
|
|
33
|
+
campaign_id: flags.campaign,
|
|
34
|
+
cpa_goal: money(flags['cpa-goal'], flags.currency),
|
|
35
|
+
default_bid_amount: money(flags['default-bid'], flags.currency),
|
|
36
|
+
end_time: startOfDayUtc(flags['end-time']),
|
|
37
|
+
name: flags.name,
|
|
38
|
+
pricing_model: flags['pricing-model'],
|
|
39
|
+
start_time: startOfDayUtc(flags['start-time'] ?? todayUtc()),
|
|
40
|
+
status: flags.status,
|
|
41
|
+
};
|
|
42
|
+
await confirmMutation(this, { body, method: 'POST', path: '/ad-groups/', summary: `Create ad group ${flags.name}` }, flags.yes);
|
|
43
|
+
const client = await createAsaClient(this.config);
|
|
44
|
+
const { replayed, result } = await asaWrite(client, 'post', '/ad-groups', {
|
|
45
|
+
body,
|
|
46
|
+
idempotencyKey: flags['idempotency-key'],
|
|
47
|
+
});
|
|
48
|
+
noteReplay(replayed, this.log.bind(this));
|
|
49
|
+
if (result.ad_group && !replayed)
|
|
50
|
+
this.log('Ad group created!');
|
|
51
|
+
printResponse(result, this.log.bind(this));
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { AsaAdGroupDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
+
export default class AsaAdGroupsGet extends Command {
|
|
4
|
+
static args: {
|
|
5
|
+
ad_group_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static description: string;
|
|
8
|
+
static enableJsonFlag: boolean;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
run(): Promise<AsaAdGroupDTO>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
|
+
import { createAsaClient } from '../../../lib/asa-client.js';
|
|
3
|
+
import { isValidUuid } from '../../../lib/flags.js';
|
|
4
|
+
import { printResponse } from '../../../lib/output.js';
|
|
5
|
+
export default class AsaAdGroupsGet extends Command {
|
|
6
|
+
static args = {
|
|
7
|
+
ad_group_id: Args.string({ description: 'Ad group ID (UUID)', required: true }),
|
|
8
|
+
};
|
|
9
|
+
static description = 'Show one ad group; read numbers with asa metrics';
|
|
10
|
+
static enableJsonFlag = true;
|
|
11
|
+
static examples = ['<%= config.bin %> asa ad-groups get 550e8400-e29b-41d4-a716-446655440000'];
|
|
12
|
+
async run() {
|
|
13
|
+
const { args } = await this.parse(AsaAdGroupsGet);
|
|
14
|
+
if (!isValidUuid(args.ad_group_id))
|
|
15
|
+
this.error('Invalid ad group ID format.', { exit: 2 });
|
|
16
|
+
const client = await createAsaClient(this.config);
|
|
17
|
+
const result = await client.get(`/ad-groups/${args.ad_group_id}`);
|
|
18
|
+
printResponse(result, this.log.bind(this));
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { AsaAdGroupDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
+
import { type PaginatedResponse } from '../../../lib/flags.js';
|
|
4
|
+
export default class AsaAdGroupsList extends Command {
|
|
5
|
+
static description: string;
|
|
6
|
+
static enableJsonFlag: boolean;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
campaign: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
app: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
'campaign-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<PaginatedResponse<AsaAdGroupDTO>>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { createAsaClient } from '../../../lib/asa-client.js';
|
|
3
|
+
import { asaPaginationFlags, campaignScopeFlags, scopeParams, statusFilter } from '../../../lib/asa-flags.js';
|
|
4
|
+
import { paginationParams } from '../../../lib/flags.js';
|
|
5
|
+
import { printList } from '../../../lib/output.js';
|
|
6
|
+
export default class AsaAdGroupsList extends Command {
|
|
7
|
+
static description = 'List ad groups; read numbers with asa metrics';
|
|
8
|
+
static enableJsonFlag = true;
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> asa ad-groups list',
|
|
11
|
+
'<%= config.bin %> asa ad-groups list --campaign CAMPAIGN_UUID',
|
|
12
|
+
];
|
|
13
|
+
static flags = { ...asaPaginationFlags, ...campaignScopeFlags, ...statusFilter(['ENABLED', 'PAUSED']) };
|
|
14
|
+
async run() {
|
|
15
|
+
const { flags } = await this.parse(AsaAdGroupsList);
|
|
16
|
+
const client = await createAsaClient(this.config);
|
|
17
|
+
const result = await client.get('/ad-groups', {
|
|
18
|
+
...paginationParams(flags),
|
|
19
|
+
...scopeParams(flags),
|
|
20
|
+
});
|
|
21
|
+
printList(result.data, this.log.bind(this), result.meta.pagination);
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { AsaAdGroupMutationDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
+
export default class AsaAdGroupsUpdate extends Command {
|
|
4
|
+
static args: {
|
|
5
|
+
ad_group_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static description: string;
|
|
8
|
+
static enableJsonFlag: boolean;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
static flags: {
|
|
11
|
+
'automated-keywords': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
'cpa-goal': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
'default-bid': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
|
+
'end-time': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
'start-time': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
currency: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
};
|
|
22
|
+
run(): Promise<AsaAdGroupMutationDTO>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
|
|
3
|
+
import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
|
|
4
|
+
import { currencyFlag, idempotencyFlags, money, moneyFlag, scheduleFlags, startOfDayUtc } from '../../../lib/asa-flags.js';
|
|
5
|
+
import { isValidUuid } from '../../../lib/flags.js';
|
|
6
|
+
import { printResponse } from '../../../lib/output.js';
|
|
7
|
+
export default class AsaAdGroupsUpdate extends Command {
|
|
8
|
+
static args = {
|
|
9
|
+
ad_group_id: Args.string({ description: 'Ad group ID (UUID)', required: true }),
|
|
10
|
+
};
|
|
11
|
+
static description = 'Change an ad group: bid, CPA goal, status or schedule';
|
|
12
|
+
static enableJsonFlag = true;
|
|
13
|
+
static examples = [
|
|
14
|
+
'<%= config.bin %> asa ad-groups update UUID --default-bid 1.50',
|
|
15
|
+
'<%= config.bin %> asa ad-groups update UUID --status PAUSED',
|
|
16
|
+
];
|
|
17
|
+
static flags = {
|
|
18
|
+
...currencyFlag,
|
|
19
|
+
...scheduleFlags,
|
|
20
|
+
...confirmFlags,
|
|
21
|
+
...idempotencyFlags,
|
|
22
|
+
'automated-keywords': Flags.boolean({ allowNo: true, description: 'Let Apple add keywords automatically' }),
|
|
23
|
+
'cpa-goal': moneyFlag('CPA goal'),
|
|
24
|
+
'default-bid': moneyFlag('Default bid'),
|
|
25
|
+
name: Flags.string({ description: 'Ad group name' }),
|
|
26
|
+
status: Flags.string({ description: 'Ad group status', options: ['ENABLED', 'PAUSED'] }),
|
|
27
|
+
};
|
|
28
|
+
async run() {
|
|
29
|
+
const { args, flags } = await this.parse(AsaAdGroupsUpdate);
|
|
30
|
+
if (!isValidUuid(args.ad_group_id))
|
|
31
|
+
this.error('Invalid ad group ID format.', { exit: 2 });
|
|
32
|
+
const body = {};
|
|
33
|
+
if (flags.name !== undefined)
|
|
34
|
+
body.name = flags.name;
|
|
35
|
+
if (flags.status !== undefined)
|
|
36
|
+
body.status = flags.status;
|
|
37
|
+
if (flags['default-bid'] !== undefined)
|
|
38
|
+
body.default_bid_amount = money(flags['default-bid'], flags.currency);
|
|
39
|
+
if (flags['cpa-goal'] !== undefined)
|
|
40
|
+
body.cpa_goal = money(flags['cpa-goal'], flags.currency);
|
|
41
|
+
if (flags['automated-keywords'] !== undefined)
|
|
42
|
+
body.automated_keywords_opt_in = flags['automated-keywords'];
|
|
43
|
+
if (flags['start-time'] !== undefined)
|
|
44
|
+
body.start_time = startOfDayUtc(flags['start-time']);
|
|
45
|
+
if (flags['end-time'] !== undefined)
|
|
46
|
+
body.end_time = startOfDayUtc(flags['end-time']);
|
|
47
|
+
if (Object.keys(body).length === 0) {
|
|
48
|
+
this.error('Nothing to change. Pass at least one field, e.g. --status PAUSED.', { exit: 2 });
|
|
49
|
+
}
|
|
50
|
+
await confirmMutation(this, { body, method: 'PUT', path: `/ad-groups/${args.ad_group_id}/`, summary: 'Update ad group' }, flags.yes);
|
|
51
|
+
const client = await createAsaClient(this.config);
|
|
52
|
+
const { replayed, result } = await asaWrite(client, 'put', `/ad-groups/${args.ad_group_id}`, {
|
|
53
|
+
body,
|
|
54
|
+
idempotencyKey: flags['idempotency-key'],
|
|
55
|
+
});
|
|
56
|
+
noteReplay(replayed, this.log.bind(this));
|
|
57
|
+
if (result.ad_group && !replayed)
|
|
58
|
+
this.log('Ad group updated!');
|
|
59
|
+
printResponse(result, this.log.bind(this));
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { AsaAdMutationDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
+
export default class AsaAdsCreate extends Command {
|
|
4
|
+
static description: string;
|
|
5
|
+
static enableJsonFlag: boolean;
|
|
6
|
+
static examples: string[];
|
|
7
|
+
static flags: {
|
|
8
|
+
'ad-group': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
'creative-id': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<AsaAdMutationDTO>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
|
|
3
|
+
import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
|
|
4
|
+
import { idempotencyFlags } from '../../../lib/asa-flags.js';
|
|
5
|
+
import { isValidUuid } from '../../../lib/flags.js';
|
|
6
|
+
import { printResponse } from '../../../lib/output.js';
|
|
7
|
+
export default class AsaAdsCreate extends Command {
|
|
8
|
+
static description = 'Create an ad from a creative in an ad group';
|
|
9
|
+
static enableJsonFlag = true;
|
|
10
|
+
static examples = ['<%= config.bin %> asa ads create --ad-group UUID --creative-id 4321 --name "Summer ad"'];
|
|
11
|
+
static flags = {
|
|
12
|
+
...confirmFlags,
|
|
13
|
+
...idempotencyFlags,
|
|
14
|
+
'ad-group': Flags.string({ description: 'Ad group ID (UUID) — the campaign is resolved from it', required: true }),
|
|
15
|
+
'creative-id': Flags.integer({ description: 'Apple creative ID from a product page or the default set', required: true }),
|
|
16
|
+
name: Flags.string({ description: 'Ad name', required: true }),
|
|
17
|
+
status: Flags.string({ description: 'Initial status', options: ['ENABLED', 'PAUSED'] }),
|
|
18
|
+
};
|
|
19
|
+
async run() {
|
|
20
|
+
const { flags } = await this.parse(AsaAdsCreate);
|
|
21
|
+
if (!isValidUuid(flags['ad-group']))
|
|
22
|
+
this.error('Invalid ad group ID format.', { exit: 2 });
|
|
23
|
+
const body = {
|
|
24
|
+
ad_group_id: flags['ad-group'],
|
|
25
|
+
creative_id: flags['creative-id'],
|
|
26
|
+
name: flags.name,
|
|
27
|
+
...(flags.status === undefined ? {} : { status: flags.status }),
|
|
28
|
+
};
|
|
29
|
+
await confirmMutation(this, { body, method: 'POST', path: '/ads/', summary: `Create ad ${flags.name}` }, flags.yes);
|
|
30
|
+
const client = await createAsaClient(this.config);
|
|
31
|
+
const { replayed, result } = await asaWrite(client, 'post', '/ads', {
|
|
32
|
+
body,
|
|
33
|
+
idempotencyKey: flags['idempotency-key'],
|
|
34
|
+
});
|
|
35
|
+
noteReplay(replayed, this.log.bind(this));
|
|
36
|
+
if (result.ad && !replayed)
|
|
37
|
+
this.log('Ad created!');
|
|
38
|
+
printResponse(result, this.log.bind(this));
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { AsaAdDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
+
export default class AsaAdsGet extends Command {
|
|
4
|
+
static args: {
|
|
5
|
+
ad_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static description: string;
|
|
8
|
+
static enableJsonFlag: boolean;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
run(): Promise<AsaAdDTO>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
|
+
import { createAsaClient } from '../../../lib/asa-client.js';
|
|
3
|
+
import { isValidUuid } from '../../../lib/flags.js';
|
|
4
|
+
import { printResponse } from '../../../lib/output.js';
|
|
5
|
+
export default class AsaAdsGet extends Command {
|
|
6
|
+
static args = {
|
|
7
|
+
ad_id: Args.string({ description: 'Ad ID (UUID)', required: true }),
|
|
8
|
+
};
|
|
9
|
+
static description = 'Show one ad and its serving state';
|
|
10
|
+
static enableJsonFlag = true;
|
|
11
|
+
static examples = ['<%= config.bin %> asa ads get 550e8400-e29b-41d4-a716-446655440000'];
|
|
12
|
+
async run() {
|
|
13
|
+
const { args } = await this.parse(AsaAdsGet);
|
|
14
|
+
if (!isValidUuid(args.ad_id))
|
|
15
|
+
this.error('Invalid ad ID format.', { exit: 2 });
|
|
16
|
+
const client = await createAsaClient(this.config);
|
|
17
|
+
const result = await client.get(`/ads/${args.ad_id}`);
|
|
18
|
+
printResponse(result, this.log.bind(this));
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { AsaAdDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
+
import { type PaginatedResponse } from '../../../lib/flags.js';
|
|
4
|
+
export default class AsaAdsList extends Command {
|
|
5
|
+
static description: string;
|
|
6
|
+
static enableJsonFlag: boolean;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'ad-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
campaign: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
'campaign-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<PaginatedResponse<AsaAdDTO>>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { createAsaClient } from '../../../lib/asa-client.js';
|
|
3
|
+
import { adScopeFlags, asaPaginationFlags, scopeParams, statusFilter } from '../../../lib/asa-flags.js';
|
|
4
|
+
import { paginationParams } from '../../../lib/flags.js';
|
|
5
|
+
import { printList } from '../../../lib/output.js';
|
|
6
|
+
export default class AsaAdsList extends Command {
|
|
7
|
+
static description = 'List ads; serving_state_reasons explains why an enabled ad is not running';
|
|
8
|
+
static enableJsonFlag = true;
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> asa ads list',
|
|
11
|
+
'<%= config.bin %> asa ads list --ad-group AD_GROUP_UUID --status ENABLED',
|
|
12
|
+
];
|
|
13
|
+
static flags = { ...asaPaginationFlags, ...adScopeFlags, ...statusFilter(['ENABLED', 'PAUSED']) };
|
|
14
|
+
async run() {
|
|
15
|
+
const { flags } = await this.parse(AsaAdsList);
|
|
16
|
+
const client = await createAsaClient(this.config);
|
|
17
|
+
const result = await client.get('/ads', {
|
|
18
|
+
...paginationParams(flags),
|
|
19
|
+
...scopeParams(flags),
|
|
20
|
+
});
|
|
21
|
+
printList(result.data, this.log.bind(this), result.meta.pagination);
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { AsaAdMutationDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
+
export default class AsaAdsUpdate extends Command {
|
|
4
|
+
static args: {
|
|
5
|
+
ad_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static description: string;
|
|
8
|
+
static enableJsonFlag: boolean;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
static flags: {
|
|
11
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<AsaAdMutationDTO>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
|
|
3
|
+
import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
|
|
4
|
+
import { idempotencyFlags } from '../../../lib/asa-flags.js';
|
|
5
|
+
import { isValidUuid } from '../../../lib/flags.js';
|
|
6
|
+
import { printResponse } from '../../../lib/output.js';
|
|
7
|
+
export default class AsaAdsUpdate extends Command {
|
|
8
|
+
static args = {
|
|
9
|
+
ad_id: Args.string({ description: 'Ad ID (UUID)', required: true }),
|
|
10
|
+
};
|
|
11
|
+
static description = 'Rename an ad or pause it; the creative and ad group are fixed at creation';
|
|
12
|
+
static enableJsonFlag = true;
|
|
13
|
+
static examples = ['<%= config.bin %> asa ads update UUID --status PAUSED'];
|
|
14
|
+
static flags = {
|
|
15
|
+
...confirmFlags,
|
|
16
|
+
...idempotencyFlags,
|
|
17
|
+
name: Flags.string({ description: 'Ad name' }),
|
|
18
|
+
status: Flags.string({ description: 'Ad status', options: ['ENABLED', 'PAUSED'] }),
|
|
19
|
+
};
|
|
20
|
+
async run() {
|
|
21
|
+
const { args, flags } = await this.parse(AsaAdsUpdate);
|
|
22
|
+
if (!isValidUuid(args.ad_id))
|
|
23
|
+
this.error('Invalid ad ID format.', { exit: 2 });
|
|
24
|
+
const body = {};
|
|
25
|
+
if (flags.name !== undefined)
|
|
26
|
+
body.name = flags.name;
|
|
27
|
+
if (flags.status !== undefined)
|
|
28
|
+
body.status = flags.status;
|
|
29
|
+
if (Object.keys(body).length === 0) {
|
|
30
|
+
this.error('Nothing to change. Pass --name or --status.', { exit: 2 });
|
|
31
|
+
}
|
|
32
|
+
await confirmMutation(this, { body, method: 'PUT', path: `/ads/${args.ad_id}/`, summary: 'Update ad' }, flags.yes);
|
|
33
|
+
const client = await createAsaClient(this.config);
|
|
34
|
+
const { replayed, result } = await asaWrite(client, 'put', `/ads/${args.ad_id}`, {
|
|
35
|
+
body,
|
|
36
|
+
idempotencyKey: flags['idempotency-key'],
|
|
37
|
+
});
|
|
38
|
+
noteReplay(replayed, this.log.bind(this));
|
|
39
|
+
if (result.ad && !replayed)
|
|
40
|
+
this.log('Ad updated!');
|
|
41
|
+
printResponse(result, this.log.bind(this));
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { AsaAppDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
+
import { type PaginatedResponse } from '../../../lib/flags.js';
|
|
4
|
+
export default class AsaAppsList extends Command {
|
|
5
|
+
static description: string;
|
|
6
|
+
static enableJsonFlag: boolean;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<PaginatedResponse<AsaAppDTO>>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { createAsaClient } from '../../../lib/asa-client.js';
|
|
3
|
+
import { asaPaginationFlags } from '../../../lib/asa-flags.js';
|
|
4
|
+
import { paginationParams } from '../../../lib/flags.js';
|
|
5
|
+
import { printList } from '../../../lib/output.js';
|
|
6
|
+
export default class AsaAppsList extends Command {
|
|
7
|
+
static description = 'List the apps promoted by this company in Apple Search Ads';
|
|
8
|
+
static enableJsonFlag = true;
|
|
9
|
+
static examples = ['<%= config.bin %> asa apps list', '<%= config.bin %> asa apps list --page 2 --page-size 50'];
|
|
10
|
+
static flags = { ...asaPaginationFlags };
|
|
11
|
+
async run() {
|
|
12
|
+
const { flags } = await this.parse(AsaAppsList);
|
|
13
|
+
const client = await createAsaClient(this.config);
|
|
14
|
+
const result = await client.get('/apps', paginationParams(flags));
|
|
15
|
+
printList(result.data, this.log.bind(this), result.meta.pagination);
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
}
|