mcm-cli 0.45__py3-none-any.whl → 0.47__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mcm-cli
3
- Version: 0.45
3
+ Version: 0.47
4
4
  Summary: A command-line interface for Moloco Commerde Media
5
5
  Home-page: https://github.com/moloco-mcm/mcm-cli
6
6
  Author: Moloco MCM Team
@@ -32,30 +32,43 @@ Please contact the Moloco representative for more details.
32
32
 
33
33
  ## How to install
34
34
 
35
- Run the command from a terminal.
35
+ To install, run:
36
+ ```
37
+ pip install mcm-cli
38
+ ```
39
+
40
+ For installation from the source code, use:
36
41
  ```
37
42
  git clone https://github.com/moloco-mcm/mcm-cli.git && pip install mcm-cli
38
43
  ```
39
44
 
40
45
  ## How to upgrade
41
46
 
42
- Run the command from a terminal.
47
+ To upgrade, run:
48
+ ```
49
+ pip install --upgrade mcm-cli
50
+ ```
51
+
52
+ Or, if installed from the source code:
43
53
  ```
44
54
  git -C mcm-cli pull && pip install mcm-cli
45
55
  ```
46
56
 
47
57
  ## How to uninstall
48
58
 
49
- Run the command from a terminal.
59
+ To uninstall, run:
50
60
  ```
51
61
  pip uninstall mcm-cli
52
62
  ```
53
63
 
54
64
  ## How to use
55
- Run `$ mcm config init` to initialize the configuration. It saves the configuration to `~/.mcm/config.toml`.
56
-
57
- Use `--help` option to learn more of each command.
65
+ Initialize the configuration with:
66
+ ```
67
+ mcm config init
68
+ ```
69
+ This saves the configuration to `~/.mcm/config.toml`.
58
70
 
