direct-cli 0.2.10__tar.gz → 0.2.11__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.2.10 → direct_cli-0.2.11}/PKG-INFO +58 -1
- {direct_cli-0.2.10 → direct_cli-0.2.11}/README.md +57 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/cli.py +46 -31
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/agencyclients.py +32 -14
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/reports_coverage.py +15 -5
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/utils.py +7 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli.egg-info/PKG-INFO +58 -1
- {direct_cli-0.2.10 → direct_cli-0.2.11}/pyproject.toml +1 -1
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/test_safe_commands.sh +1 -3
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/API_COVERAGE.md +14 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/MANUAL_COVERAGE.md +7 -4
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_api_coverage.py +16 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_cli.py +24 -1
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_dry_run.py +8 -4
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_integration.py +208 -16
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_integration_write.py +4 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/.env.example +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/.github/copilot-instructions.md +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/.github/workflows/api-coverage.yml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/.github/workflows/claude-code-review.yml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/.github/workflows/claude.yml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/.gitignore +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/AGENTS.md +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/CLAUDE.md +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/MANIFEST.in +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/__init__.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/_deprecated.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/_vendor/__init__.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/api.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/auth.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/__init__.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/adextensions.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/adgroups.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/adimages.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/ads.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/advideos.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/audiencetargets.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/auth.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/bidmodifiers.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/bids.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/businesses.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/campaigns.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/changes.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/clients.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/creatives.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/dictionaries.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/dynamicads.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/dynamicfeedadtargets.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/feeds.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/keywordbids.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/keywords.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/keywordsresearch.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/leads.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/reports.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/retargeting.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/sitelinks.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/smartadtargets.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/strategies.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/turbopages.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/commands/vcards.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/output.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/smoke_matrix.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli/wsdl_coverage.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli.egg-info/SOURCES.txt +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli.egg-info/dependency_links.txt +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli.egg-info/entry_points.txt +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli.egg-info/requires.txt +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/direct_cli.egg-info/top_level.txt +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/anonymize_cassettes.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/build_api_coverage_checklist.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/build_api_coverage_report.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/check_reports_drift.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/check_wsdl_drift.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/patch_vendor_imports.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/refresh_reports_cache.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/refresh_wsdl_cache.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/release_pypi.sh +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/sandbox_write_live.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/test_dangerous_commands.sh +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/test_sandbox_write.sh +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/scripts/update_vendor.sh +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/setup.cfg +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/setup.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/API_ISSUE_AUDIT.md +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/__init__.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_adgroups_add_update_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_adimages_add_get_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_ads_add_update_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_advideos_add_get.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_add_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_suspend_resume.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_bids_set.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_campaign_create_get_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_add_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_suspend_resume.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_keywordbids_set.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_add_update_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_suspend_resume.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_sitelinks_add_get_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_add_update_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_suspend_resume.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/conftest.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/fixtures/test-video.mp4 +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/reports_cache/raw/fields-list.html +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/reports_cache/raw/headers.html +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/reports_cache/raw/spec.html +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/reports_cache/raw/type.html +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/reports_cache/spec.json +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_auth_bw.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_auth_oauth.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_auth_op.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_comprehensive.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_integration_live_write.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_reports_drift.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_smoke_matrix.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_transport_contract.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/test_vendor_imports.py +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/adextensions.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/adgroups.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/adimages.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/ads.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/advideos.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/agencyclients.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/audiencetargets.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/bidmodifiers.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/bids.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/businesses.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/campaigns.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/changes.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/clients.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/creatives.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/dictionaries.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/feeds.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/keywordbids.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/keywords.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/keywordsresearch.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/leads.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/retargetinglists.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/sitelinks.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/smartadtargets.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/strategies.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/turbopages.xml +0 -0
- {direct_cli-0.2.10 → direct_cli-0.2.11}/tests/wsdl_cache/vcards.xml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: direct-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.11
|
|
4
4
|
Summary: Command-line interface for Yandex Direct API
|
|
5
5
|
Author: axisrow
|
|
6
6
|
License: MIT
|
|
@@ -97,6 +97,23 @@ Notes:
|
|
|
97
97
|
- Authorization is performed via `direct auth login`.
|
|
98
98
|
- Alias `auth_login` is not supported.
|
|
99
99
|
|
|
100
|
+
Credential resolution priority:
|
|
101
|
+
|
|
102
|
+
| Priority | Source | Example |
|
|
103
|
+
|----------|--------|---------|
|
|
104
|
+
| 1 | Explicit CLI options | `direct --token TOKEN --login LOGIN campaigns get` |
|
|
105
|
+
| 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
|
|
106
|
+
| 3 | Profile-specific env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
|
|
107
|
+
| 4 | Base env vars or project `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
|
|
108
|
+
| 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
|
|
109
|
+
| 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
|
|
110
|
+
|
|
111
|
+
The project `.env` file is loaded automatically. If a profile is selected
|
|
112
|
+
with `--profile` or `direct auth use --profile NAME`, Direct CLI does not
|
|
113
|
+
fall back to base `YANDEX_DIRECT_LOGIN`; this prevents mixing a profile token
|
|
114
|
+
with a login from the project `.env`. For multi-account setups, prefer OAuth
|
|
115
|
+
profiles or profile-specific env vars instead of base credentials.
|
|
116
|
+
|
|
100
117
|
Install with `pip install direct-cli`, then run commands with `direct`.
|
|
101
118
|
Invoking the deprecated `direct-cli` entrypoint exits with
|
|
102
119
|
`use direct instead of direct-cli`.
|
|
@@ -634,6 +651,45 @@ YANDEX_DIRECT_LOGIN=ваш_логин_на_яндексе
|
|
|
634
651
|
direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
|
|
635
652
|
```
|
|
636
653
|
|
|
654
|
+
Используйте профильные credentials из `.env`:
|
|
655
|
+
|
|
656
|
+
```env
|
|
657
|
+
YANDEX_DIRECT_TOKEN_AGENCY1=token-1
|
|
658
|
+
YANDEX_DIRECT_LOGIN_AGENCY1=client-login-1
|
|
659
|
+
YANDEX_DIRECT_TOKEN_AGENCY2=token-2
|
|
660
|
+
YANDEX_DIRECT_LOGIN_AGENCY2=client-login-2
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
OAuth и profile-команды:
|
|
664
|
+
|
|
665
|
+
```bash
|
|
666
|
+
direct auth login
|
|
667
|
+
direct auth login --profile agency1
|
|
668
|
+
direct auth login --code abc123 --profile agency1
|
|
669
|
+
direct auth login --oauth-token y0_example --profile agency1
|
|
670
|
+
direct auth list
|
|
671
|
+
direct auth use --profile agency1
|
|
672
|
+
direct auth status --profile agency1
|
|
673
|
+
direct --profile agency1 campaigns get
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
Порядок выбора credentials:
|
|
677
|
+
|
|
678
|
+
| Приоритет | Источник | Пример |
|
|
679
|
+
|-----------|----------|--------|
|
|
680
|
+
| 1 | Явные CLI-опции | `direct --token TOKEN --login LOGIN campaigns get` |
|
|
681
|
+
| 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
|
|
682
|
+
| 3 | Профильные env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
|
|
683
|
+
| 4 | Базовые env vars или project `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
|
|
684
|
+
| 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
|
|
685
|
+
| 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
|
|
686
|
+
|
|
687
|
+
Файл `.env` в проекте загружается автоматически. Если профиль выбран через
|
|
688
|
+
`--profile` или `direct auth use --profile NAME`, Direct CLI не подставляет
|
|
689
|
+
base `YANDEX_DIRECT_LOGIN`; это защищает от смешивания токена из профиля с
|
|
690
|
+
логином из project `.env`. Для нескольких аккаунтов используйте OAuth profiles
|
|
691
|
+
или профильные env vars, а не базовые credentials.
|
|
692
|
+
|
|
637
693
|
Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
|
|
638
694
|
через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
|
|
639
695
|
подсказкой `use direct instead of direct-cli`.
|
|
@@ -644,6 +700,7 @@ direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
|
|
|
644
700
|
|-------|----------|
|
|
645
701
|
| `--token` | OAuth-токен доступа к API |
|
|
646
702
|
| `--login` | Direct client login |
|
|
703
|
+
| `--profile` | Имя credential profile |
|
|
647
704
|
| `--sandbox` | Использовать тестовое API (песочница) |
|
|
648
705
|
|
|
649
706
|
### Использование
|
|
@@ -58,6 +58,23 @@ Notes:
|
|
|
58
58
|
- Authorization is performed via `direct auth login`.
|
|
59
59
|
- Alias `auth_login` is not supported.
|
|
60
60
|
|
|
61
|
+
Credential resolution priority:
|
|
62
|
+
|
|
63
|
+
| Priority | Source | Example |
|
|
64
|
+
|----------|--------|---------|
|
|
65
|
+
| 1 | Explicit CLI options | `direct --token TOKEN --login LOGIN campaigns get` |
|
|
66
|
+
| 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
|
|
67
|
+
| 3 | Profile-specific env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
|
|
68
|
+
| 4 | Base env vars or project `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
|
|
69
|
+
| 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
|
|
70
|
+
| 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
|
|
71
|
+
|
|
72
|
+
The project `.env` file is loaded automatically. If a profile is selected
|
|
73
|
+
with `--profile` or `direct auth use --profile NAME`, Direct CLI does not
|
|
74
|
+
fall back to base `YANDEX_DIRECT_LOGIN`; this prevents mixing a profile token
|
|
75
|
+
with a login from the project `.env`. For multi-account setups, prefer OAuth
|
|
76
|
+
profiles or profile-specific env vars instead of base credentials.
|
|
77
|
+
|
|
61
78
|
Install with `pip install direct-cli`, then run commands with `direct`.
|
|
62
79
|
Invoking the deprecated `direct-cli` entrypoint exits with
|
|
63
80
|
`use direct instead of direct-cli`.
|
|
@@ -595,6 +612,45 @@ YANDEX_DIRECT_LOGIN=ваш_логин_на_яндексе
|
|
|
595
612
|
direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
|
|
596
613
|
```
|
|
597
614
|
|
|
615
|
+
Используйте профильные credentials из `.env`:
|
|
616
|
+
|
|
617
|
+
```env
|
|
618
|
+
YANDEX_DIRECT_TOKEN_AGENCY1=token-1
|
|
619
|
+
YANDEX_DIRECT_LOGIN_AGENCY1=client-login-1
|
|
620
|
+
YANDEX_DIRECT_TOKEN_AGENCY2=token-2
|
|
621
|
+
YANDEX_DIRECT_LOGIN_AGENCY2=client-login-2
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
OAuth и profile-команды:
|
|
625
|
+
|
|
626
|
+
```bash
|
|
627
|
+
direct auth login
|
|
628
|
+
direct auth login --profile agency1
|
|
629
|
+
direct auth login --code abc123 --profile agency1
|
|
630
|
+
direct auth login --oauth-token y0_example --profile agency1
|
|
631
|
+
direct auth list
|
|
632
|
+
direct auth use --profile agency1
|
|
633
|
+
direct auth status --profile agency1
|
|
634
|
+
direct --profile agency1 campaigns get
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
Порядок выбора credentials:
|
|
638
|
+
|
|
639
|
+
| Приоритет | Источник | Пример |
|
|
640
|
+
|-----------|----------|--------|
|
|
641
|
+
| 1 | Явные CLI-опции | `direct --token TOKEN --login LOGIN campaigns get` |
|
|
642
|
+
| 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
|
|
643
|
+
| 3 | Профильные env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
|
|
644
|
+
| 4 | Базовые env vars или project `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
|
|
645
|
+
| 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
|
|
646
|
+
| 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
|
|
647
|
+
|
|
648
|
+
Файл `.env` в проекте загружается автоматически. Если профиль выбран через
|
|
649
|
+
`--profile` или `direct auth use --profile NAME`, Direct CLI не подставляет
|
|
650
|
+
base `YANDEX_DIRECT_LOGIN`; это защищает от смешивания токена из профиля с
|
|
651
|
+
логином из project `.env`. Для нескольких аккаунтов используйте OAuth profiles
|
|
652
|
+
или профильные env vars, а не базовые credentials.
|
|
653
|
+
|
|
598
654
|
Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
|
|
599
655
|
через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
|
|
600
656
|
подсказкой `use direct instead of direct-cli`.
|
|
@@ -605,6 +661,7 @@ direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
|
|
|
605
661
|
|-------|----------|
|
|
606
662
|
| `--token` | OAuth-токен доступа к API |
|
|
607
663
|
| `--login` | Direct client login |
|
|
664
|
+
| `--profile` | Имя credential profile |
|
|
608
665
|
| `--sandbox` | Использовать тестовое API (песочница) |
|
|
609
666
|
|
|
610
667
|
### Использование
|
|
@@ -8,6 +8,7 @@ from dotenv import load_dotenv
|
|
|
8
8
|
|
|
9
9
|
from . import __version__
|
|
10
10
|
from .auth import get_active_profile, get_credentials
|
|
11
|
+
from .utils import get_docs_url
|
|
11
12
|
|
|
12
13
|
from .commands.campaigns import campaigns
|
|
13
14
|
from .commands.adgroups import adgroups
|
|
@@ -129,38 +130,52 @@ def cli(
|
|
|
129
130
|
ctx.obj["login"] = login
|
|
130
131
|
|
|
131
132
|
|
|
133
|
+
def _register_command(command: click.Command) -> None:
|
|
134
|
+
"""Register a command and append mapped documentation URL to group help."""
|
|
135
|
+
docs_url = get_docs_url(command.name or "")
|
|
136
|
+
if docs_url:
|
|
137
|
+
docs_line = f"\b\nDocumentation: {docs_url}"
|
|
138
|
+
command.epilog = (
|
|
139
|
+
f"{command.epilog}\n\n{docs_line}" if command.epilog else docs_line
|
|
140
|
+
)
|
|
141
|
+
cli.add_command(command)
|
|
142
|
+
|
|
143
|
+
|
|
132
144
|
# Register all commands
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
145
|
+
for command in (
|
|
146
|
+
campaigns,
|
|
147
|
+
adgroups,
|
|
148
|
+
ads,
|
|
149
|
+
keywords,
|
|
150
|
+
keywordbids,
|
|
151
|
+
bids,
|
|
152
|
+
bidmodifiers,
|
|
153
|
+
audiencetargets,
|
|
154
|
+
retargeting,
|
|
155
|
+
creatives,
|
|
156
|
+
adimages,
|
|
157
|
+
adextensions,
|
|
158
|
+
sitelinks,
|
|
159
|
+
vcards,
|
|
160
|
+
leads,
|
|
161
|
+
clients,
|
|
162
|
+
agencyclients,
|
|
163
|
+
dictionaries,
|
|
164
|
+
changes,
|
|
165
|
+
reports,
|
|
166
|
+
turbopages,
|
|
167
|
+
negativekeywordsharedsets,
|
|
168
|
+
feeds,
|
|
169
|
+
smartadtargets,
|
|
170
|
+
businesses,
|
|
171
|
+
keywordsresearch,
|
|
172
|
+
dynamicads,
|
|
173
|
+
advideos,
|
|
174
|
+
dynamicfeedadtargets,
|
|
175
|
+
strategies,
|
|
176
|
+
auth,
|
|
177
|
+
):
|
|
178
|
+
_register_command(command)
|
|
164
179
|
|
|
165
180
|
|
|
166
181
|
if __name__ == "__main__":
|
|
@@ -6,7 +6,7 @@ import click
|
|
|
6
6
|
|
|
7
7
|
from ..api import create_client
|
|
8
8
|
from ..output import format_output, print_error
|
|
9
|
-
from ..utils import get_default_fields
|
|
9
|
+
from ..utils import get_default_fields
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def _build_notification(
|
|
@@ -21,10 +21,24 @@ def _build_notification(
|
|
|
21
21
|
notification["Email"] = notification_email
|
|
22
22
|
if notification_lang:
|
|
23
23
|
notification["Lang"] = notification_lang
|
|
24
|
-
if
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
if notification_email:
|
|
25
|
+
subscriptions = []
|
|
26
|
+
if send_account_news is not None:
|
|
27
|
+
subscriptions.append(
|
|
28
|
+
{
|
|
29
|
+
"Option": "RECEIVE_RECOMMENDATIONS",
|
|
30
|
+
"Value": "YES" if send_account_news else "NO",
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
if send_warnings is not None:
|
|
34
|
+
subscriptions.append(
|
|
35
|
+
{
|
|
36
|
+
"Option": "TRACK_POSITION_CHANGES",
|
|
37
|
+
"Value": "YES" if send_warnings else "NO",
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
if subscriptions:
|
|
41
|
+
notification["EmailSubscriptions"] = subscriptions
|
|
28
42
|
return notification
|
|
29
43
|
|
|
30
44
|
|
|
@@ -34,14 +48,21 @@ def agencyclients():
|
|
|
34
48
|
|
|
35
49
|
|
|
36
50
|
@agencyclients.command()
|
|
37
|
-
@click.option("--
|
|
51
|
+
@click.option("--logins", help="Comma-separated client logins")
|
|
52
|
+
@click.option(
|
|
53
|
+
"--archived",
|
|
54
|
+
type=click.Choice(["YES", "NO"]),
|
|
55
|
+
default="NO",
|
|
56
|
+
show_default=True,
|
|
57
|
+
help="Filter archived clients",
|
|
58
|
+
)
|
|
38
59
|
@click.option("--limit", type=int, help="Limit number of results")
|
|
39
60
|
@click.option("--fetch-all", is_flag=True, help="Fetch all pages")
|
|
40
61
|
@click.option("--format", "output_format", default="json", help="Output format")
|
|
41
62
|
@click.option("--output", help="Output file")
|
|
42
63
|
@click.option("--fields", help="Comma-separated field names")
|
|
43
64
|
@click.pass_context
|
|
44
|
-
def get(ctx,
|
|
65
|
+
def get(ctx, logins, archived, limit, fetch_all, output_format, output, fields):
|
|
45
66
|
"""Get agency clients"""
|
|
46
67
|
try:
|
|
47
68
|
client = create_client(
|
|
@@ -52,14 +73,11 @@ def get(ctx, ids, limit, fetch_all, output_format, output, fields):
|
|
|
52
73
|
|
|
53
74
|
field_names = fields.split(",") if fields else get_default_fields("clients")
|
|
54
75
|
|
|
55
|
-
criteria = {}
|
|
56
|
-
if
|
|
57
|
-
criteria["
|
|
58
|
-
|
|
59
|
-
params = {"FieldNames": field_names}
|
|
76
|
+
criteria = {"Archived": archived}
|
|
77
|
+
if logins:
|
|
78
|
+
criteria["Logins"] = [login.strip() for login in logins.split(",")]
|
|
60
79
|
|
|
61
|
-
|
|
62
|
-
params["SelectionCriteria"] = criteria
|
|
80
|
+
params = {"SelectionCriteria": criteria, "FieldNames": field_names}
|
|
63
81
|
|
|
64
82
|
if limit:
|
|
65
83
|
params["Page"] = {"Limit": limit}
|
|
@@ -11,12 +11,22 @@ import json
|
|
|
11
11
|
import re
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
from .utils import get_docs_pages
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
_REPORTS_DOCS_PAGES = get_docs_pages("reports")
|
|
18
|
+
_REQUIRED_DOCS_KEYS = ("type", "period", "fields-list", "headers")
|
|
19
|
+
_missing_docs_keys = [k for k in _REQUIRED_DOCS_KEYS if k not in _REPORTS_DOCS_PAGES]
|
|
20
|
+
if _missing_docs_keys:
|
|
21
|
+
raise RuntimeError(
|
|
22
|
+
"reports docs_pages mapping missing required keys "
|
|
23
|
+
f"{_missing_docs_keys}; vendored resource_mapping.py is out of sync"
|
|
24
|
+
)
|
|
15
25
|
REPORTS_SPEC_URLS: dict[str, str] = {
|
|
16
|
-
"type": "
|
|
17
|
-
"spec": "
|
|
18
|
-
"fields-list": "
|
|
19
|
-
"headers": "
|
|
26
|
+
"type": _REPORTS_DOCS_PAGES["type"],
|
|
27
|
+
"spec": _REPORTS_DOCS_PAGES["period"],
|
|
28
|
+
"fields-list": _REPORTS_DOCS_PAGES["fields-list"],
|
|
29
|
+
"headers": _REPORTS_DOCS_PAGES["headers"],
|
|
20
30
|
}
|
|
21
31
|
|
|
22
32
|
REPORTS_CACHE_DIR = Path(__file__).resolve().parent.parent / "tests" / "reports_cache"
|
|
@@ -296,3 +296,10 @@ def get_docs_url(service: str) -> Optional[str]:
|
|
|
296
296
|
"""Return documentation URL for a service from tapi resource mapping."""
|
|
297
297
|
entry = RESOURCE_MAPPING_V5.get(service)
|
|
298
298
|
return entry.get("docs") if entry else None
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def get_docs_pages(service: str) -> Dict[str, str]:
|
|
302
|
+
"""Return documentation page URLs for a service from tapi resource mapping."""
|
|
303
|
+
entry = RESOURCE_MAPPING_V5.get(service)
|
|
304
|
+
docs_pages = entry.get("docs_pages") if entry else None
|
|
305
|
+
return dict(docs_pages) if docs_pages else {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: direct-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.11
|
|
4
4
|
Summary: Command-line interface for Yandex Direct API
|
|
5
5
|
Author: axisrow
|
|
6
6
|
License: MIT
|
|
@@ -97,6 +97,23 @@ Notes:
|
|
|
97
97
|
- Authorization is performed via `direct auth login`.
|
|
98
98
|
- Alias `auth_login` is not supported.
|
|
99
99
|
|
|
100
|
+
Credential resolution priority:
|
|
101
|
+
|
|
102
|
+
| Priority | Source | Example |
|
|
103
|
+
|----------|--------|---------|
|
|
104
|
+
| 1 | Explicit CLI options | `direct --token TOKEN --login LOGIN campaigns get` |
|
|
105
|
+
| 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
|
|
106
|
+
| 3 | Profile-specific env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
|
|
107
|
+
| 4 | Base env vars or project `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
|
|
108
|
+
| 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
|
|
109
|
+
| 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
|
|
110
|
+
|
|
111
|
+
The project `.env` file is loaded automatically. If a profile is selected
|
|
112
|
+
with `--profile` or `direct auth use --profile NAME`, Direct CLI does not
|
|
113
|
+
fall back to base `YANDEX_DIRECT_LOGIN`; this prevents mixing a profile token
|
|
114
|
+
with a login from the project `.env`. For multi-account setups, prefer OAuth
|
|
115
|
+
profiles or profile-specific env vars instead of base credentials.
|
|
116
|
+
|
|
100
117
|
Install with `pip install direct-cli`, then run commands with `direct`.
|
|
101
118
|
Invoking the deprecated `direct-cli` entrypoint exits with
|
|
102
119
|
`use direct instead of direct-cli`.
|
|
@@ -634,6 +651,45 @@ YANDEX_DIRECT_LOGIN=ваш_логин_на_яндексе
|
|
|
634
651
|
direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
|
|
635
652
|
```
|
|
636
653
|
|
|
654
|
+
Используйте профильные credentials из `.env`:
|
|
655
|
+
|
|
656
|
+
```env
|
|
657
|
+
YANDEX_DIRECT_TOKEN_AGENCY1=token-1
|
|
658
|
+
YANDEX_DIRECT_LOGIN_AGENCY1=client-login-1
|
|
659
|
+
YANDEX_DIRECT_TOKEN_AGENCY2=token-2
|
|
660
|
+
YANDEX_DIRECT_LOGIN_AGENCY2=client-login-2
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
OAuth и profile-команды:
|
|
664
|
+
|
|
665
|
+
```bash
|
|
666
|
+
direct auth login
|
|
667
|
+
direct auth login --profile agency1
|
|
668
|
+
direct auth login --code abc123 --profile agency1
|
|
669
|
+
direct auth login --oauth-token y0_example --profile agency1
|
|
670
|
+
direct auth list
|
|
671
|
+
direct auth use --profile agency1
|
|
672
|
+
direct auth status --profile agency1
|
|
673
|
+
direct --profile agency1 campaigns get
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
Порядок выбора credentials:
|
|
677
|
+
|
|
678
|
+
| Приоритет | Источник | Пример |
|
|
679
|
+
|-----------|----------|--------|
|
|
680
|
+
| 1 | Явные CLI-опции | `direct --token TOKEN --login LOGIN campaigns get` |
|
|
681
|
+
| 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
|
|
682
|
+
| 3 | Профильные env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
|
|
683
|
+
| 4 | Базовые env vars или project `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
|
|
684
|
+
| 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
|
|
685
|
+
| 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
|
|
686
|
+
|
|
687
|
+
Файл `.env` в проекте загружается автоматически. Если профиль выбран через
|
|
688
|
+
`--profile` или `direct auth use --profile NAME`, Direct CLI не подставляет
|
|
689
|
+
base `YANDEX_DIRECT_LOGIN`; это защищает от смешивания токена из профиля с
|
|
690
|
+
логином из project `.env`. Для нескольких аккаунтов используйте OAuth profiles
|
|
691
|
+
или профильные env vars, а не базовые credentials.
|
|
692
|
+
|
|
637
693
|
Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
|
|
638
694
|
через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
|
|
639
695
|
подсказкой `use direct instead of direct-cli`.
|
|
@@ -644,6 +700,7 @@ direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
|
|
|
644
700
|
|-------|----------|
|
|
645
701
|
| `--token` | OAuth-токен доступа к API |
|
|
646
702
|
| `--login` | Direct client login |
|
|
703
|
+
| `--profile` | Имя credential profile |
|
|
647
704
|
| `--sandbox` | Использовать тестовое API (песочница) |
|
|
648
705
|
|
|
649
706
|
### Использование
|
|
@@ -165,9 +165,7 @@ run_test "sitelinks get --ids (env auth)" direct sitelinks get --ids 1
|
|
|
165
165
|
run_test "vcards get --ids (env auth)" direct vcards get --ids 1
|
|
166
166
|
run_test "leads get --turbo-page-ids (env auth)" direct leads get --turbo-page-ids 1 --limit 1
|
|
167
167
|
run_test "clients get (env auth)" direct clients get
|
|
168
|
-
|
|
169
|
-
echo -e " ${CYAN}[BUG #73]${RESET} agencyclients get — требует агентский аккаунт (sandbox)"
|
|
170
|
-
((KNOWN++)) || true
|
|
168
|
+
skip_bug 73 "agencyclients get — требует агентский аккаунт (sandbox)"
|
|
171
169
|
run_test "feeds get --ids (env auth)" direct feeds get --ids 1
|
|
172
170
|
run_test "creatives get (env auth)" direct creatives get
|
|
173
171
|
# businesses requires Ids/Name/Url in SelectionCriteria
|
|
@@ -105,6 +105,20 @@ Sandbox re-check is not useful — this is an account-tier limitation.
|
|
|
105
105
|
Testing strategy: manual-only on an account where DYNAMIC_TEXT_CAMPAIGN and
|
|
106
106
|
SMART_CAMPAIGN are enabled. See `tests/MANUAL_COVERAGE.md`.
|
|
107
107
|
|
|
108
|
+
### Category C — account-permission limited (code 3001)
|
|
109
|
+
|
|
110
|
+
The endpoint is available, but the current sandbox agency account does not
|
|
111
|
+
have rights to create agency clients. This differs from read-only
|
|
112
|
+
`agencyclients get`, which is covered against sandbox.
|
|
113
|
+
|
|
114
|
+
| Scenario | Symptom | Error code | Test class |
|
|
115
|
+
|---|---|---|---|
|
|
116
|
+
| agencyclients add-passport-organization | no rights to create clients | 3001 | manual-only |
|
|
117
|
+
|
|
118
|
+
Testing strategy: keep `agencyclients get` in read-only integration coverage;
|
|
119
|
+
keep agency-client creation manual-only unless a sandbox agency account with
|
|
120
|
+
client-creation rights is available.
|
|
121
|
+
|
|
108
122
|
Originally classified in
|
|
109
123
|
[#28 issuecomment-4275359621](https://github.com/axisrow/direct-cli/issues/28#issuecomment-4275359621)
|
|
110
124
|
and
|
|
@@ -14,9 +14,12 @@ account requirements, or external dependencies.
|
|
|
14
14
|
## Account-Scoped Operations
|
|
15
15
|
|
|
16
16
|
- **agencyclients add/update/delete** — requires an agency-type account.
|
|
17
|
-
Non-agency accounts receive 403.
|
|
17
|
+
Non-agency accounts receive 403. The current sandbox agency account can
|
|
18
|
+
read agency clients, but creation returns error 3001: "No rights to create
|
|
19
|
+
clients".
|
|
18
20
|
- **agencyclients add-passport-organization** — creates a real Passport
|
|
19
|
-
organization linked to the account.
|
|
21
|
+
organization linked to the account. The current sandbox agency account is
|
|
22
|
+
sandbox-limited for this operation with error 3001.
|
|
20
23
|
- **agencyclients add-passport-organization-member** — sends an invitation
|
|
21
24
|
email to an external user.
|
|
22
25
|
|
|
@@ -64,8 +67,8 @@ Live tests skip gracefully when the API returns error 3500.
|
|
|
64
67
|
|---|---|---|
|
|
65
68
|
| ads moderate | Irreversible | Moderate |
|
|
66
69
|
| campaigns/ads suspend/resume (live) | Traffic impact | High |
|
|
67
|
-
| agencyclients add/update/delete | Account type | None (
|
|
68
|
-
| agencyclients add-passport-organization* | External state | Moderate |
|
|
70
|
+
| agencyclients add/update/delete | Account type / sandbox rights (403 or 3001) | None (skip) |
|
|
71
|
+
| agencyclients add-passport-organization* | External state / sandbox rights (3001) | Moderate |
|
|
69
72
|
| bids/keywordbids/bidmodifiers set | Financial | High |
|
|
70
73
|
| dynamicads (all) | Account type (3500) | None (skip) |
|
|
71
74
|
| smartadtargets (all) | Account type (3500) | None (skip) |
|
|
@@ -1119,6 +1119,22 @@ class TestApiCoverage:
|
|
|
1119
1119
|
class TestReportsCoverage:
|
|
1120
1120
|
"""Tests for Reports API spec snapshot and CLI parity."""
|
|
1121
1121
|
|
|
1122
|
+
def test_reports_spec_urls_come_from_resource_mapping(self):
|
|
1123
|
+
"""Reports docs URLs must be derived from the vendored resource mapping."""
|
|
1124
|
+
from direct_cli._vendor.tapi_yandex_direct.resource_mapping import (
|
|
1125
|
+
RESOURCE_MAPPING_V5,
|
|
1126
|
+
)
|
|
1127
|
+
from direct_cli.reports_coverage import REPORTS_SPEC_URLS
|
|
1128
|
+
|
|
1129
|
+
docs_pages = RESOURCE_MAPPING_V5["reports"]["docs_pages"]
|
|
1130
|
+
assert REPORTS_SPEC_URLS == {
|
|
1131
|
+
"type": docs_pages["type"],
|
|
1132
|
+
"spec": docs_pages["period"],
|
|
1133
|
+
"fields-list": docs_pages["fields-list"],
|
|
1134
|
+
"headers": docs_pages["headers"],
|
|
1135
|
+
}
|
|
1136
|
+
assert all(not url.endswith(".html") for url in REPORTS_SPEC_URLS.values())
|
|
1137
|
+
|
|
1122
1138
|
def test_reports_cache_files_exist(self):
|
|
1123
1139
|
"""All 4 raw HTML files and spec.json must be committed."""
|
|
1124
1140
|
from direct_cli.reports_coverage import REPORTS_CACHE_DIR
|
|
@@ -12,8 +12,10 @@ from unittest.mock import patch
|
|
|
12
12
|
|
|
13
13
|
from click.testing import CliRunner
|
|
14
14
|
|
|
15
|
-
from direct_cli.
|
|
15
|
+
from direct_cli._vendor.tapi_yandex_direct.resource_mapping import RESOURCE_MAPPING_V5
|
|
16
16
|
from direct_cli._deprecated import DEPRECATED_ENTRYPOINT_MESSAGE, deprecated_main
|
|
17
|
+
from direct_cli.cli import cli
|
|
18
|
+
from direct_cli.utils import get_docs_url
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
class TestCLI(unittest.TestCase):
|
|
@@ -42,6 +44,7 @@ class TestCLI(unittest.TestCase):
|
|
|
42
44
|
self.assertEqual(result.exit_code, 0)
|
|
43
45
|
self.assertIn("Manage campaigns", result.output)
|
|
44
46
|
self.assertIn("Usage: direct campaigns", result.output)
|
|
47
|
+
self.assertIn(f"Documentation: {get_docs_url('campaigns')}", result.output)
|
|
45
48
|
|
|
46
49
|
def test_adgroups_help(self):
|
|
47
50
|
"""Test adgroups help"""
|
|
@@ -60,6 +63,26 @@ class TestCLI(unittest.TestCase):
|
|
|
60
63
|
result = self.runner.invoke(cli, ["reports", "--help"])
|
|
61
64
|
self.assertEqual(result.exit_code, 0)
|
|
62
65
|
self.assertIn("Generate and manage reports", result.output)
|
|
66
|
+
self.assertIn(f"Documentation: {get_docs_url('reports')}", result.output)
|
|
67
|
+
|
|
68
|
+
def test_registered_mapped_groups_show_docs_url(self):
|
|
69
|
+
"""Registered groups from resource mapping show their documentation URL."""
|
|
70
|
+
mapped_groups = sorted(set(cli.commands) & set(RESOURCE_MAPPING_V5))
|
|
71
|
+
self.assertTrue(mapped_groups)
|
|
72
|
+
for group in mapped_groups:
|
|
73
|
+
with self.subTest(group=group):
|
|
74
|
+
result = self.runner.invoke(cli, [group, "--help"])
|
|
75
|
+
self.assertEqual(result.exit_code, 0)
|
|
76
|
+
self.assertIn(
|
|
77
|
+
f"Documentation: {get_docs_url(group)}",
|
|
78
|
+
result.output,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def test_auth_help_has_no_docs_url(self):
|
|
82
|
+
"""Auth is not a Yandex Direct API resource and has no docs epilog."""
|
|
83
|
+
result = self.runner.invoke(cli, ["auth", "--help"])
|
|
84
|
+
self.assertEqual(result.exit_code, 0)
|
|
85
|
+
self.assertNotIn("Documentation:", result.output)
|
|
63
86
|
|
|
64
87
|
def test_canonical_groups_in_help(self):
|
|
65
88
|
"""Test canonical transport groups"""
|
|
@@ -1378,8 +1378,10 @@ def test_agencyclients_add_builds_notification_from_typed_flags():
|
|
|
1378
1378
|
"Notification": {
|
|
1379
1379
|
"Email": "ops@example.com",
|
|
1380
1380
|
"Lang": "RU",
|
|
1381
|
-
"
|
|
1382
|
-
|
|
1381
|
+
"EmailSubscriptions": [
|
|
1382
|
+
{"Option": "RECEIVE_RECOMMENDATIONS", "Value": "YES"},
|
|
1383
|
+
{"Option": "TRACK_POSITION_CHANGES", "Value": "NO"},
|
|
1384
|
+
],
|
|
1383
1385
|
},
|
|
1384
1386
|
}
|
|
1385
1387
|
body = _dry_run(
|
|
@@ -1584,8 +1586,10 @@ def test_agencyclients_add_passport_organization_payload():
|
|
|
1584
1586
|
"Notification": {
|
|
1585
1587
|
"Email": "ops@example.com",
|
|
1586
1588
|
"Lang": "EN",
|
|
1587
|
-
"
|
|
1588
|
-
|
|
1589
|
+
"EmailSubscriptions": [
|
|
1590
|
+
{"Option": "RECEIVE_RECOMMENDATIONS", "Value": "NO"},
|
|
1591
|
+
{"Option": "TRACK_POSITION_CHANGES", "Value": "YES"},
|
|
1592
|
+
],
|
|
1589
1593
|
},
|
|
1590
1594
|
}
|
|
1591
1595
|
|