direct-cli 0.3.5__tar.gz → 0.3.6__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.3.5 → direct_cli-0.3.6}/PKG-INFO +7 -7
- {direct_cli-0.3.5 → direct_cli-0.3.6}/README.md +6 -6
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/auth.py +13 -2
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/campaigns.py +106 -1
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli.egg-info/PKG-INFO +7 -7
- {direct_cli-0.3.5 → direct_cli-0.3.6}/pyproject.toml +1 -1
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_auth_oauth.py +97 -7
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_dry_run.py +124 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/.env.example +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/.github/copilot-instructions.md +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/.github/workflows/api-coverage.yml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/.github/workflows/claude-code-review.yml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/.github/workflows/claude.yml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/.github/workflows/quality.yml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/.gitignore +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/AGENTS.md +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/CHANGELOG.md +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/CLAUDE.md +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/MANIFEST.in +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/__init__.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_deprecated.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_smoke_probes.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/__init__.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/endpoints.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/api.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/auth.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/cli.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/__init__.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/adextensions.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/adgroups.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/adimages.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/ads.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/advideos.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/agencyclients.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/audiencetargets.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/balance.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/bidmodifiers.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/bids.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/businesses.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/changes.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/clients.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/creatives.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/dictionaries.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/dynamicads.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/dynamicfeedadtargets.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/feeds.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/keywordbids.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/keywords.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/keywordsresearch.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/leads.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/reports.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/retargeting.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/sitelinks.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/smartadtargets.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/strategies.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/turbopages.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/v4account.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/v4events.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/v4finance.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/v4goals.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/v4shells.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/v4wordstat.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/commands/vcards.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/output.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/reports_coverage.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/smoke_matrix.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/utils.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/v4/__init__.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/v4/money.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/v4_contracts.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/wsdl_coverage.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli.egg-info/SOURCES.txt +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli.egg-info/dependency_links.txt +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli.egg-info/entry_points.txt +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli.egg-info/requires.txt +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli.egg-info/top_level.txt +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/anonymize_cassettes.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/build_api_coverage_checklist.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/build_api_coverage_report.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/check_reports_drift.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/check_wsdl_drift.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/patch_vendor_imports.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/refresh_reports_cache.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/refresh_wsdl_cache.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/release_pypi.sh +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/sandbox_write_live.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/test_dangerous_commands.sh +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/test_safe_commands.sh +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/test_sandbox_write.sh +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/scripts/update_vendor.sh +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/setup.cfg +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/setup.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/API_COVERAGE.md +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/API_ISSUE_AUDIT.md +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/MANUAL_COVERAGE.md +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/__init__.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/api_coverage_payloads.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_adgroups_add_update_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_adimages_add_get_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_ads_add_update_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_advideos_add_get.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_add_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_suspend_resume.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_bids_set.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_campaign_create_get_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_add_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_suspend_resume.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_keywordbids_set.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_add_update_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_suspend_resume.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_sitelinks_add_get_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_add_update_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_suspend_resume.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/conftest.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/fixtures/test-video.mp4 +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/reports_cache/raw/fields-list.html +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/reports_cache/raw/headers.html +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/reports_cache/raw/period.html +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/reports_cache/raw/spec.html +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/reports_cache/raw/type.html +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/reports_cache/spec.json +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_api_coverage.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_auth_bw.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_auth_op.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_balance.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_cli.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_cli_contract.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_comprehensive.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_integration.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_integration_live_write.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_integration_write.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_low_coverage_payloads.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_reports_drift.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_reports_parsing.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_smoke_matrix.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_transport_contract.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_v4_contracts.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_v4_foundation.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_v4_live_contracts.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_v4_safety.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_v4account.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_v4events.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_v4finance_money.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_v4finance_read.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_v4goals.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_v4wordstat.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/test_vendor_imports.py +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/adextensions.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/adgroups.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/adimages.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/ads.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/advideos.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/agencyclients.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/audiencetargets.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/bidmodifiers.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/bids.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/businesses.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/campaigns.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/changes.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/clients.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/creatives.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/dictionaries.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/feeds.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/imports/adextensiontypes.xsd +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/imports/general.xsd +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/imports/generalclients.xsd +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/keywordbids.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/keywords.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/keywordsresearch.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/leads.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/retargetinglists.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/sitelinks.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/smartadtargets.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/strategies.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/tests/wsdl_cache/turbopages.xml +0 -0
- {direct_cli-0.3.5 → direct_cli-0.3.6}/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.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Command-line interface for Yandex Direct API
|
|
5
5
|
Author: axisrow
|
|
6
6
|
License: MIT
|
|
@@ -88,7 +88,7 @@ direct auth login
|
|
|
88
88
|
direct auth login --profile agency1
|
|
89
89
|
direct auth login --profile agency1 --format json
|
|
90
90
|
direct auth login --code abc123 --profile agency1
|
|
91
|
-
printf '%s\n' abc123 | direct auth login --code-
|
|
91
|
+
printf '%s\n' abc123 | direct auth login --code - --profile agency1
|
|
92
92
|
direct auth list
|
|
93
93
|
direct auth use --profile agency1
|
|
94
94
|
direct auth status --profile agency1
|
|
@@ -101,8 +101,8 @@ Notes:
|
|
|
101
101
|
- `--login` remains Direct client login.
|
|
102
102
|
- Authorization is performed via `direct auth login`.
|
|
103
103
|
- OAuth profiles store refresh tokens and refresh access tokens automatically.
|
|
104
|
-
- In a non-interactive shell, run `direct auth login --profile NAME` first, then finish with `direct auth login --code-
|
|
105
|
-
- `direct auth login --code CODE --profile NAME` remains supported for compatibility, but `--code
|
|
104
|
+
- In a non-interactive shell, run `direct auth login --profile NAME` first, then finish with `direct auth login --code - --profile NAME` and pass the browser code on stdin.
|
|
105
|
+
- `direct auth login --code CODE --profile NAME` remains supported for compatibility, but automation should use `--code -` to avoid exposing the code in process arguments.
|
|
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.
|
|
@@ -750,7 +750,7 @@ direct auth login
|
|
|
750
750
|
direct auth login --profile agency1
|
|
751
751
|
direct auth login --profile agency1 --format json
|
|
752
752
|
direct auth login --code abc123 --profile agency1
|
|
753
|
-
printf '%s\n' abc123 | direct auth login --code-
|
|
753
|
+
printf '%s\n' abc123 | direct auth login --code - --profile agency1
|
|
754
754
|
direct auth list
|
|
755
755
|
direct auth use --profile agency1
|
|
756
756
|
direct auth status --profile agency1
|
|
@@ -759,8 +759,8 @@ direct --profile agency1 campaigns get
|
|
|
759
759
|
|
|
760
760
|
Примечания:
|
|
761
761
|
- OAuth profiles сохраняют refresh token и автоматически обновляют access token.
|
|
762
|
-
- В non-interactive shell сначала выполните `direct auth login --profile NAME`, затем завершите через `direct auth login --code-
|
|
763
|
-
- `direct auth login --code CODE --profile NAME` сохраняется для совместимости, но `--code
|
|
762
|
+
- В non-interactive shell сначала выполните `direct auth login --profile NAME`, затем завершите через `direct auth login --code - --profile NAME` и передайте browser code через stdin.
|
|
763
|
+
- `direct auth login --code CODE --profile NAME` сохраняется для совместимости, но автоматизация должна использовать `--code -`, чтобы не раскрывать код в process arguments.
|
|
764
764
|
- Если первый non-interactive шаг включает `--client-secret`, secret запоминается для последующего completion step.
|
|
765
765
|
- Если profile уже хранит confidential OAuth client, `direct auth login --code CODE --profile NAME` использует сохраненные `client_id` и `client_secret`.
|
|
766
766
|
- `direct auth login --oauth-token TOKEN` импортирует access token вручную и не включает auto-refresh.
|
|
@@ -45,7 +45,7 @@ direct auth login
|
|
|
45
45
|
direct auth login --profile agency1
|
|
46
46
|
direct auth login --profile agency1 --format json
|
|
47
47
|
direct auth login --code abc123 --profile agency1
|
|
48
|
-
printf '%s\n' abc123 | direct auth login --code-
|
|
48
|
+
printf '%s\n' abc123 | direct auth login --code - --profile agency1
|
|
49
49
|
direct auth list
|
|
50
50
|
direct auth use --profile agency1
|
|
51
51
|
direct auth status --profile agency1
|
|
@@ -58,8 +58,8 @@ Notes:
|
|
|
58
58
|
- `--login` remains Direct client login.
|
|
59
59
|
- Authorization is performed via `direct auth login`.
|
|
60
60
|
- OAuth profiles store refresh tokens and refresh access tokens automatically.
|
|
61
|
-
- In a non-interactive shell, run `direct auth login --profile NAME` first, then finish with `direct auth login --code-
|
|
62
|
-
- `direct auth login --code CODE --profile NAME` remains supported for compatibility, but `--code
|
|
61
|
+
- In a non-interactive shell, run `direct auth login --profile NAME` first, then finish with `direct auth login --code - --profile NAME` and pass the browser code on stdin.
|
|
62
|
+
- `direct auth login --code CODE --profile NAME` remains supported for compatibility, but automation should use `--code -` to avoid exposing the code in process arguments.
|
|
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.
|
|
@@ -707,7 +707,7 @@ direct auth login
|
|
|
707
707
|
direct auth login --profile agency1
|
|
708
708
|
direct auth login --profile agency1 --format json
|
|
709
709
|
direct auth login --code abc123 --profile agency1
|
|
710
|
-
printf '%s\n' abc123 | direct auth login --code-
|
|
710
|
+
printf '%s\n' abc123 | direct auth login --code - --profile agency1
|
|
711
711
|
direct auth list
|
|
712
712
|
direct auth use --profile agency1
|
|
713
713
|
direct auth status --profile agency1
|
|
@@ -716,8 +716,8 @@ direct --profile agency1 campaigns get
|
|
|
716
716
|
|
|
717
717
|
Примечания:
|
|
718
718
|
- OAuth profiles сохраняют refresh token и автоматически обновляют access token.
|
|
719
|
-
- В non-interactive shell сначала выполните `direct auth login --profile NAME`, затем завершите через `direct auth login --code-
|
|
720
|
-
- `direct auth login --code CODE --profile NAME` сохраняется для совместимости, но `--code
|
|
719
|
+
- В non-interactive shell сначала выполните `direct auth login --profile NAME`, затем завершите через `direct auth login --code - --profile NAME` и передайте browser code через stdin.
|
|
720
|
+
- `direct auth login --code CODE --profile NAME` сохраняется для совместимости, но автоматизация должна использовать `--code -`, чтобы не раскрывать код в process arguments.
|
|
721
721
|
- Если первый non-interactive шаг включает `--client-secret`, secret запоминается для последующего completion step.
|
|
722
722
|
- Если profile уже хранит confidential OAuth client, `direct auth login --code CODE --profile NAME` использует сохраненные `client_id` и `client_secret`.
|
|
723
723
|
- `direct auth login --oauth-token TOKEN` импортирует access token вручную и не включает auto-refresh.
|
|
@@ -35,10 +35,14 @@ def auth():
|
|
|
35
35
|
|
|
36
36
|
@auth.command()
|
|
37
37
|
@click.option("--profile", default="default", show_default=True, help="Profile name")
|
|
38
|
-
@click.option(
|
|
38
|
+
@click.option(
|
|
39
|
+
"--code",
|
|
40
|
+
help="OAuth authorization code; use '-' to read from stdin for automation",
|
|
41
|
+
)
|
|
39
42
|
@click.option(
|
|
40
43
|
"--code-stdin",
|
|
41
44
|
is_flag=True,
|
|
45
|
+
hidden=True,
|
|
42
46
|
help="Read OAuth authorization code from stdin",
|
|
43
47
|
)
|
|
44
48
|
@click.option("--oauth-token", help="Ready OAuth access token")
|
|
@@ -78,9 +82,16 @@ def login(
|
|
|
78
82
|
"--code-stdin cannot be combined with --code, "
|
|
79
83
|
"--oauth-token, or --start-pkce."
|
|
80
84
|
)
|
|
85
|
+
code = "-"
|
|
86
|
+
|
|
87
|
+
if code == "-":
|
|
88
|
+
if oauth_token or start_pkce:
|
|
89
|
+
raise click.ClickException(
|
|
90
|
+
"--code - cannot be combined with --oauth-token or --start-pkce."
|
|
91
|
+
)
|
|
81
92
|
code = sys.stdin.read().strip()
|
|
82
93
|
if not code:
|
|
83
|
-
raise click.ClickException("--code-
|
|
94
|
+
raise click.ClickException("--code - requires a code on stdin.")
|
|
84
95
|
|
|
85
96
|
if start_pkce:
|
|
86
97
|
if code or oauth_token or client_secret:
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Campaigns commands
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
|
|
5
7
|
import click
|
|
6
8
|
|
|
7
9
|
from ..api import create_client
|
|
@@ -13,6 +15,7 @@ from ..utils import (
|
|
|
13
15
|
get_default_fields,
|
|
14
16
|
MICRO_RUBLES,
|
|
15
17
|
parse_ids,
|
|
18
|
+
parse_csv_strings,
|
|
16
19
|
parse_setting_specs,
|
|
17
20
|
)
|
|
18
21
|
|
|
@@ -22,6 +25,14 @@ def campaigns():
|
|
|
22
25
|
"""Manage campaigns"""
|
|
23
26
|
|
|
24
27
|
|
|
28
|
+
def _parse_csv_option(option_name: str, value: Optional[str]) -> Optional[List[str]]:
|
|
29
|
+
"""Parse a CSV option and reject explicitly empty input."""
|
|
30
|
+
parsed = parse_csv_strings(value)
|
|
31
|
+
if value is not None and not parsed:
|
|
32
|
+
raise click.UsageError(f"{option_name} must contain at least one value")
|
|
33
|
+
return parsed
|
|
34
|
+
|
|
35
|
+
|
|
25
36
|
@campaigns.command()
|
|
26
37
|
@click.option("--ids", help="Comma-separated campaign IDs")
|
|
27
38
|
@click.option("--status", help="Filter by status (ACTIVE, SUSPENDED, etc.)")
|
|
@@ -41,6 +52,40 @@ def campaigns():
|
|
|
41
52
|
@click.option(
|
|
42
53
|
"--fields", help="Comma-separated field names (default: all common fields)"
|
|
43
54
|
)
|
|
55
|
+
@click.option("--text-campaign-fields", help="Comma-separated TextCampaignFieldNames")
|
|
56
|
+
@click.option(
|
|
57
|
+
"--text-campaign-search-strategy-placement-types-fields",
|
|
58
|
+
help="Comma-separated TextCampaignSearchStrategyPlacementTypesFieldNames",
|
|
59
|
+
)
|
|
60
|
+
@click.option(
|
|
61
|
+
"--mobile-app-campaign-fields",
|
|
62
|
+
help="Comma-separated MobileAppCampaignFieldNames",
|
|
63
|
+
)
|
|
64
|
+
@click.option(
|
|
65
|
+
"--dynamic-text-campaign-fields",
|
|
66
|
+
help="Comma-separated DynamicTextCampaignFieldNames",
|
|
67
|
+
)
|
|
68
|
+
@click.option(
|
|
69
|
+
"--dynamic-text-campaign-search-strategy-placement-types-fields",
|
|
70
|
+
help="Comma-separated DynamicTextCampaignSearchStrategyPlacementTypesFieldNames",
|
|
71
|
+
)
|
|
72
|
+
@click.option(
|
|
73
|
+
"--cpm-banner-campaign-fields",
|
|
74
|
+
help="Comma-separated CpmBannerCampaignFieldNames",
|
|
75
|
+
)
|
|
76
|
+
@click.option("--smart-campaign-fields", help="Comma-separated SmartCampaignFieldNames")
|
|
77
|
+
@click.option(
|
|
78
|
+
"--unified-campaign-fields",
|
|
79
|
+
help="Comma-separated UnifiedCampaignFieldNames",
|
|
80
|
+
)
|
|
81
|
+
@click.option(
|
|
82
|
+
"--unified-campaign-search-strategy-placement-types-fields",
|
|
83
|
+
help="Comma-separated UnifiedCampaignSearchStrategyPlacementTypesFieldNames",
|
|
84
|
+
)
|
|
85
|
+
@click.option(
|
|
86
|
+
"--unified-campaign-package-bidding-strategy-platforms-fields",
|
|
87
|
+
help="Comma-separated UnifiedCampaignPackageBiddingStrategyPlatformsFieldNames",
|
|
88
|
+
)
|
|
44
89
|
@click.option("--dry-run", is_flag=True, help="Show request without sending")
|
|
45
90
|
@click.pass_context
|
|
46
91
|
def get(
|
|
@@ -56,6 +101,16 @@ def get(
|
|
|
56
101
|
output_format,
|
|
57
102
|
output,
|
|
58
103
|
fields,
|
|
104
|
+
text_campaign_fields,
|
|
105
|
+
text_campaign_search_strategy_placement_types_fields,
|
|
106
|
+
mobile_app_campaign_fields,
|
|
107
|
+
dynamic_text_campaign_fields,
|
|
108
|
+
dynamic_text_campaign_search_strategy_placement_types_fields,
|
|
109
|
+
cpm_banner_campaign_fields,
|
|
110
|
+
smart_campaign_fields,
|
|
111
|
+
unified_campaign_fields,
|
|
112
|
+
unified_campaign_search_strategy_placement_types_fields,
|
|
113
|
+
unified_campaign_package_bidding_strategy_platforms_fields,
|
|
59
114
|
dry_run,
|
|
60
115
|
):
|
|
61
116
|
"""Get campaigns"""
|
|
@@ -70,7 +125,11 @@ def get(
|
|
|
70
125
|
)
|
|
71
126
|
|
|
72
127
|
# Parse field names
|
|
73
|
-
field_names =
|
|
128
|
+
field_names = (
|
|
129
|
+
_parse_csv_option("--fields", fields)
|
|
130
|
+
if fields is not None
|
|
131
|
+
else get_default_fields("campaigns")
|
|
132
|
+
)
|
|
74
133
|
|
|
75
134
|
# Build selection criteria
|
|
76
135
|
criteria = build_selection_criteria(
|
|
@@ -86,6 +145,52 @@ def get(
|
|
|
86
145
|
params = build_common_params(
|
|
87
146
|
criteria=criteria, field_names=field_names, limit=limit
|
|
88
147
|
)
|
|
148
|
+
selector_options = {
|
|
149
|
+
"TextCampaignFieldNames": (
|
|
150
|
+
"--text-campaign-fields",
|
|
151
|
+
text_campaign_fields,
|
|
152
|
+
),
|
|
153
|
+
"TextCampaignSearchStrategyPlacementTypesFieldNames": (
|
|
154
|
+
"--text-campaign-search-strategy-placement-types-fields",
|
|
155
|
+
text_campaign_search_strategy_placement_types_fields,
|
|
156
|
+
),
|
|
157
|
+
"MobileAppCampaignFieldNames": (
|
|
158
|
+
"--mobile-app-campaign-fields",
|
|
159
|
+
mobile_app_campaign_fields,
|
|
160
|
+
),
|
|
161
|
+
"DynamicTextCampaignFieldNames": (
|
|
162
|
+
"--dynamic-text-campaign-fields",
|
|
163
|
+
dynamic_text_campaign_fields,
|
|
164
|
+
),
|
|
165
|
+
"DynamicTextCampaignSearchStrategyPlacementTypesFieldNames": (
|
|
166
|
+
"--dynamic-text-campaign-search-strategy-placement-types-fields",
|
|
167
|
+
dynamic_text_campaign_search_strategy_placement_types_fields,
|
|
168
|
+
),
|
|
169
|
+
"CpmBannerCampaignFieldNames": (
|
|
170
|
+
"--cpm-banner-campaign-fields",
|
|
171
|
+
cpm_banner_campaign_fields,
|
|
172
|
+
),
|
|
173
|
+
"SmartCampaignFieldNames": (
|
|
174
|
+
"--smart-campaign-fields",
|
|
175
|
+
smart_campaign_fields,
|
|
176
|
+
),
|
|
177
|
+
"UnifiedCampaignFieldNames": (
|
|
178
|
+
"--unified-campaign-fields",
|
|
179
|
+
unified_campaign_fields,
|
|
180
|
+
),
|
|
181
|
+
"UnifiedCampaignSearchStrategyPlacementTypesFieldNames": (
|
|
182
|
+
"--unified-campaign-search-strategy-placement-types-fields",
|
|
183
|
+
unified_campaign_search_strategy_placement_types_fields,
|
|
184
|
+
),
|
|
185
|
+
"UnifiedCampaignPackageBiddingStrategyPlatformsFieldNames": (
|
|
186
|
+
"--unified-campaign-package-bidding-strategy-platforms-fields",
|
|
187
|
+
unified_campaign_package_bidding_strategy_platforms_fields,
|
|
188
|
+
),
|
|
189
|
+
}
|
|
190
|
+
for request_key, (option_name, value) in selector_options.items():
|
|
191
|
+
parsed = _parse_csv_option(option_name, value)
|
|
192
|
+
if parsed:
|
|
193
|
+
params[request_key] = parsed
|
|
89
194
|
|
|
90
195
|
body = {"method": "get", "params": params}
|
|
91
196
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: direct-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Command-line interface for Yandex Direct API
|
|
5
5
|
Author: axisrow
|
|
6
6
|
License: MIT
|
|
@@ -88,7 +88,7 @@ direct auth login
|
|
|
88
88
|
direct auth login --profile agency1
|
|
89
89
|
direct auth login --profile agency1 --format json
|
|
90
90
|
direct auth login --code abc123 --profile agency1
|
|
91
|
-
printf '%s\n' abc123 | direct auth login --code-
|
|
91
|
+
printf '%s\n' abc123 | direct auth login --code - --profile agency1
|
|
92
92
|
direct auth list
|
|
93
93
|
direct auth use --profile agency1
|
|
94
94
|
direct auth status --profile agency1
|
|
@@ -101,8 +101,8 @@ Notes:
|
|
|
101
101
|
- `--login` remains Direct client login.
|
|
102
102
|
- Authorization is performed via `direct auth login`.
|
|
103
103
|
- OAuth profiles store refresh tokens and refresh access tokens automatically.
|
|
104
|
-
- In a non-interactive shell, run `direct auth login --profile NAME` first, then finish with `direct auth login --code-
|
|
105
|
-
- `direct auth login --code CODE --profile NAME` remains supported for compatibility, but `--code
|
|
104
|
+
- In a non-interactive shell, run `direct auth login --profile NAME` first, then finish with `direct auth login --code - --profile NAME` and pass the browser code on stdin.
|
|
105
|
+
- `direct auth login --code CODE --profile NAME` remains supported for compatibility, but automation should use `--code -` to avoid exposing the code in process arguments.
|
|
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.
|
|
@@ -750,7 +750,7 @@ direct auth login
|
|
|
750
750
|
direct auth login --profile agency1
|
|
751
751
|
direct auth login --profile agency1 --format json
|
|
752
752
|
direct auth login --code abc123 --profile agency1
|
|
753
|
-
printf '%s\n' abc123 | direct auth login --code-
|
|
753
|
+
printf '%s\n' abc123 | direct auth login --code - --profile agency1
|
|
754
754
|
direct auth list
|
|
755
755
|
direct auth use --profile agency1
|
|
756
756
|
direct auth status --profile agency1
|
|
@@ -759,8 +759,8 @@ direct --profile agency1 campaigns get
|
|
|
759
759
|
|
|
760
760
|
Примечания:
|
|
761
761
|
- OAuth profiles сохраняют refresh token и автоматически обновляют access token.
|
|
762
|
-
- В non-interactive shell сначала выполните `direct auth login --profile NAME`, затем завершите через `direct auth login --code-
|
|
763
|
-
- `direct auth login --code CODE --profile NAME` сохраняется для совместимости, но `--code
|
|
762
|
+
- В non-interactive shell сначала выполните `direct auth login --profile NAME`, затем завершите через `direct auth login --code - --profile NAME` и передайте browser code через stdin.
|
|
763
|
+
- `direct auth login --code CODE --profile NAME` сохраняется для совместимости, но автоматизация должна использовать `--code -`, чтобы не раскрывать код в process arguments.
|
|
764
764
|
- Если первый non-interactive шаг включает `--client-secret`, secret запоминается для последующего completion step.
|
|
765
765
|
- Если profile уже хранит confidential OAuth client, `direct auth login --code CODE --profile NAME` использует сохраненные `client_id` и `client_secret`.
|
|
766
766
|
- `direct auth login --oauth-token TOKEN` импортирует access token вручную и не включает auto-refresh.
|
|
@@ -460,7 +460,7 @@ class TestAuthOAuth:
|
|
|
460
460
|
},
|
|
461
461
|
)
|
|
462
462
|
@patch("direct_cli.commands.auth.time.time", return_value=1000.0)
|
|
463
|
-
def
|
|
463
|
+
def test_auth_login_code_dash_uses_pending_pkce_state(
|
|
464
464
|
self, mock_time, mock_exchange, isolated_auth_store
|
|
465
465
|
):
|
|
466
466
|
save_auth_store(
|
|
@@ -483,7 +483,7 @@ class TestAuthOAuth:
|
|
|
483
483
|
|
|
484
484
|
result = runner.invoke(
|
|
485
485
|
cli,
|
|
486
|
-
["auth", "login", "--profile", "agency1", "--code-
|
|
486
|
+
["auth", "login", "--profile", "agency1", "--code", "-"],
|
|
487
487
|
input="abc123\n",
|
|
488
488
|
)
|
|
489
489
|
|
|
@@ -501,19 +501,108 @@ class TestAuthOAuth:
|
|
|
501
501
|
assert profile["token"] == "y0_pkce"
|
|
502
502
|
assert profile["login"] == "client-login"
|
|
503
503
|
|
|
504
|
-
|
|
504
|
+
@patch(
|
|
505
|
+
"direct_cli.commands.auth.exchange_oauth_code",
|
|
506
|
+
return_value={
|
|
507
|
+
"access_token": "y0_pkce",
|
|
508
|
+
"refresh_token": "r1",
|
|
509
|
+
"expires_in": 3600,
|
|
510
|
+
},
|
|
511
|
+
)
|
|
512
|
+
@patch("direct_cli.commands.auth.time.time", return_value=1000.0)
|
|
513
|
+
def test_auth_login_code_stdin_alias_uses_pending_pkce_state(
|
|
514
|
+
self, mock_time, mock_exchange, isolated_auth_store
|
|
515
|
+
):
|
|
516
|
+
save_auth_store(
|
|
517
|
+
{
|
|
518
|
+
"profiles": {},
|
|
519
|
+
"active_profile": None,
|
|
520
|
+
"pending_pkce": {
|
|
521
|
+
"agency1": {
|
|
522
|
+
"type": "pkce",
|
|
523
|
+
"client_id": "cid",
|
|
524
|
+
"code_verifier": "ver",
|
|
525
|
+
"login": "client-login",
|
|
526
|
+
"created_at": 900.0,
|
|
527
|
+
"expires_at": 1500.0,
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
}
|
|
531
|
+
)
|
|
505
532
|
runner = CliRunner()
|
|
506
533
|
|
|
507
534
|
result = runner.invoke(
|
|
508
535
|
cli,
|
|
509
536
|
["auth", "login", "--profile", "agency1", "--code-stdin"],
|
|
537
|
+
input="abc123\n",
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
assert result.exit_code == 0
|
|
541
|
+
assert "abc123" not in result.output
|
|
542
|
+
mock_exchange.assert_called_once_with(
|
|
543
|
+
code="abc123",
|
|
544
|
+
client_id="cid",
|
|
545
|
+
client_secret=None,
|
|
546
|
+
code_verifier="ver",
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
def test_auth_login_code_dash_requires_input(self, isolated_auth_store):
|
|
550
|
+
runner = CliRunner()
|
|
551
|
+
|
|
552
|
+
result = runner.invoke(
|
|
553
|
+
cli,
|
|
554
|
+
["auth", "login", "--profile", "agency1", "--code", "-"],
|
|
510
555
|
input="\n",
|
|
511
556
|
)
|
|
512
557
|
|
|
513
558
|
assert result.exit_code != 0
|
|
514
|
-
assert "--code-
|
|
559
|
+
assert "--code - requires a code on stdin" in result.output
|
|
515
560
|
|
|
516
|
-
def
|
|
561
|
+
def test_auth_login_code_dash_conflicts_before_reading_stdin(
|
|
562
|
+
self, isolated_auth_store
|
|
563
|
+
):
|
|
564
|
+
runner = CliRunner()
|
|
565
|
+
|
|
566
|
+
result = runner.invoke(
|
|
567
|
+
cli,
|
|
568
|
+
[
|
|
569
|
+
"auth",
|
|
570
|
+
"login",
|
|
571
|
+
"--profile",
|
|
572
|
+
"agency1",
|
|
573
|
+
"--code",
|
|
574
|
+
"-",
|
|
575
|
+
"--start-pkce",
|
|
576
|
+
],
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
assert result.exit_code != 0
|
|
580
|
+
assert "--code - cannot be combined" in result.output
|
|
581
|
+
|
|
582
|
+
def test_auth_login_code_dash_oauth_token_conflict_before_reading_stdin(
|
|
583
|
+
self, isolated_auth_store
|
|
584
|
+
):
|
|
585
|
+
runner = CliRunner()
|
|
586
|
+
|
|
587
|
+
result = runner.invoke(
|
|
588
|
+
cli,
|
|
589
|
+
[
|
|
590
|
+
"auth",
|
|
591
|
+
"login",
|
|
592
|
+
"--profile",
|
|
593
|
+
"agency1",
|
|
594
|
+
"--code",
|
|
595
|
+
"-",
|
|
596
|
+
"--oauth-token",
|
|
597
|
+
"token",
|
|
598
|
+
],
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
assert result.exit_code != 0
|
|
602
|
+
assert "--code - cannot be combined" in result.output
|
|
603
|
+
assert "--code - requires a code on stdin" not in result.output
|
|
604
|
+
|
|
605
|
+
def test_auth_login_code_stdin_alias_conflicts_with_code(self, isolated_auth_store):
|
|
517
606
|
runner = CliRunner()
|
|
518
607
|
|
|
519
608
|
result = runner.invoke(
|
|
@@ -542,7 +631,7 @@ class TestAuthOAuth:
|
|
|
542
631
|
},
|
|
543
632
|
)
|
|
544
633
|
@patch("direct_cli.commands.auth.time.time", return_value=1000.0)
|
|
545
|
-
def
|
|
634
|
+
def test_auth_login_code_dash_custom_app(
|
|
546
635
|
self, mock_time, mock_exchange, isolated_auth_store
|
|
547
636
|
):
|
|
548
637
|
runner = CliRunner()
|
|
@@ -554,7 +643,8 @@ class TestAuthOAuth:
|
|
|
554
643
|
"login",
|
|
555
644
|
"--profile",
|
|
556
645
|
"agency1",
|
|
557
|
-
"--code
|
|
646
|
+
"--code",
|
|
647
|
+
"-",
|
|
558
648
|
"--client-id",
|
|
559
649
|
"cid",
|
|
560
650
|
"--client-secret",
|
|
@@ -326,6 +326,130 @@ def test_advideos_get_ids_required():
|
|
|
326
326
|
assert "Missing option '--ids'" in result.output
|
|
327
327
|
|
|
328
328
|
|
|
329
|
+
def test_campaigns_get_text_campaign_fields_dry_run():
|
|
330
|
+
body = _read_dry_run(
|
|
331
|
+
"campaigns",
|
|
332
|
+
"get",
|
|
333
|
+
"--fields",
|
|
334
|
+
"Id,Name,State",
|
|
335
|
+
"--text-campaign-fields",
|
|
336
|
+
"BiddingStrategy",
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
assert body["params"]["FieldNames"] == ["Id", "Name", "State"]
|
|
340
|
+
assert body["params"]["TextCampaignFieldNames"] == ["BiddingStrategy"]
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def test_campaigns_get_campaign_specific_fields_dry_run():
|
|
344
|
+
body = _read_dry_run(
|
|
345
|
+
"campaigns",
|
|
346
|
+
"get",
|
|
347
|
+
"--text-campaign-fields",
|
|
348
|
+
"BiddingStrategy,PriorityGoals",
|
|
349
|
+
"--mobile-app-campaign-fields",
|
|
350
|
+
"Settings,BiddingStrategy",
|
|
351
|
+
"--dynamic-text-campaign-fields",
|
|
352
|
+
"BiddingStrategy,Settings",
|
|
353
|
+
"--cpm-banner-campaign-fields",
|
|
354
|
+
"BiddingStrategy,Settings",
|
|
355
|
+
"--smart-campaign-fields",
|
|
356
|
+
"BiddingStrategy,Settings",
|
|
357
|
+
"--unified-campaign-fields",
|
|
358
|
+
"BiddingStrategy,PriorityGoals",
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
params = body["params"]
|
|
362
|
+
assert params["TextCampaignFieldNames"] == ["BiddingStrategy", "PriorityGoals"]
|
|
363
|
+
assert params["MobileAppCampaignFieldNames"] == ["Settings", "BiddingStrategy"]
|
|
364
|
+
assert params["DynamicTextCampaignFieldNames"] == ["BiddingStrategy", "Settings"]
|
|
365
|
+
assert params["CpmBannerCampaignFieldNames"] == ["BiddingStrategy", "Settings"]
|
|
366
|
+
assert params["SmartCampaignFieldNames"] == ["BiddingStrategy", "Settings"]
|
|
367
|
+
assert params["UnifiedCampaignFieldNames"] == ["BiddingStrategy", "PriorityGoals"]
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def test_campaigns_get_strategy_placement_fields_dry_run():
|
|
371
|
+
body = _read_dry_run(
|
|
372
|
+
"campaigns",
|
|
373
|
+
"get",
|
|
374
|
+
"--text-campaign-search-strategy-placement-types-fields",
|
|
375
|
+
"SearchResults,ProductGallery",
|
|
376
|
+
"--dynamic-text-campaign-search-strategy-placement-types-fields",
|
|
377
|
+
"SearchResults,DynamicPlaces",
|
|
378
|
+
"--unified-campaign-search-strategy-placement-types-fields",
|
|
379
|
+
"SearchResults,Maps,SearchOrganizationList",
|
|
380
|
+
"--unified-campaign-package-bidding-strategy-platforms-fields",
|
|
381
|
+
"SearchResult,Network",
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
params = body["params"]
|
|
385
|
+
assert params["TextCampaignSearchStrategyPlacementTypesFieldNames"] == [
|
|
386
|
+
"SearchResults",
|
|
387
|
+
"ProductGallery",
|
|
388
|
+
]
|
|
389
|
+
assert params["DynamicTextCampaignSearchStrategyPlacementTypesFieldNames"] == [
|
|
390
|
+
"SearchResults",
|
|
391
|
+
"DynamicPlaces",
|
|
392
|
+
]
|
|
393
|
+
assert params["UnifiedCampaignSearchStrategyPlacementTypesFieldNames"] == [
|
|
394
|
+
"SearchResults",
|
|
395
|
+
"Maps",
|
|
396
|
+
"SearchOrganizationList",
|
|
397
|
+
]
|
|
398
|
+
assert params["UnifiedCampaignPackageBiddingStrategyPlatformsFieldNames"] == [
|
|
399
|
+
"SearchResult",
|
|
400
|
+
"Network",
|
|
401
|
+
]
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def test_campaigns_get_omits_campaign_specific_fields_by_default():
|
|
405
|
+
body = _read_dry_run("campaigns", "get", "--fields", "Id,Name,State")
|
|
406
|
+
|
|
407
|
+
omitted_keys = {
|
|
408
|
+
"TextCampaignFieldNames",
|
|
409
|
+
"TextCampaignSearchStrategyPlacementTypesFieldNames",
|
|
410
|
+
"MobileAppCampaignFieldNames",
|
|
411
|
+
"DynamicTextCampaignFieldNames",
|
|
412
|
+
"DynamicTextCampaignSearchStrategyPlacementTypesFieldNames",
|
|
413
|
+
"CpmBannerCampaignFieldNames",
|
|
414
|
+
"SmartCampaignFieldNames",
|
|
415
|
+
"UnifiedCampaignFieldNames",
|
|
416
|
+
"UnifiedCampaignSearchStrategyPlacementTypesFieldNames",
|
|
417
|
+
"UnifiedCampaignPackageBiddingStrategyPlatformsFieldNames",
|
|
418
|
+
}
|
|
419
|
+
assert body["params"]["FieldNames"] == ["Id", "Name", "State"]
|
|
420
|
+
assert omitted_keys.isdisjoint(body["params"])
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def test_campaigns_get_rejects_empty_fields_csv():
|
|
424
|
+
result = CliRunner().invoke(
|
|
425
|
+
cli,
|
|
426
|
+
["campaigns", "get", "--fields", ",", "--dry-run"],
|
|
427
|
+
env={"YANDEX_DIRECT_TOKEN": "test-token", "YANDEX_DIRECT_LOGIN": ""},
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
assert result.exit_code != 0
|
|
431
|
+
assert "--fields must contain at least one value" in result.output
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def test_campaigns_get_rejects_empty_campaign_specific_fields_csv():
|
|
435
|
+
result = CliRunner().invoke(
|
|
436
|
+
cli,
|
|
437
|
+
[
|
|
438
|
+
"campaigns",
|
|
439
|
+
"get",
|
|
440
|
+
"--fields",
|
|
441
|
+
"Id",
|
|
442
|
+
"--text-campaign-fields",
|
|
443
|
+
",",
|
|
444
|
+
"--dry-run",
|
|
445
|
+
],
|
|
446
|
+
env={"YANDEX_DIRECT_TOKEN": "test-token", "YANDEX_DIRECT_LOGIN": ""},
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
assert result.exit_code != 0
|
|
450
|
+
assert "--text-campaign-fields must contain at least one value" in result.output
|
|
451
|
+
|
|
452
|
+
|
|
329
453
|
def _reports_get_result(*extra_args: str) -> Result:
|
|
330
454
|
return CliRunner().invoke(
|
|
331
455
|
cli,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py
RENAMED
|
File without changes
|
{direct_cli-0.3.5 → direct_cli-0.3.6}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py
RENAMED
|
File without changes
|