71
+ For more details on each command, use the `--help` option.
59
72
  ```
60
73
  $ mcm --help
61
74
 
@@ -65,9 +78,10 @@ $ mcm --help
65
78
  │ --help Show this message and exit. │
66
79
  ╰──────────────────────────────────────────────────────────────────╯
67
80
  ╭─ Commands ───────────────────────────────────────────────────────╮
81
+ │ account Ad account management │
68
82
  │ auth Authentication management │
69
83
  │ config Configurations │
70
- │ version
84
+ │ version Show the tool version
71
85
  │ wallet Wallet management │
72
86
  ╰──────────────────────────────────────────────────────────────────╯
73
87
 
@@ -0,0 +1,21 @@
1
+ mcmcli/__init__.py,sha256=-U6lMZ9_99IXAKwnqnYXYr6NcO6TSmG-kxewgvJjU4k,575
2
+ mcmcli/__main__.py,sha256=5pnw_eUdsX1vMa1zaSqYC7VnQG0QDFppq6z89YpkijI,1356
3
+ mcmcli/logging.py,sha256=xjRS5ey1ONx_d34qB1Fetb_SwPysoh2hzNDuNAaYYWQ,1739
4
+ mcmcli/requests.py,sha256=50N_LiWIWr8-3EPs_yR9f4uEQf8rQ68s_FoMYRhgjzI,2343
5
+ mcmcli/command/account.py,sha256=U5kF3UYdTRfKu3RLLY5Nb519s9rEuOiRdLTi5hTtDy4,14672
6
+ mcmcli/command/auth.py,sha256=QLdr_XFW5BVw9r4a7Kjj5BTBXpSux3AWI9eI03S8aiA,2480
7
+ mcmcli/command/config.py,sha256=sdzge-l_Yi2P_TlTgSLqShcGyPCzpW3QJzctpIvc-g4,4195
8
+ mcmcli/command/wallet.py,sha256=TB3WrmniREsDk0ui20p3ha6OZMwA2wAJaxQ9QDGtKnA,8968
9
+ mcmcli/data/account.py,sha256=pe7tPapP6vlUD5D5L5Nh5k2bkWdYOK01Mpt5fBYFnJs,1782
10
+ mcmcli/data/account_user.py,sha256=27nQp52nMma5a3QszSJGqgq5Z0ivIb-nMZcZuhEgbEg,1328
11
+ mcmcli/data/error.py,sha256=d6xa_jTXumlA0EzXy2PJQ86ajBb0Pm90fss9R3LuHUc,1094
12
+ mcmcli/data/seller.py,sha256=40SA7QekM3a3svDrDYLo_QYJ68VUxDO0KeGejJMp4k4,1004
13
+ mcmcli/data/token.py,sha256=11wtyLHCAZHu0LVbNDPa-yipcL6lenxoYIKEI58VzFs,1744
14
+ mcmcli/data/wallet.py,sha256=W-CksF9SPqiv3jZg07Wy8ehVUP5Ot1Gbq2LEGNQCOC8,1906
15
+ mcm_cli-0.47.dist-info/LICENSE,sha256=RFhQPdSOiMTguUX7JSoIuTxA7HVzCbj_p8WU36HjUQQ,10947
16
+ mcm_cli-0.47.dist-info/METADATA,sha256=BDJI_fsakfAl54HLi7OteGj0n_wtKcotesYUPDK72rU,2944
17
+ mcm_cli-0.47.dist-info/NOTICE,sha256=Ldnl2MjRaXPxcldUdbI2NTybq60XAa2LowRhFrRTuiI,76
18
+ mcm_cli-0.47.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
19
+ mcm_cli-0.47.dist-info/entry_points.txt,sha256=qTHAWZ-ejSiU4t11RYwtAU8ScqhQPDeMVTG9y4wMVLg,60
20
+ mcm_cli-0.47.dist-info/top_level.txt,sha256=sh7oqIaqLQlMtKHlxHHgpV2xGMrBMPFWpSp0C6nvJ_Y,7
21
+ mcm_cli-0.47.dist-info/RECORD,,
mcmcli/__main__.py CHANGED
@@ -17,8 +17,7 @@
17
17
  """mcm cli entry point script."""
18
18
  # mcmcli/__main__.py
19
19
 
20
- from typing import Optional
21
-
20
+ import mcmcli.command.account
22
21
  import mcmcli.command.auth
23
22
  import mcmcli.command.config
24
23
  import mcmcli.command.wallet
@@ -32,8 +31,9 @@ def version():
32
31
  """
33
32
  Show the tool version
34
33
  """
35
- typer.echo(f"Version: mcm-cli v0.45")
34
+ typer.echo(f"Version: mcm-cli v0.47")
36
35
 
36
+ app.add_typer(mcmcli.command.account.app, name="account", help="Ad account management")
37
37
  app.add_typer(mcmcli.command.auth.app, name="auth", help="Authentication management")
38
38
  app.add_typer(mcmcli.command.config.app, name="config", help="Configurations")
39
39
  app.add_typer(mcmcli.command.wallet.app, name="wallet", help="Wallet management")
@@ -0,0 +1,461 @@
1
+ # Copyright 2023 Moloco, Inc
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from enum import Enum
16
+ from mcmcli.data.account import Account, AccountListWrapper
17
+ from mcmcli.data.account_user import User, UserWrapper, UserListWrapper
18
+ from mcmcli.data.error import Error
19
+ from mcmcli.data.seller import Seller, SellerListWrapper
20
+ from mcmcli.requests import CurlString, api_request
21
+ from typing import Any, Callable, Dict, Optional, Tuple, TypeVar
22
+
23
+ import csv
24
+ import mcmcli.command.auth
25
+ import mcmcli.command.config
26
+ import mcmcli.logging
27
+ import mcmcli.requests
28
+ import sys
29
+ import time
30
+ import typer
31
+
32
+ MAX_NUM_ITEMS_PER_PAGE = 5000
33
+ T = TypeVar('T')
34
+
35
+ class UserRole(Enum):
36
+ AD_ACCOUNT_OWNER = "AD_ACCOUNT_OWNER"
37
+
38
+ app = typer.Typer(add_completion=False)
39
+
40
+
41
+ @app.command()
42
+ def bulk_invite_ad_account_owners(
43
+ csv_file: str = typer.Option(help="CSV file path that contains the list of ad account id, user email address, and the user name."),
44
+ dry_run: bool = typer.Option(True, help="Perform a dry run without sending emails."),
45
+ skip_csv_header: bool = typer.Option(False, help="Skip the first line of the CSV file."),
46
+ create_account: bool = typer.Option(False, help="Create the ad account if it doesn't exist."),
47
+ create_user: bool = typer.Option(False, help="Create the ad account user if it doesn't exist."),
48
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
49
+ ):
50
+ """
51
+ Send campaign manager invitation emails to ad account owners. Ensure the CSV file has three columns:
52
+ ad account ID, user email address, and user name. Extra columns are ignored.
53
+ """
54
+ csv_data = _read_csv_file(csv_file, skip_csv_header)
55
+ if csv_data is None:
56
+ return
57
+
58
+ a = _create_account_command(profile)
59
+ if a is None:
60
+ return
61
+
62
+ if dry_run:
63
+ print(f"Dry run initiated.")
64
+
65
+ #
66
+ # get the list of sellers
67
+ #
68
+ _, error, seller_dictionary = a.list_sellers()
69
+ if error:
70
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
71
+ return
72
+
73
+
74
+ print('Processing the command ', end='', file=sys.stderr, flush=True)
75
+ total_count = len(csv_data)
76
+ success_count = 0
77
+ account_creation_count = 0
78
+ for row in csv_data:
79
+ account_id = row[0]
80
+ email_address = row[1]
81
+ user_name = row[2]
82
+ print('.', end='', file=sys.stderr, flush=True)
83
+
84
+ # Check if the ad account exists
85
+ if _lookup_dict(account_id, seller_dictionary) is None:
86
+ print(f"\nERROR: Could not find the ad account ID {account_id}", file=sys.stderr, flush=True)
87
+ continue
88
+
89
+ # Create the ad account if the seller doesn't have one yet and the CLI command tells to create the ad account.
90
+ seller_info = seller_dictionary[account_id]
91
+ if not seller_info.is_registered and create_account:
92
+ error, _ = a.create_account_with_retry(seller_info.id, seller_info.title, dry_run)
93
+ if error:
94
+ print(f"\nERROR: Failed to create the ad account {account_id}. {error.message}.", file=sys.stderr, flush=True)
95
+ continue
96
+ account_creation_count += 1
97
+
98
+ error = a.send_invitation_email_with_retry(account_id, email_address, user_name, create_user, dry_run)
99
+ if error:
100
+ print(f"\nERROR: Failed to send the mail for the ad account ID {account_id}. {error.message}.", file=sys.stderr, flush=True)
101
+ continue
102
+
103
+ success_count += 1
104
+
105
+ print(' Done', file=sys.stderr, flush=True)
106
+ if dry_run:
107
+ print(f"If this wasn't a dry-run, it would have sent {success_count} out of {total_count} emails and created {account_creation_count} new ad accounts.", flush=True)
108
+ else:
109
+ print(f'Sent {success_count} out of {total_count} emails, and created {account_creation_count} new ad accounts.', flush=True)
110
+ return
111
+
112
+
113
+ @app.command()
114
+ def bulk_check_user_registrations(
115
+ csv_file: str = typer.Option(help="CSV file path that contains the list of ad account id and user email address."),
116
+ skip_csv_header: bool = typer.Option(False, help="Skip the first line of the CSV file."),
117
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
118
+ ):
119
+ """
120
+ Check user status. Ensure the CSV file has two columns: ad account ID and user email address. Extra columns are ignored.
121
+ """
122
+ csv_data = _read_csv_file(csv_file, skip_csv_header)
123
+ if csv_data is None:
124
+ return
125
+
126
+ a = _create_account_command(profile)
127
+ if a is None:
128
+ return
129
+
130
+ _, error, account_dictionary = a.list_accounts(to_curl=False)
131
+ if error:
132
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
133
+ return
134
+
135
+ print('"Ad Account ID","Is Ad Account Exist","User Email","Is User Exist","User Role","User Status"')
136
+ for row in csv_data:
137
+ account_id = row[0]
138
+ email_address = row[1]
139
+ account = _lookup_dict(account_id, account_dictionary)
140
+ is_account_exist = "No Account Exists" if account is None else "Account Exists"
141
+
142
+ error, user_dictionary = a.list_account_users_with_retry(account_id)
143
+ if error:
144
+ print(f"\nERROR: {error.message}", file=sys.stderr, flush=True)
145
+ return
146
+
147
+ user = _lookup_dict(email_address, user_dictionary)
148
+ is_user_exist = "No User Exist" if user is None else "User Exists"
149
+ user_role = "" if user is None else user.role
150
+ user_status = "" if user is None else user.status
151
+ print(f'"{account_id}","{is_account_exist}","{email_address}","{is_user_exist}","{user_role}","{user_status}"')
152
+ return
153
+
154
+
155
+ class AccountCommand:
156
+ def __init__(
157
+ self,
158
+ profile,
159
+ auth_command: mcmcli.command.auth.AuthCommand,
160
+ token
161
+ ):
162
+ self.config = mcmcli.command.config.get_config(profile)
163
+ if (self.config is None):
164
+ print(f"ERROR: Failed to load the CLI profile", file=sys.stderr, flush=True)
165
+ sys.exit()
166
+
167
+ self.profile = profile
168
+ self.auth_command = auth_command
169
+ self.api_base_url = f"{self.config['management_api_hostname']}/rmp/mgmt/v1/platforms/{self.config['platform_id']}"
170
+ self.headers = {
171
+ "accept": "application/json",
172
+ "content-type": "application/json",
173
+ "Authorization": f"Bearer {token}"
174
+ }
175
+
176
+
177
+ def refresh_token(
178
+ self,
179
+ ) -> None:
180
+ _, error, token = self.auth_command.get_token()
181
+ if error:
182
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
183
+ return
184
+ self.headers["Authorization"] = f"Bearer {token.token}"
185
+
186
+
187
+ def retry_with_token_refresh(
188
+ self,
189
+ operation: Callable[[], Tuple[Optional[Error], Any]],
190
+ max_retries: int = 3,
191
+ delay: int = 1,
192
+ ) -> Tuple[
193
+ Optional[Error],
194
+ Any
195
+ ]:
196
+ retries = 0
197
+ while retries < max_retries:
198
+ error, result = operation()
199
+ if error and error.code == 16:
200
+ print(f"\nERROR: authentication token expired. Retrying...", file=sys.stderr, flush=True)
201
+ self.refresh_token()
202
+ retries += 1
203
+ time.sleep(delay)
204
+ continue
205
+ return error, result
206
+ return Error(code=16, message="Failed to regain an authentication token"), None
207
+
208
+
209
+ def send_invitation_email(
210
+ self,
211
+ account_id: str,
212
+ email_address: str,
213
+ user_name: str,
214
+ create_user: bool,
215
+ ) -> Optional[Error]:
216
+
217
+ error, user_dictionary = self.list_account_users(account_id)
218
+ if error:
219
+ return error
220
+
221
+ user = _lookup_dict(email_address, user_dictionary)
222
+ if user is None:
223
+ if not create_user:
224
+ _msg = f"\nERROR: The ad account {account_id} exists. But we could not find the user with email {email_address}"
225
+ print(_msg, file=sys.stderr, flush=True)
226
+ return Error(code=1, message = _msg)
227
+
228
+ _, error, _ = self.invite_user(account_id, email_address, user_name, to_curl=False)
229
+ return error
230
+
231
+ #
232
+ # The ad account and the user exists. We can just send the password reset email
233
+ #
234
+ _api_url = f"{self.api_base_url}/users/{user.id}/password-reset-tokens"
235
+ _, error, _ = api_request('POST', False, _api_url, self.headers)
236
+ return error
237
+
238
+ def send_invitation_email_with_retry(
239
+ self,
240
+ account_id: str,
241
+ email_address: str,
242
+ user_name: str,
243
+ create_user: bool,
244
+ dry_run: bool,
245
+ ) -> Optional[Error]:
246
+ if dry_run:
247
+ return None
248
+
249
+ error, _ = self.retry_with_token_refresh(
250
+ lambda: (
251
+ self.send_invitation_email(account_id, email_address, user_name, create_user),
252
+ None,
253
+ ),
254
+ )
255
+ return error
256
+
257
+
258
+ def invite_user(
259
+ self,
260
+ account_id,
261
+ user_email,
262
+ user_name,
263
+ role = UserRole.AD_ACCOUNT_OWNER,
264
+ to_curl = True,
265
+ ) -> tuple[
266
+ Optional[CurlString],
267
+ Optional[Error],
268
+ Optional[User]
269
+ ]:
270
+ _api_url = f"{self.api_base_url}/ad-accounts/{account_id}/users"
271
+ _payload = {
272
+ "user": {
273
+ "ad_account_id": account_id,
274
+ "email": user_email,
275
+ "name": user_name,
276
+ "role": role.value,
277
+ }
278
+ }
279
+ curl, error, json_obj = api_request('POST', to_curl, _api_url, self.headers, _payload)
280
+ if curl:
281
+ return curl, None, None
282
+ if error:
283
+ return None, error, None
284
+
285
+ ret = UserWrapper(**json_obj).user
286
+ return None, None, ret
287
+
288
+ def create_account(
289
+ self,
290
+ account_id,
291
+ account_name,
292
+ to_curl
293
+ ) -> tuple[
294
+ Optional[CurlString],
295
+ Optional[Error],
296
+ Optional[Account]
297
+ ]:
298
+ _api_url = f"{self.api_base_url}/ad-accounts"
299
+ _payload = {
300
+ "ad_account": {
301
+ "id": account_id,
302
+ "title": account_name
303
+ }
304
+ }
305
+
306
+ curl, error, json_obj = api_request('POST', to_curl, _api_url, self.headers, _payload)
307
+ if curl:
308
+ return curl, None, None
309
+ if error:
310
+ return None, error, None
311
+
312
+ account_info = Account(**json_obj['ad_account'])
313
+ return None, None, account_info
314
+
315
+
316
+ def create_account_with_retry(
317
+ self,
318
+ account_id: str,
319
+ account_name: str,
320
+ dry_run: bool,
321
+ ) -> tuple[
322
+ Optional[Error],
323
+ Optional[Account],
324
+ ]:
325
+ if dry_run:
326
+ #print(f"\nDRY RUN: create_account({account_id}, {account_name})")
327
+ return None, None
328
+
329
+ retries = 0
330
+ delay = 1
331
+ max_retries = 3
332
+ while retries < max_retries:
333
+ _, error, account = self.create_account(account_id, account_name, to_curl=False)
334
+ if error and error.code == 16:
335
+ print(f"\nERROR: authentication token expired. Retrying...", file=sys.stderr, flush=True)
336
+ self.refresh_token()
337
+ retries += 1
338
+ time.sleep(delay)
339
+ continue
340
+ return error, account
341
+ return Error(code=16, message="Failed to regain an authentication token"), None
342
+
343
+
344
+ def list_accounts(
345
+ self,
346
+ to_curl=False
347
+ ) -> tuple[
348
+ Optional[CurlString],
349
+ Optional[Error],
350
+ Dict[str, Account],
351
+ ]:
352
+ _api_url = f"{self.api_base_url}/ad-accounts"
353
+
354
+ curl, error, json_obj = api_request('GET', to_curl, _api_url, self.headers)
355
+ if curl:
356
+ return curl, None, {}
357
+ if error:
358
+ return None, error, {}
359
+
360
+ account_list = AccountListWrapper(**json_obj).ad_accounts
361
+
362
+ accounts = {}
363
+ for x in account_list:
364
+ accounts[x.id] = x
365
+ return None, None, accounts
366
+
367
+
368
+ def list_sellers(
369
+ self,
370
+ ) -> tuple[
371
+ Optional[CurlString],
372
+ Optional[Error],
373
+ Dict[str, Seller],
374
+ ]:
375
+ _api_url = f"{self.api_base_url}/sellers"
376
+
377
+ curl, error, json_obj = api_request('GET', False, _api_url, self.headers)
378
+ if curl:
379
+ return curl, None, {}
380
+ if error:
381
+ return None, error, {}
382
+
383
+ seller_list = SellerListWrapper(**json_obj).sellers
384
+
385
+ sellers = {}
386
+ for x in seller_list:
387
+ sellers[x.id] = x
388
+ return None, None, sellers
389
+
390
+
391
+ def list_account_users(
392
+ self,
393
+ account_id: str,
394
+ ) -> tuple [
395
+ Optional[Error],
396
+ Dict[str, User],
397
+ ]:
398
+ _api_url = f"{self.api_base_url}/ad-accounts/{account_id}/users"
399
+
400
+ _, error, json_obj = api_request('GET', False, _api_url, self.headers)
401
+ if error:
402
+ return error, {}
403
+ user_list = UserListWrapper(**json_obj).users
404
+
405
+ users = {}
406
+ for x in user_list:
407
+ users[x.email] = x
408
+
409
+ return None, users
410
+
411
+ def list_account_users_with_retry(
412
+ self,
413
+ account_id: str
414
+ ) -> tuple [
415
+ Optional[Error],
416
+ Dict[str, User],
417
+ ]:
418
+ return self.retry_with_token_refresh(
419
+ lambda: self.list_account_users(account_id),
420
+ )
421
+
422
+
423
+
424
+ #
425
+ # Helper functions
426
+ #
427
+ def _create_account_command(
428
+ profile: str
429
+ ) -> Optional[AccountCommand]:
430
+ auth = mcmcli.command.auth.AuthCommand(profile)
431
+ _, error, token = auth.get_token()
432
+ if error:
433
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
434
+ return None
435
+ return AccountCommand(profile, auth, token.token)
436
+
437
+
438
+ def _read_csv_file(
439
+ csv_file: str,
440
+ skip_csv_header: bool,
441
+ ) -> Optional[list[list]]:
442
+ try:
443
+ with open(csv_file, mode='r', newline='') as file:
444
+ csv_reader = csv.reader(file)
445
+ if skip_csv_header:
446
+ next(csv_reader)
447
+ return list(csv_reader)
448
+ except Exception as e:
449
+ print(f"ERROR: {e}", file=sys.stderr, flush=True)
450
+ return None
451
+
452
+
453
+ def _lookup_dict(
454
+ id: str,
455
+ dictionary: Dict[str, T],
456
+ ) -> Optional[T]:
457
+ if id in dictionary:
458
+ return dictionary[id]
459
+ return None
460
+
461
+
mcmcli/data/account.py ADDED
@@ -0,0 +1,59 @@
1
+ # Copyright 2023 Moloco, Inc
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from pydantic import BaseModel
16
+
17
+ #
18
+ # API error response dataclasses
19
+ #
20
+ # list-accounts command's example response is as below:
21
+ #
22
+ # {
23
+ # "ad_accounts": [
24
+ # {
25
+ # "id": "0d139a1b-abcd-11ee-b5f8-12f12be0eb51",
26
+ # "title": "Smoke Distribution Inc.",
27
+ # "timezone": "America/New_York",
28
+ # "state_info": {
29
+ # "ad_account_id": "0d139a1b-abcd-11ee-b5f8-12f12be0eb51",
30
+ # "state": "ACTIVE",
31
+ # "state_case": "INIT_BY_PLATFORM",
32
+ # "state_updated_at": "2023-10-23T12:43:31.542660Z",
33
+ # "updated_at": "2023-10-23T12:43:31.542660Z"
34
+ # },
35
+ # "available_features": [],
36
+ # "created_at": "2023-10-23T12:43:31.542660Z",
37
+ # "updated_at": "2023-10-23T12:43:31.542660Z"
38
+ # }
39
+ # ]
40
+ # }
41
+
42
+ class State(BaseModel):
43
+ ad_account_id: str
44
+ state: str
45
+ state_case: str
46
+ state_updated_at: str
47
+ updated_at: str
48
+
49
+ class Account(BaseModel):
50
+ id: str
51
+ title: str
52
+ timezone: str
53
+ state_info: State
54
+ # available_features: []
55
+ created_at: str
56
+ updated_at: str
57
+
58
+ class AccountListWrapper(BaseModel):
59
+ ad_accounts: list[Account]
@@ -0,0 +1,50 @@
1
+ # Copyright 2023 Moloco, Inc
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from pydantic import BaseModel
16
+
17
+ #
18
+ # Ad account users response example is as below:f
19
+ #
20
+ # {
21
+ # "users": [
22
+ # {
23
+ # "ad_account_id": "1234",
24
+ # "id": "CEdyiC4f1yyyySGEOGj7",
25
+ # "email": "user@example.com",
26
+ # "name": "John Doe",
27
+ # "role": "AD_ACCOUNT_OWNER",
28
+ # "status": "NOT_REGISTERED",
29
+ # "created_at": "2024-06-06T14:29:42Z",
30
+ # "updated_at": "2024-06-06T14:29:42Z"
31
+ # }
32
+ # ]
33
+ # }
34
+
35
+ class User(BaseModel):
36
+ ad_account_id: str
37
+ id: str
38
+ email: str
39
+ name: str
40
+ role: str
41
+ status: str
42
+ created_at: str
43
+ updated_at: str
44
+
45
+ class UserListWrapper(BaseModel):
46
+ users: list[User]
47
+
48
+ class UserWrapper(BaseModel):
49
+ user: User
50
+
mcmcli/data/seller.py ADDED
@@ -0,0 +1,38 @@
1
+ # Copyright 2023 Moloco, Inc
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from pydantic import BaseModel
16
+
17
+ #
18
+ # API error response dataclasses
19
+ #
20
+ # list-accounts command's example response is as below:
21
+ #
22
+ # {
23
+ # "sellers": [
24
+ # {
25
+ # "id": "4450",
26
+ # "title": "1 All Good",
27
+ # "is_registered": false
28
+ # }
29
+ # ]
30
+ # }
31
+
32
+ class Seller(BaseModel):
33
+ id: str
34
+ title: str
35
+ is_registered: bool
36
+
37
+ class SellerListWrapper(BaseModel):
38
+ sellers: list[Seller]
mcmcli/data/wallet.py CHANGED
@@ -24,7 +24,7 @@ from pydantic import BaseModel
24
24
  # {
25
25
  # "id": "TjNwMMlONkaMqNyn",
26
26
  # "title": "",
27
- # "ad_account_id": "side_bar_1-ME",
27
+ # "ad_account_id": "1234",
28
28
  # "type": "PRE_PAYMENT",
29
29
  # "accounts": [
30
30
  # {
@@ -1,17 +0,0 @@
1
- mcmcli/__init__.py,sha256=-U6lMZ9_99IXAKwnqnYXYr6NcO6TSmG-kxewgvJjU4k,575
2
- mcmcli/__main__.py,sha256=Mil8isMhqc3nzQyTDIfC8q3bgmM8jDHaIKtDupyj_xw,1267
3
- mcmcli/logging.py,sha256=xjRS5ey1ONx_d34qB1Fetb_SwPysoh2hzNDuNAaYYWQ,1739
4
- mcmcli/requests.py,sha256=50N_LiWIWr8-3EPs_yR9f4uEQf8rQ68s_FoMYRhgjzI,2343
5
- mcmcli/command/auth.py,sha256=QLdr_XFW5BVw9r4a7Kjj5BTBXpSux3AWI9eI03S8aiA,2480
6
- mcmcli/command/config.py,sha256=sdzge-l_Yi2P_TlTgSLqShcGyPCzpW3QJzctpIvc-g4,4195
7
- mcmcli/command/wallet.py,sha256=TB3WrmniREsDk0ui20p3ha6OZMwA2wAJaxQ9QDGtKnA,8968
8
- mcmcli/data/error.py,sha256=d6xa_jTXumlA0EzXy2PJQ86ajBb0Pm90fss9R3LuHUc,1094
9
- mcmcli/data/token.py,sha256=11wtyLHCAZHu0LVbNDPa-yipcL6lenxoYIKEI58VzFs,1744
10
- mcmcli/data/wallet.py,sha256=HvFJsRsPNRw-n_lwm6kAKlUxQbtnft97QFkHiS3StjY,1915
11
- mcm_cli-0.45.dist-info/LICENSE,sha256=RFhQPdSOiMTguUX7JSoIuTxA7HVzCbj_p8WU36HjUQQ,10947
12
- mcm_cli-0.45.dist-info/METADATA,sha256=4G0mNs2xqIgMeE3GkIoDDNiFxFIrl2hSrrWDFE9zkKA,2755
13
- mcm_cli-0.45.dist-info/NOTICE,sha256=Ldnl2MjRaXPxcldUdbI2NTybq60XAa2LowRhFrRTuiI,76
14
- mcm_cli-0.45.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
15
- mcm_cli-0.45.dist-info/entry_points.txt,sha256=qTHAWZ-ejSiU4t11RYwtAU8ScqhQPDeMVTG9y4wMVLg,60
16
- mcm_cli-0.45.dist-info/top_level.txt,sha256=sh7oqIaqLQlMtKHlxHHgpV2xGMrBMPFWpSp0C6nvJ_Y,7
17
- mcm_cli-0.45.dist-info/RECORD,,
File without changes