direct-cli 0.2.1__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. {direct_cli-0.2.1/direct_cli.egg-info → direct_cli-0.2.2}/PKG-INFO +1 -1
  2. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/ads.py +22 -2
  3. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/utils.py +1 -1
  4. {direct_cli-0.2.1 → direct_cli-0.2.2/direct_cli.egg-info}/PKG-INFO +1 -1
  5. {direct_cli-0.2.1 → direct_cli-0.2.2}/pyproject.toml +1 -1
  6. {direct_cli-0.2.1 → direct_cli-0.2.2}/tests/test_dry_run.py +44 -0
  7. {direct_cli-0.2.1 → direct_cli-0.2.2}/tests/test_integration.py +29 -0
  8. {direct_cli-0.2.1 → direct_cli-0.2.2}/.env.example +0 -0
  9. {direct_cli-0.2.1 → direct_cli-0.2.2}/MANIFEST.in +0 -0
  10. {direct_cli-0.2.1 → direct_cli-0.2.2}/README.md +0 -0
  11. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/__init__.py +0 -0
  12. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/api.py +0 -0
  13. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/auth.py +0 -0
  14. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/cli.py +0 -0
  15. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/__init__.py +0 -0
  16. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/adextensions.py +0 -0
  17. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/adgroups.py +0 -0
  18. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/adimages.py +0 -0
  19. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/agencyclients.py +0 -0
  20. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/audiencetargets.py +0 -0
  21. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/bidmodifiers.py +0 -0
  22. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/bids.py +0 -0
  23. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/businesses.py +0 -0
  24. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/campaigns.py +0 -0
  25. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/changes.py +0 -0
  26. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/clients.py +0 -0
  27. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/creatives.py +0 -0
  28. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/dictionaries.py +0 -0
  29. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/dynamicads.py +0 -0
  30. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/feeds.py +0 -0
  31. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/keywordbids.py +0 -0
  32. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/keywords.py +0 -0
  33. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/keywordsresearch.py +0 -0
  34. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/leads.py +0 -0
  35. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
  36. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/reports.py +0 -0
  37. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/retargeting.py +0 -0
  38. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/sitelinks.py +0 -0
  39. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/smartadtargets.py +0 -0
  40. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/turbopages.py +0 -0
  41. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/commands/vcards.py +0 -0
  42. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli/output.py +0 -0
  43. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli.egg-info/SOURCES.txt +0 -0
  44. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli.egg-info/dependency_links.txt +0 -0
  45. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli.egg-info/entry_points.txt +0 -0
  46. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli.egg-info/requires.txt +0 -0
  47. {direct_cli-0.2.1 → direct_cli-0.2.2}/direct_cli.egg-info/top_level.txt +0 -0
  48. {direct_cli-0.2.1 → direct_cli-0.2.2}/setup.cfg +0 -0
  49. {direct_cli-0.2.1 → direct_cli-0.2.2}/setup.py +0 -0
  50. {direct_cli-0.2.1 → direct_cli-0.2.2}/tests/test_auth_bw.py +0 -0
  51. {direct_cli-0.2.1 → direct_cli-0.2.2}/tests/test_auth_op.py +0 -0
  52. {direct_cli-0.2.1 → direct_cli-0.2.2}/tests/test_cli.py +0 -0
  53. {direct_cli-0.2.1 → direct_cli-0.2.2}/tests/test_comprehensive.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Command-line interface for Yandex Direct API
5
5
  Author: axisrow
6
6
  License: MIT
@@ -24,7 +24,11 @@ def ads():
24
24
  @click.option("--fetch-all", is_flag=True, help="Fetch all pages")
25
25
  @click.option("--format", "output_format", default="json", help="Output format")
26
26
  @click.option("--output", help="Output file")
27
- @click.option("--fields", help="Comma-separated field names")
27
+ @click.option("--fields", help="Comma-separated top-level field names")
28
+ @click.option(
29
+ "--text-ad-fields", help="Comma-separated TextAd field names (e.g. Title,Text,Href)"
30
+ )
31
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
28
32
  @click.pass_context
