direct-cli 0.4.1__tar.gz → 0.4.2__tar.gz
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.
- {direct_cli-0.4.1 → direct_cli-0.4.2}/CHANGELOG.md +43 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/CLAUDE.md +3 -3
- {direct_cli-0.4.1 → direct_cli-0.4.2}/PKG-INFO +30 -21
- {direct_cli-0.4.1 → direct_cli-0.4.2}/README.md +29 -20
- direct_cli-0.4.2/direct_cli/_autotargeting.py +206 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_bidding_strategy.py +876 -882
- direct_cli-0.4.2/direct_cli/_flag_validation.py +53 -0
- direct_cli-0.4.2/direct_cli/_smoke_probes.py +173 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +1 -1
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +4 -2
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/api.py +23 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/auth.py +179 -68
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/cli.py +10 -1
- direct_cli-0.4.2/direct_cli/commands/_lifecycle.py +92 -0
- direct_cli-0.4.2/direct_cli/commands/adextensions.py +124 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/adgroups.py +257 -434
- direct_cli-0.4.2/direct_cli/commands/adimages.py +122 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/ads.py +497 -712
- direct_cli-0.4.2/direct_cli/commands/advideos.py +96 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/agencyclients.py +144 -205
- direct_cli-0.4.2/direct_cli/commands/audiencetargets.py +198 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/auth.py +139 -7
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/balance.py +10 -15
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/bidmodifiers.py +166 -245
- direct_cli-0.4.2/direct_cli/commands/bids.py +226 -0
- direct_cli-0.4.2/direct_cli/commands/businesses.py +57 -0
- direct_cli-0.4.2/direct_cli/commands/campaigns.py +7205 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/changes.py +30 -49
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/clients.py +93 -126
- direct_cli-0.4.2/direct_cli/commands/creatives.py +150 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/dictionaries.py +26 -42
- direct_cli-0.4.2/direct_cli/commands/dynamicads.py +189 -0
- direct_cli-0.4.2/direct_cli/commands/dynamicfeedadtargets.py +190 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/feeds.py +132 -187
- direct_cli-0.4.2/direct_cli/commands/keywordbids.py +299 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/keywords.py +145 -393
- direct_cli-0.4.2/direct_cli/commands/keywordsresearch.py +68 -0
- direct_cli-0.4.2/direct_cli/commands/leads.py +80 -0
- direct_cli-0.4.2/direct_cli/commands/negativekeywordsharedsets.py +128 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/reports.py +63 -67
- direct_cli-0.4.2/direct_cli/commands/retargeting.py +210 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/sitelinks.py +67 -105
- direct_cli-0.4.2/direct_cli/commands/smartadtargets.py +281 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/strategies.py +192 -288
- direct_cli-0.4.2/direct_cli/commands/turbopages.py +67 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/v4account.py +6 -77
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/v4adimage.py +3 -25
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/v4events.py +4 -21
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/v4finance.py +33 -99
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/v4forecast.py +7 -47
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/v4goals.py +5 -48
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/v4keywords.py +4 -21
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/v4tags.py +5 -41
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/v4wordstat.py +9 -47
- direct_cli-0.4.2/direct_cli/commands/vcards.py +245 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/output.py +26 -0
- direct_cli-0.4.2/direct_cli/translations/auth.json +25 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/common.json +2 -2
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/utils.py +96 -6
- direct_cli-0.4.2/direct_cli/v4/emit.py +79 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/v4/money.py +8 -27
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli.egg-info/PKG-INFO +30 -21
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli.egg-info/SOURCES.txt +13 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/pyproject.toml +1 -1
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/build_api_coverage_report.py +11 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/patch_vendor_imports.py +109 -2
- direct_cli-0.4.2/tests/MANUAL_COVERAGE.md +147 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/api_coverage_payloads.py +1 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[creatives_get].yaml +2 -2
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[retargeting_get].yaml +2 -2
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[strategies_get].yaml +2 -2
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_add_update_delete.yaml +64 -64
- direct_cli-0.4.2/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_add_delete.yaml +560 -0
- direct_cli-0.4.2/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_suspend_resume.yaml +622 -0
- direct_cli-0.4.2/tests/cassettes/test_v5_live_write/test_v5_live_draft_feeds_add_update_delete.yaml +250 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_add_update_delete.yaml +122 -60
- direct_cli-0.4.1/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_suspend_resume.yaml → direct_cli-0.4.2/tests/cassettes/test_v5_live_write/test_v5_live_draft_retargeting_add_update_delete.yaml +40 -103
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_add_update_delete.yaml +24 -24
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_suspend_resume.yaml +24 -24
- direct_cli-0.4.1/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_add_delete.yaml → direct_cli-0.4.2/tests/cassettes/test_v5_live_write/test_v5_live_draft_strategies_add_update_archive_unarchive.yaml +49 -50
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/conftest.py +77 -6
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_auth_bw.py +30 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_auth_oauth.py +293 -5
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_auth_op.py +30 -0
- direct_cli-0.4.2/tests/test_autotargeting.py +228 -0
- direct_cli-0.4.2/tests/test_bidding_strategy_constants.py +197 -0
- direct_cli-0.4.2/tests/test_cassette_integrity.py +87 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_cli.py +4 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_dry_run.py +123 -20
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_env_loading.py +54 -0
- direct_cli-0.4.2/tests/test_field_names_option.py +85 -0
- direct_cli-0.4.2/tests/test_flag_validation.py +130 -0
- direct_cli-0.4.2/tests/test_handle_api_errors.py +101 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_integration.py +12 -14
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_low_coverage_payloads.py +71 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_read_cassettes.py +6 -2
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_transport_contract.py +20 -10
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4_exit_codes.py +9 -9
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4account.py +26 -26
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4adimage.py +2 -2
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4events.py +4 -4
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4finance_money.py +42 -10
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4finance_read.py +9 -9
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4forecast.py +6 -6
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4goals.py +6 -6
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4keywords.py +2 -2
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4tags.py +4 -4
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4wordstat.py +7 -7
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v5_live_write.py +218 -7
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_vendor_imports.py +119 -0
- direct_cli-0.4.1/direct_cli/_smoke_probes.py +0 -94
- direct_cli-0.4.1/direct_cli/commands/adextensions.py +0 -164
- direct_cli-0.4.1/direct_cli/commands/adimages.py +0 -158
- direct_cli-0.4.1/direct_cli/commands/advideos.py +0 -119
- direct_cli-0.4.1/direct_cli/commands/audiencetargets.py +0 -306
- direct_cli-0.4.1/direct_cli/commands/bids.py +0 -262
- direct_cli-0.4.1/direct_cli/commands/businesses.py +0 -67
- direct_cli-0.4.1/direct_cli/commands/campaigns.py +0 -7610
- direct_cli-0.4.1/direct_cli/commands/creatives.py +0 -173
- direct_cli-0.4.1/direct_cli/commands/dynamicads.py +0 -296
- direct_cli-0.4.1/direct_cli/commands/dynamicfeedadtargets.py +0 -299
- direct_cli-0.4.1/direct_cli/commands/keywordbids.py +0 -331
- direct_cli-0.4.1/direct_cli/commands/keywordsresearch.py +0 -88
- direct_cli-0.4.1/direct_cli/commands/leads.py +0 -84
- direct_cli-0.4.1/direct_cli/commands/negativekeywordsharedsets.py +0 -180
- direct_cli-0.4.1/direct_cli/commands/retargeting.py +0 -244
- direct_cli-0.4.1/direct_cli/commands/smartadtargets.py +0 -397
- direct_cli-0.4.1/direct_cli/commands/turbopages.py +0 -79
- direct_cli-0.4.1/direct_cli/commands/vcards.py +0 -289
- direct_cli-0.4.1/direct_cli/translations/auth.json +0 -23
- direct_cli-0.4.1/tests/MANUAL_COVERAGE.md +0 -92
- {direct_cli-0.4.1 → direct_cli-0.4.2}/.env.example +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/.github/copilot-instructions.md +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/.github/workflows/api-coverage.yml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/.github/workflows/claude.yml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/.github/workflows/quality.yml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/.gitignore +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/AGENTS.md +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/MANIFEST.in +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/__init__.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_deprecated.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_vendor/__init__.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/endpoints.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/__init__.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/commands/v4shells.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/i18n.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/reports_coverage.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/smoke_matrix.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/adextensions.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/adgroups.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/adimages.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/ads.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/advideos.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/agencyclients.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/audiencetargets.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/balance.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/bidmodifiers.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/bids.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/businesses.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/campaigns.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/changes.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/clients.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/creatives.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/dictionaries.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/dynamicads.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/dynamicfeedadtargets.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/feeds.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/keywordbids.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/keywords.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/keywordsresearch.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/leads.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/negativekeywordsharedsets.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/reports.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/retargeting.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/sitelinks.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/smartadtargets.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/strategies.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/turbopages.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/v4account.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/v4adimage.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/v4events.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/v4finance.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/v4forecast.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/v4goals.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/v4keywords.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/v4shells.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/v4tags.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/v4wordstat.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/translations/vcards.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/v4/__init__.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/v4_contracts.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli/wsdl_coverage.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli.egg-info/dependency_links.txt +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli.egg-info/entry_points.txt +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli.egg-info/requires.txt +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/direct_cli.egg-info/top_level.txt +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/docs/audits/API_COVERAGE.md +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/docs/audits/PROJECT_WIRE_SHAPE_AUDIT_2026-05-30.md +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/docs/audits/WIRE_SHAPE_TRIAGE_2026-05-30.md +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/docs/audits/issue-198-mutating-wsdl-audit.md +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/docs/audits/wire_shape.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/anonymize_cassettes.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/audit_wire_shape.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/build_api_coverage_checklist.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/build_wsdl_optional_field_audit.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/check_all_docs_urls.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/check_reports_drift.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/check_wsdl_drift.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/preflight_check.sh +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/probe_drift_urls.sh +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/refresh_reports_cache.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/refresh_wsdl_cache.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/release_pypi.sh +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/sandbox_write_audit.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/sandbox_write_live.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/test_dangerous_commands.sh +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/test_safe_commands.sh +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/test_sandbox_write.sh +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/scripts/update_vendor.sh +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/setup.cfg +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/setup.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/API_COVERAGE.md +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/API_ISSUE_AUDIT.md +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/WSDL_OPTIONAL_FIELD_AUDIT.md +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/__init__.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/_orphan_store.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_get.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_set_auto.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteRetargetingUpdate.test_retargeting_update.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteStrategies.test_strategies_lifecycle.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[adextensions_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[adgroups_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[adimages_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[ads_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[advideos_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[audiencetargets_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[bidmodifiers_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[bids_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[businesses_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[campaigns_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[changes_check].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[changes_check_campaigns].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[changes_check_dictionaries].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[clients_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[dictionaries_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[dynamicads_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[dynamicfeedadtargets_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[feeds_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[keywordbids_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[keywords_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[keywordsresearch_deduplicate].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[keywordsresearch_has_search_volume].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[leads_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[negativekeywordsharedsets_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[reports_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[sitelinks_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[smartadtargets_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[turbopages_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4events_get_events_log].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4finance_get_clients_units].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4forecast_list].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4goals_get_stat_goals].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4tags_get_campaigns].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4wordstat_list_reports].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[vcards_get].yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_v4finance_check_payment_unknown_transaction.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adgroups_add_update_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adimages_add_get_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_advideos_add_get.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_bids_set.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_campaign_create_get_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_add_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_suspend_resume.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywordbids_set.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_suspend_resume.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_sitelinks_add_get_delete.yaml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/fixtures/test-video.mp4 +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/reports_cache/raw/fields-list.html +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/reports_cache/raw/headers.html +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/reports_cache/raw/period.html +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/reports_cache/raw/spec.html +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/reports_cache/raw/type.html +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/reports_cache/spec.json +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_api_coverage.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_audit_wire_shape.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_auth_write_json.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_balance.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_changes.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_cli_contract.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_comprehensive.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_i18n.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_integration_write.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_reports_drift.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_reports_parsing.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_sandbox_write_audit.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_smoke_matrix.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_unknown_option_hints.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4_contracts.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4_foundation.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4_live_contracts.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4_runtime_shape.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_v4_safety.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/test_wsdl_parity_gate.py +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/adextensions.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/adgroups.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/adimages.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/ads.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/advideos.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/agencyclients.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/audiencetargets.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/bidmodifiers.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/bids.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/businesses.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/campaigns.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/changes.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/clients.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/creatives.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/dictionaries.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/feeds.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/imports/adextensiontypes.xsd +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/imports/general.xsd +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/imports/generalclients.xsd +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/keywordbids.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/keywords.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/keywordsresearch.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/leads.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/retargetinglists.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/sitelinks.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/smartadtargets.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/strategies.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/turbopages.xml +0 -0
- {direct_cli-0.4.1 → direct_cli-0.4.2}/tests/wsdl_cache/vcards.xml +0 -0
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.2
|
|
4
|
+
|
|
5
|
+
**BREAKING CHANGES - get requires SelectionCriteria (#498):**
|
|
6
|
+
|
|
7
|
+
- `adgroups` / `ads` / `keywords` / `strategies` / `creatives` / `dynamicads` /
|
|
8
|
+
`smartadtargets` / `audiencetargets` `get` now refuse an empty
|
|
9
|
+
`SelectionCriteria` before the API call, raising a `UsageError` that asks for
|
|
10
|
+
at least one filter — instead of sending `{"SelectionCriteria": {}}` (which the
|
|
11
|
+
API rejects with the opaque error 4001 for ad-group/ad/keyword resources).
|
|
12
|
+
Extends the same guard already shipped for `bids` / `keywordbids` in 0.4.1
|
|
13
|
+
(#483). WSDL declares `GetRequest.SelectionCriteria` as `minOccurs=1` for all
|
|
14
|
+
eight resources.
|
|
15
|
+
- `retargeting get` gains `--dry-run` and the shared read/pagination option
|
|
16
|
+
stack; its `SelectionCriteria` stays optional (WSDL `minOccurs=0`), so a
|
|
17
|
+
no-filter call is still valid and now omits the empty criteria from the
|
|
18
|
+
payload.
|
|
19
|
+
- All eight commands and `retargeting get` build their request via the shared
|
|
20
|
+
`build_common_params` helper, completing the dedup epic #491 (B3c).
|
|
21
|
+
|
|
22
|
+
**BREAKING CHANGES - auth precedence (#489):**
|
|
23
|
+
|
|
24
|
+
- Base `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` credentials from the
|
|
25
|
+
environment or current-directory `.env` now win over the active OAuth profile
|
|
26
|
+
selected by `direct auth use` when `--profile` is not passed. Explicit
|
|
27
|
+
`--token`, `--login`, and `--profile` still take priority.
|
|
28
|
+
- `direct auth status` reports the selected effective credentials, including
|
|
29
|
+
base env/`.env` and secret-manager fallbacks, instead of reporting only the
|
|
30
|
+
active OAuth profile.
|
|
31
|
+
- `direct auth login` can now ask interactive users whether to save the OAuth
|
|
32
|
+
access token and resolved login into the current-directory `.env`; the default
|
|
33
|
+
answer is no.
|
|
34
|
+
|
|
35
|
+
**Docs — live-write coverage limitation (#538):**
|
|
36
|
+
|
|
37
|
+
- Documented why the SMART_CAMPAIGN / DYNAMIC_TEXT_CAMPAIGN / `adimages`
|
|
38
|
+
live-write lifecycle (`dynamicads`, `smartadtargets`, `adimages`) stays
|
|
39
|
+
recorded only as 3500/5004 error cassettes. Verified via direct API calls that
|
|
40
|
+
the available sandbox **agency** account has no client accounts under it
|
|
41
|
+
(`agencyclients.get` → empty), cannot create one (3001 "No rights to create
|
|
42
|
+
clients", access by request only), and that without a client login every
|
|
43
|
+
agency-scoped mutation returns 8000. Closed #538 as a documented account-tier
|
|
44
|
+
limitation; no CLI code change. See `tests/MANUAL_COVERAGE.md`.
|
|
45
|
+
|
|
3
46
|
## 0.4.1
|
|
4
47
|
|
|
5
48
|
Russian-default CLI localization across all command modules (epic #466).
|
|
@@ -24,9 +24,9 @@ Click group-of-groups. Each Yandex Direct API resource = one file in `direct_cli
|
|
|
24
24
|
|
|
25
25
|
**Request flow:** `cli.py` → `auth.py` (resolves token/login) → `api.py` (`create_client`) → `tapi_yandex_direct.YandexDirect` → Yandex API → `output.py` (format/print).
|
|
26
26
|
|
|
27
|
-
**Credentials priority (CLI):** CLI flags (`--token`, `--login`) >
|
|
27
|
+
**Credentials priority (CLI):** explicit CLI flags (`--token`, `--login`, `--profile`) > base env/current working directory `.env` (`YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN`) > active profile from `direct auth login` / `direct auth use` > 1Password/Bitwarden refs. Explicit `--profile` is isolated and does not fall back to base `.env` login. See `direct_cli/auth.py` (`get_credentials`) and README table for the full chain.
|
|
28
28
|
|
|
29
|
-
**Credentials priority (tests):**
|
|
29
|
+
**Credentials priority (tests):** env vars/current working directory `.env` > active profile > skip. Tests must not silently hit production when a developer has an active `direct auth` profile, so env vars take precedence over the profile (see `tests/test_v4_live_contracts.py::_credentials`).
|
|
30
30
|
|
|
31
31
|
**Shared utilities** (`utils.py`): `parse_ids`, `parse_json`, `build_selection_criteria`, `build_common_params`, `get_default_fields`, `COMMON_FIELDS` dict. All command modules import from here — don't duplicate.
|
|
32
32
|
|
|
@@ -106,7 +106,7 @@ Builds dist artifacts, runs twine checks, uploads to TestPyPI + PyPI. Does **not
|
|
|
106
106
|
|
|
107
107
|
- **Unit** (`test_cli.py`, `test_comprehensive.py`) — no API calls, no token needed.
|
|
108
108
|
- **Integration** (`test_integration.py`, `@pytest.mark.integration`) — require `.env` with `YANDEX_DIRECT_TOKEN` and `YANDEX_DIRECT_LOGIN`. Auto-skip if absent.
|
|
109
|
-
- **Credential resolution in tests:** env vars first, then active `direct auth` profile, then skip. This
|
|
109
|
+
- **Credential resolution in tests:** env vars/current working directory `.env` first, then active `direct auth` profile, then skip. This matches the safe CLI default for base env vs. active profile: a developer machine with an active profile must not silently hit production on a plain `pytest`.
|
|
110
110
|
|
|
111
111
|
## Dangerous Commands — Never Auto-Test
|
|
112
112
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: direct-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Command-line interface for Yandex Direct API
|
|
5
5
|
Author: axisrow
|
|
6
6
|
License: MIT
|
|
@@ -106,6 +106,9 @@ Notes:
|
|
|
106
106
|
- If the first non-interactive step includes `--client-secret`, the secret is remembered for the matching completion step.
|
|
107
107
|
- If a profile already stores a confidential OAuth client, `direct auth login --code CODE --profile NAME` reuses the saved `client_id` and `client_secret`.
|
|
108
108
|
- `direct auth login --oauth-token TOKEN` is a manual access-token import and does not auto-refresh.
|
|
109
|
+
- After a successful interactive login, Direct CLI asks whether to save the
|
|
110
|
+
access token and login to the current working directory `.env`; non-interactive
|
|
111
|
+
login flows do not prompt.
|
|
109
112
|
- Alias `auth_login` is not supported.
|
|
110
113
|
|
|
111
114
|
Credential resolution priority:
|
|
@@ -113,22 +116,23 @@ Credential resolution priority:
|
|
|
113
116
|
| Priority | Source | Example |
|
|
114
117
|
|----------|--------|---------|
|
|
115
118
|
| 1 | Explicit CLI options | `direct --token TOKEN --login LOGIN campaigns get` |
|
|
116
|
-
| 2 |
|
|
117
|
-
| 3 |
|
|
118
|
-
| 4 |
|
|
119
|
+
| 2 | Explicit profile credentials | `direct --profile agency1 campaigns get` |
|
|
120
|
+
| 3 | Base env vars or current working directory `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
|
|
121
|
+
| 4 | Active profile credentials | `direct auth use --profile agency1` |
|
|
119
122
|
| 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
|
|
120
123
|
| 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
|
|
121
124
|
|
|
122
125
|
Direct CLI automatically loads only the `.env` file from the current working
|
|
123
126
|
directory, i.e. the directory where you run `direct`. It does not search for
|
|
124
|
-
`.env` from the installed package or source-code location.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
`.env` from the installed package or source-code location. Without an explicit
|
|
128
|
+
`--profile`, base `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` from env or cwd
|
|
129
|
+
`.env` win over the active OAuth profile. With explicit `--profile`, Direct CLI
|
|
130
|
+
uses only that profile's OAuth/profile-env credentials and does not fall back to
|
|
131
|
+
base `YANDEX_DIRECT_LOGIN`; this prevents mixing accounts. `direct auth status`
|
|
132
|
+
without `--profile` reports the effective credential source; with `--profile` it
|
|
133
|
+
reports that profile.
|
|
130
134
|
|
|
131
|
-
> **Tests
|
|
135
|
+
> **Tests follow the safe credential order.** Live-API test suites (e.g. `tests/test_v4_live_contracts.py`) read `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` from the environment first, only then fall back to the active `direct auth` profile, and skip the test if neither is set. This prevents a developer machine with an active profile from silently hitting production on a plain `pytest` invocation. See `CLAUDE.md` for the contract.
|
|
132
136
|
|
|
133
137
|
Install with `pip install direct-cli`, then run commands with `direct`.
|
|
134
138
|
Invoking the deprecated `direct-cli` entrypoint exits with
|
|
@@ -1065,27 +1069,32 @@ direct --profile agency1 campaigns get
|
|
|
1065
1069
|
- Если первый non-interactive шаг включает `--client-secret`, secret запоминается для последующего completion step.
|
|
1066
1070
|
- Если profile уже хранит confidential OAuth client, `direct auth login --code CODE --profile NAME` использует сохраненные `client_id` и `client_secret`.
|
|
1067
1071
|
- `direct auth login --oauth-token TOKEN` импортирует access token вручную и не включает auto-refresh.
|
|
1072
|
+
- После успешного интерактивного входа Direct CLI спрашивает, сохранить ли
|
|
1073
|
+
access token и login в `.env` текущей рабочей папки; non-interactive вход этот
|
|
1074
|
+
вопрос не задаёт.
|
|
1068
1075
|
|
|
1069
1076
|
Порядок выбора credentials:
|
|
1070
1077
|
|
|
1071
1078
|
| Приоритет | Источник | Пример |
|
|
1072
1079
|
|-----------|----------|--------|
|
|
1073
1080
|
| 1 | Явные CLI-опции | `direct --token TOKEN --login LOGIN campaigns get` |
|
|
1074
|
-
| 2 |
|
|
1075
|
-
| 3 |
|
|
1076
|
-
| 4 |
|
|
1081
|
+
| 2 | Явно выбранный profile | `direct --profile agency1 campaigns get` |
|
|
1082
|
+
| 3 | Базовые env vars или `.env` из папки запуска | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
|
|
1083
|
+
| 4 | Активный profile | `direct auth use --profile agency1` |
|
|
1077
1084
|
| 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
|
|
1078
1085
|
| 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
|
|
1079
1086
|
|
|
1080
1087
|
Direct CLI автоматически читает только `.env` из текущей рабочей папки, то есть
|
|
1081
1088
|
из папки, где запущена команда `direct`. Он не ищет `.env` от папки
|
|
1082
|
-
установленного пакета или исходного кода.
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
+
установленного пакета или исходного кода. Без явного `--profile` базовые
|
|
1090
|
+
`YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения или cwd `.env`
|
|
1091
|
+
побеждают активный OAuth profile. С явным `--profile` Direct CLI использует
|
|
1092
|
+
только OAuth/profile-env credentials этого профиля и не подставляет base
|
|
1093
|
+
`YANDEX_DIRECT_LOGIN`; это защищает от смешивания аккаунтов. `direct auth status`
|
|
1094
|
+
без `--profile` показывает реально выбранный источник credentials, а с
|
|
1095
|
+
`--profile` показывает этот профиль.
|
|
1096
|
+
|
|
1097
|
+
> **Тесты используют безопасный порядок credentials.** Live-API тесты (например `tests/test_v4_live_contracts.py`) сначала читают `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения, затем падают на активный профиль `direct auth`, и скипают тест если ни того ни другого нет. Это защищает от случайного обращения к production API на машине разработчика с активным profile. Контракт зафиксирован в `CLAUDE.md`.
|
|
1089
1098
|
|
|
1090
1099
|
Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
|
|
1091
1100
|
через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
|
|
@@ -63,6 +63,9 @@ Notes:
|
|
|
63
63
|
- If the first non-interactive step includes `--client-secret`, the secret is remembered for the matching completion step.
|
|
64
64
|
- If a profile already stores a confidential OAuth client, `direct auth login --code CODE --profile NAME` reuses the saved `client_id` and `client_secret`.
|
|
65
65
|
- `direct auth login --oauth-token TOKEN` is a manual access-token import and does not auto-refresh.
|
|
66
|
+
- After a successful interactive login, Direct CLI asks whether to save the
|
|
67
|
+
access token and login to the current working directory `.env`; non-interactive
|
|
68
|
+
login flows do not prompt.
|
|
66
69
|
- Alias `auth_login` is not supported.
|
|
67
70
|
|
|
68
71
|
Credential resolution priority:
|
|
@@ -70,22 +73,23 @@ Credential resolution priority:
|
|
|
70
73
|
| Priority | Source | Example |
|
|
71
74
|
|----------|--------|---------|
|
|
72
75
|
| 1 | Explicit CLI options | `direct --token TOKEN --login LOGIN campaigns get` |
|
|
73
|
-
| 2 |
|
|
74
|
-
| 3 |
|
|
75
|
-
| 4 |
|
|
76
|
+
| 2 | Explicit profile credentials | `direct --profile agency1 campaigns get` |
|
|
77
|
+
| 3 | Base env vars or current working directory `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
|
|
78
|
+
| 4 | Active profile credentials | `direct auth use --profile agency1` |
|
|
76
79
|
| 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
|
|
77
80
|
| 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
|
|
78
81
|
|
|
79
82
|
Direct CLI automatically loads only the `.env` file from the current working
|
|
80
83
|
directory, i.e. the directory where you run `direct`. It does not search for
|
|
81
|
-
`.env` from the installed package or source-code location.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
`.env` from the installed package or source-code location. Without an explicit
|
|
85
|
+
`--profile`, base `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` from env or cwd
|
|
86
|
+
`.env` win over the active OAuth profile. With explicit `--profile`, Direct CLI
|
|
87
|
+
uses only that profile's OAuth/profile-env credentials and does not fall back to
|
|
88
|
+
base `YANDEX_DIRECT_LOGIN`; this prevents mixing accounts. `direct auth status`
|
|
89
|
+
without `--profile` reports the effective credential source; with `--profile` it
|
|
90
|
+
reports that profile.
|
|
87
91
|
|
|
88
|
-
> **Tests
|
|
92
|
+
> **Tests follow the safe credential order.** Live-API test suites (e.g. `tests/test_v4_live_contracts.py`) read `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` from the environment first, only then fall back to the active `direct auth` profile, and skip the test if neither is set. This prevents a developer machine with an active profile from silently hitting production on a plain `pytest` invocation. See `CLAUDE.md` for the contract.
|
|
89
93
|
|
|
90
94
|
Install with `pip install direct-cli`, then run commands with `direct`.
|
|
91
95
|
Invoking the deprecated `direct-cli` entrypoint exits with
|
|
@@ -1022,27 +1026,32 @@ direct --profile agency1 campaigns get
|
|
|
1022
1026
|
- Если первый non-interactive шаг включает `--client-secret`, secret запоминается для последующего completion step.
|
|
1023
1027
|
- Если profile уже хранит confidential OAuth client, `direct auth login --code CODE --profile NAME` использует сохраненные `client_id` и `client_secret`.
|
|
1024
1028
|
- `direct auth login --oauth-token TOKEN` импортирует access token вручную и не включает auto-refresh.
|
|
1029
|
+
- После успешного интерактивного входа Direct CLI спрашивает, сохранить ли
|
|
1030
|
+
access token и login в `.env` текущей рабочей папки; non-interactive вход этот
|
|
1031
|
+
вопрос не задаёт.
|
|
1025
1032
|
|
|
1026
1033
|
Порядок выбора credentials:
|
|
1027
1034
|
|
|
1028
1035
|
| Приоритет | Источник | Пример |
|
|
1029
1036
|
|-----------|----------|--------|
|
|
1030
1037
|
| 1 | Явные CLI-опции | `direct --token TOKEN --login LOGIN campaigns get` |
|
|
1031
|
-
| 2 |
|
|
1032
|
-
| 3 |
|
|
1033
|
-
| 4 |
|
|
1038
|
+
| 2 | Явно выбранный profile | `direct --profile agency1 campaigns get` |
|
|
1039
|
+
| 3 | Базовые env vars или `.env` из папки запуска | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
|
|
1040
|
+
| 4 | Активный profile | `direct auth use --profile agency1` |
|
|
1034
1041
|
| 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
|
|
1035
1042
|
| 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
|
|
1036
1043
|
|
|
1037
1044
|
Direct CLI автоматически читает только `.env` из текущей рабочей папки, то есть
|
|
1038
1045
|
из папки, где запущена команда `direct`. Он не ищет `.env` от папки
|
|
1039
|
-
установленного пакета или исходного кода.
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
+
установленного пакета или исходного кода. Без явного `--profile` базовые
|
|
1047
|
+
`YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения или cwd `.env`
|
|
1048
|
+
побеждают активный OAuth profile. С явным `--profile` Direct CLI использует
|
|
1049
|
+
только OAuth/profile-env credentials этого профиля и не подставляет base
|
|
1050
|
+
`YANDEX_DIRECT_LOGIN`; это защищает от смешивания аккаунтов. `direct auth status`
|
|
1051
|
+
без `--profile` показывает реально выбранный источник credentials, а с
|
|
1052
|
+
`--profile` показывает этот профиль.
|
|
1053
|
+
|
|
1054
|
+
> **Тесты используют безопасный порядок credentials.** Live-API тесты (например `tests/test_v4_live_contracts.py`) сначала читают `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения, затем падают на активный профиль `direct auth`, и скипают тест если ни того ни другого нет. Это защищает от случайного обращения к production API на машине разработчика с активным profile. Контракт зафиксирован в `CLAUDE.md`.
|
|
1046
1055
|
|
|
1047
1056
|
Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
|
|
1048
1057
|
через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""Shared autotargeting CLI helpers for ad groups and keywords.
|
|
2
|
+
|
|
3
|
+
``adgroups`` (DynamicTextAdGroup/DynamicTextFeedAdGroup) and ``keywords`` both
|
|
4
|
+
parse the same AutotargetingCategories / AutotargetingSettings CLI surface.
|
|
5
|
+
The helpers used to be duplicated in each command module and had drifted
|
|
6
|
+
(category-token normalization, and which legacy flags conflict with the typed
|
|
7
|
+
``AutotargetingSettings`` flags). This module is the single source of truth.
|
|
8
|
+
|
|
9
|
+
The legacy-mix guard models an **intentional** asymmetry explicitly via
|
|
10
|
+
``legacy_candidates``: ``adgroups`` treats only ``--autotargeting-category`` as
|
|
11
|
+
legacy (it has no ``--autotargeting-brand-option`` flag at all), while
|
|
12
|
+
``keywords`` treats both ``--autotargeting-category`` and
|
|
13
|
+
``--autotargeting-brand-option`` as legacy.
|
|
14
|
+
|
|
15
|
+
Token normalization is unified on :func:`normalize_enum_token` for both
|
|
16
|
+
categories and brand options. For categories this is behavior-preserving (the
|
|
17
|
+
constants are single words with no ``_``, so ``-``->``_`` only ever touches
|
|
18
|
+
already-invalid tokens). For brand options it is a deliberate *loosening*: the
|
|
19
|
+
constants contain underscores (``WITHOUT_BRANDS``), so the hyphenated spelling
|
|
20
|
+
(``WITHOUT-BRANDS``) — rejected by the old flat ``.strip().upper()`` — is now
|
|
21
|
+
accepted, making every autotargeting enum normalize identically.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from typing import Dict, List, Optional, Sequence, Tuple
|
|
25
|
+
|
|
26
|
+
import click
|
|
27
|
+
|
|
28
|
+
from .i18n import t
|
|
29
|
+
|
|
30
|
+
AUTOTARGETING_CATEGORIES = (
|
|
31
|
+
"EXACT",
|
|
32
|
+
"ALTERNATIVE",
|
|
33
|
+
"COMPETITOR",
|
|
34
|
+
"BROADER",
|
|
35
|
+
"ACCESSORY",
|
|
36
|
+
)
|
|
37
|
+
AUTOTARGETING_BRAND_OPTIONS = (
|
|
38
|
+
"WITHOUT_BRANDS",
|
|
39
|
+
"WITH_ADVERTISER_BRAND",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def normalize_enum_token(value: str) -> str:
|
|
44
|
+
"""Normalize enum-like CLI tokens to Yandex Direct uppercase constants."""
|
|
45
|
+
return value.strip().upper().replace("-", "_")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def parse_autotargeting_categories(
|
|
49
|
+
raw_values: Tuple[str, ...],
|
|
50
|
+
) -> Optional[List[Dict[str, str]]]:
|
|
51
|
+
"""Parse AutotargetingCategories CLI items (CATEGORY=YES|NO)."""
|
|
52
|
+
if not raw_values:
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
allowed_categories = ", ".join(AUTOTARGETING_CATEGORIES)
|
|
56
|
+
items: List[Dict[str, str]] = []
|
|
57
|
+
for raw_value in raw_values:
|
|
58
|
+
category_raw, separator, value_raw = raw_value.strip().partition("=")
|
|
59
|
+
if not separator:
|
|
60
|
+
raise click.UsageError(
|
|
61
|
+
t(
|
|
62
|
+
"--autotargeting-category expects CATEGORY=YES|NO "
|
|
63
|
+
"(for example EXACT=YES)"
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
category = normalize_enum_token(category_raw)
|
|
68
|
+
value = normalize_enum_token(value_raw)
|
|
69
|
+
if category not in AUTOTARGETING_CATEGORIES:
|
|
70
|
+
raise click.UsageError(
|
|
71
|
+
t(
|
|
72
|
+
"Invalid --autotargeting-category category {category_raw!r}; "
|
|
73
|
+
"allowed: {allowed_categories}"
|
|
74
|
+
).format(
|
|
75
|
+
category_raw=category_raw, allowed_categories=allowed_categories
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
if value not in {"YES", "NO"}:
|
|
79
|
+
raise click.UsageError(
|
|
80
|
+
t(
|
|
81
|
+
"Invalid --autotargeting-category value {value_raw!r}; "
|
|
82
|
+
"expected YES or NO"
|
|
83
|
+
).format(value_raw=value_raw)
|
|
84
|
+
)
|
|
85
|
+
items.append({"Category": category, "Value": value})
|
|
86
|
+
|
|
87
|
+
return items
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def parse_autotargeting_brand_options(
|
|
91
|
+
raw_values: Tuple[str, ...],
|
|
92
|
+
) -> Optional[List[Dict[str, str]]]:
|
|
93
|
+
"""Parse AutotargetingSettings BrandOptions CLI items (OPTION=YES|NO)."""
|
|
94
|
+
if not raw_values:
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
allowed_options = ", ".join(AUTOTARGETING_BRAND_OPTIONS)
|
|
98
|
+
items: List[Dict[str, str]] = []
|
|
99
|
+
for raw_value in raw_values:
|
|
100
|
+
option_raw, separator, value_raw = raw_value.strip().partition("=")
|
|
101
|
+
if not separator:
|
|
102
|
+
raise click.UsageError(
|
|
103
|
+
t(
|
|
104
|
+
"--autotargeting-brand-option expects OPTION=YES|NO "
|
|
105
|
+
"(for example WITHOUT_BRANDS=YES)"
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
option = normalize_enum_token(option_raw)
|
|
110
|
+
value = normalize_enum_token(value_raw)
|
|
111
|
+
if option not in AUTOTARGETING_BRAND_OPTIONS:
|
|
112
|
+
raise click.UsageError(
|
|
113
|
+
t(
|
|
114
|
+
"Invalid --autotargeting-brand-option option {option_raw!r}; "
|
|
115
|
+
"allowed: {allowed_options}"
|
|
116
|
+
).format(option_raw=option_raw, allowed_options=allowed_options)
|
|
117
|
+
)
|
|
118
|
+
if value not in {"YES", "NO"}:
|
|
119
|
+
raise click.UsageError(
|
|
120
|
+
t(
|
|
121
|
+
"Invalid --autotargeting-brand-option value {value_raw!r}; "
|
|
122
|
+
"expected YES or NO"
|
|
123
|
+
).format(value_raw=value_raw)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
items.append({"Option": option, "Value": value})
|
|
127
|
+
|
|
128
|
+
return items
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def build_autotargeting_settings(
|
|
132
|
+
*,
|
|
133
|
+
exact: Optional[str],
|
|
134
|
+
narrow: Optional[str],
|
|
135
|
+
alternative: Optional[str],
|
|
136
|
+
accessory: Optional[str],
|
|
137
|
+
broader: Optional[str],
|
|
138
|
+
without_brands: Optional[str],
|
|
139
|
+
with_advertiser_brand: Optional[str],
|
|
140
|
+
with_competitors_brand: Optional[str],
|
|
141
|
+
) -> Optional[Dict[str, Dict[str, str]]]:
|
|
142
|
+
"""Build AutotargetingSettings (Categories + BrandOptions) from typed flags."""
|
|
143
|
+
categories: Dict[str, str] = {}
|
|
144
|
+
for field_name, value in (
|
|
145
|
+
("Exact", exact),
|
|
146
|
+
("Narrow", narrow),
|
|
147
|
+
("Alternative", alternative),
|
|
148
|
+
("Accessory", accessory),
|
|
149
|
+
("Broader", broader),
|
|
150
|
+
):
|
|
151
|
+
if value is not None:
|
|
152
|
+
categories[field_name] = value.upper()
|
|
153
|
+
|
|
154
|
+
brand_options: Dict[str, str] = {}
|
|
155
|
+
for field_name, value in (
|
|
156
|
+
("WithoutBrands", without_brands),
|
|
157
|
+
("WithAdvertiserBrand", with_advertiser_brand),
|
|
158
|
+
("WithCompetitorsBrand", with_competitors_brand),
|
|
159
|
+
):
|
|
160
|
+
if value is not None:
|
|
161
|
+
brand_options[field_name] = value.upper()
|
|
162
|
+
|
|
163
|
+
settings: Dict[str, Dict[str, str]] = {}
|
|
164
|
+
if categories:
|
|
165
|
+
settings["Categories"] = categories
|
|
166
|
+
if brand_options:
|
|
167
|
+
settings["BrandOptions"] = brand_options
|
|
168
|
+
|
|
169
|
+
return settings or None
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def reject_legacy_autotargeting_mix(
|
|
173
|
+
settings: Optional[Dict[str, Dict[str, str]]],
|
|
174
|
+
*,
|
|
175
|
+
legacy_candidates: Sequence[Tuple[str, bool]],
|
|
176
|
+
) -> None:
|
|
177
|
+
"""Reject typed AutotargetingSettings combined with legacy autotargeting flags.
|
|
178
|
+
|
|
179
|
+
``legacy_candidates`` is an ordered ``(flag_name, supplied)`` sequence that
|
|
180
|
+
declares which flags count as legacy for the calling resource. Ad groups
|
|
181
|
+
declare only ``--autotargeting-category``; keywords declare both
|
|
182
|
+
``--autotargeting-category`` and ``--autotargeting-brand-option``. The
|
|
183
|
+
error message preserves both original source strings so every translation
|
|
184
|
+
catalog entry stays referenced and the rendered output is unchanged.
|
|
185
|
+
"""
|
|
186
|
+
if settings is None:
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
declared = [flag for flag, _ in legacy_candidates]
|
|
190
|
+
supplied = [flag for flag, present in legacy_candidates if present]
|
|
191
|
+
if not supplied:
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
if declared == ["--autotargeting-category"]:
|
|
195
|
+
raise click.UsageError(
|
|
196
|
+
t(
|
|
197
|
+
"AutotargetingSettings flags cannot be combined with legacy "
|
|
198
|
+
"--autotargeting-category flags."
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
raise click.UsageError(
|
|
203
|
+
t(
|
|
204
|
+
"AutotargetingSettings flags cannot be combined with legacy {arg0} flags."
|
|
205
|
+
).format(arg0=", ".join(supplied))
|
|
206
|
+
)
|