29
33
  def get(
30
34
  ctx,
@@ -37,6 +41,8 @@ def get(
37
41
  output_format,
38
42
  output,
39
43
  fields,
44
+ text_ad_fields,
45
+ dry_run,
40
46
  ):
41
47
  """Get ads"""
42
48
  try:
@@ -52,6 +58,12 @@ def get(
52
58
  else ["Id", "CampaignId", "AdGroupId", "Status", "State", "Type"]
53
59
  )
54
60
 
61
+ text_ad_field_names = (
62
+ text_ad_fields.split(",")
63
+ if text_ad_fields
64
+ else ["Title", "Title2", "Text", "Href"]
65
+ )
66
+
55
67
  criteria = {}
56
68
  if ids:
57
69
  criteria["Ids"] = parse_ids(ids)
@@ -62,13 +74,21 @@ def get(
62
74
  if status:
63
75
  criteria["Statuses"] = [status]
64
76
 
65
- params = {"SelectionCriteria": criteria, "FieldNames": field_names}
77
+ params = {
78
+ "SelectionCriteria": criteria,
79
+ "FieldNames": field_names,
80
+ "TextAdFieldNames": text_ad_field_names,
81
+ }
66
82
 
67
83
  if limit:
68
84
  params["Page"] = {"Limit": limit}
69
85
 
70
86
  body = {"method": "get", "params": params}
71
87
 
88
+ if dry_run:
89
+ format_output(body, "json", None)
90
+ return
91
+
72
92
  result = client.ads().post(data=body)
73
93
 
74
94
  if fetch_all:
@@ -89,7 +89,7 @@ COMMON_FIELDS = {
89
89
  "ClientInfo",
90
90
  ],
91
91
  "adgroups": ["Id", "Name", "CampaignId", "Status", "Type", "RegionIds"],
92
- "ads": ["Id", "CampaignId", "AdGroupId", "Status", "State", "Type"],
92
+ "ads": ["Id", "CampaignId", "AdGroupId", "Status", "State", "Type", "TextAd"],
93
93
  "keywords": [
94
94
  "Id",
95
95
  "Keyword",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Command-line interface for Yandex Direct API
5
5
  Author: axisrow
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "direct-cli"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "Command-line interface for Yandex Direct API"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -115,6 +115,50 @@ def test_ads_update_extra_json_merges_into_payload():
115
115
  assert ad["TextAd"] == {"Title": "Updated"}
116
116
 
117
117
 
118
+ def test_ads_get_default_fieldnames():
119
+ """Default FieldNames includes basic top-level fields, plus TextAdFieldNames."""
120
+ body = _dry_run("ads", "get", "--campaign-ids", "12345")
121
+ assert body["method"] == "get"
122
+ assert body["params"]["FieldNames"] == [
123
+ "Id",
124
+ "CampaignId",
125
+ "AdGroupId",
126
+ "Status",
127
+ "State",
128
+ "Type",
129
+ ]
130
+ assert body["params"]["TextAdFieldNames"] == ["Title", "Title2", "Text", "Href"]
131
+
132
+
133
+ def test_ads_get_with_fields_overrides_defaults():
134
+ """--fields and --text-ad-fields override the defaults."""
135
+ body = _dry_run(
136
+ "ads",
137
+ "get",
138
+ "--campaign-ids",
139
+ "12345",
140
+ "--fields",
141
+ "Id,State",
142
+ "--text-ad-fields",
143
+ "Title",
144
+ )
145
+ assert body["params"]["FieldNames"] == ["Id", "State"]
146
+ assert body["params"]["TextAdFieldNames"] == ["Title"]
147
+
148
+
149
+ def test_ads_get_with_ids_and_status():
150
+ """Multiple selection criteria are combined correctly."""
151
+ body = _dry_run(
152
+ "ads", "get", "--ids", "1,2,3", "--status", "ACCEPTED", "--limit", "10"
153
+ )
154
+ assert body["params"]["SelectionCriteria"] == {
155
+ "Ids": [1, 2, 3],
156
+ "Statuses": ["ACCEPTED"],
157
+ }
158
+ assert body["params"]["Page"] == {"Limit": 10}
159
+ assert "TextAdFieldNames" in body["params"]
160
+
161
+
118
162
  # ----------------------------------------------------------------------
119
163
  # adgroups
120
164
  # ----------------------------------------------------------------------
@@ -157,6 +157,35 @@ class TestReadOnlyAds(unittest.TestCase):
157
157
  )
158
158
  assert_success(result, "ads get")
159
159
 
160
+ def test_get_ads_returns_textad(self):
161
+ """TEXT_AD ads must include TextAd with Title and Text."""
162
+ if not self.campaign_id:
163
+ self.skipTest("No campaigns found in account")
164
+ result = invoke_get(
165
+ "ads",
166
+ "get",
167
+ "--campaign-ids",
168
+ str(self.campaign_id),
169
+ "--limit",
170
+ "50",
171
+ "--format",
172
+ "json",
173
+ )
174
+ assert_success(result, "ads get (TextAd check)")
175
+ data = json.loads(result.output)
176
+ text_ads = [ad for ad in data if ad.get("Type") == "TEXT_AD"]
177
+ if not text_ads:
178
+ self.skipTest("No TEXT_AD ads found in first 50 results")
179
+ for ad in text_ads:
180
+ self.assertIn(
181
+ "TextAd",
182
+ ad,
183
+ f"TEXT_AD {ad['Id']} missing TextAd — "
184
+ "TextAdFieldNames may not be sent",
185
+ )
186
+ self.assertIn("Title", ad["TextAd"])
187
+ self.assertIn("Text", ad["TextAd"])
188
+
160
189
 
161
190
  @pytest.mark.integration
162
191
  @skip_if_no_token
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes