atlassian-cli 0.10.4__tar.gz → 0.11.0__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.
- {atlassian_cli-0.10.4/src/atlassian_cli.egg-info → atlassian_cli-0.11.0}/PKG-INFO +12 -2
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/README.md +11 -1
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/pyproject.toml +1 -1
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli/confluence.py +87 -9
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0/src/atlassian_cli.egg-info}/PKG-INFO +12 -2
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/tests/test_confluence.py +155 -1
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/LICENSE +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/setup.cfg +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli/__init__.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli/adf.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli/config.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli/hints.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli/http.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli/jira.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli/jira_assets.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli/jira_issues.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli/output.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli/update_check.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli.egg-info/SOURCES.txt +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli.egg-info/dependency_links.txt +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli.egg-info/entry_points.txt +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli.egg-info/requires.txt +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli.egg-info/top_level.txt +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/tests/test_adf.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/tests/test_config.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/tests/test_http.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/tests/test_jira_assets.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/tests/test_jira_issues.py +0 -0
- {atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/tests/test_output.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: atlassian-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Fast CLI tools for Atlassian Cloud (Confluence + Jira) — optimized for AI agents
|
|
5
5
|
Author-email: Alex Fishlock <alex.fishlock@catapult.cx>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -104,9 +104,19 @@ confluence diff 9268920323
|
|
|
104
104
|
confluence sync POL
|
|
105
105
|
confluence sync COMPLY --workers 20 --force
|
|
106
106
|
|
|
107
|
-
#
|
|
107
|
+
# Bulk-render pages as markdown (instead of ADF JSON) — handy for AI ingestion
|
|
108
|
+
confluence sync POL --md # writes pages/POL/<id>.md
|
|
109
|
+
confluence get 9268920323 --md # single page, as markdown
|
|
110
|
+
|
|
111
|
+
# Search local page index by title (instant, no API call)
|
|
108
112
|
confluence search "risk assessment"
|
|
109
113
|
|
|
114
|
+
# Content-level search via Confluence's CQL (hits the live API)
|
|
115
|
+
confluence cql 'text ~ "risk register"'
|
|
116
|
+
confluence cql 'text ~ "risk"' --space POL --limit 10
|
|
117
|
+
confluence cql 'text ~ "risk"' --md # markdown bullet list with deep links
|
|
118
|
+
confluence --json cql 'text ~ "risk"' # JSON array
|
|
119
|
+
|
|
110
120
|
# Rebuild the page index
|
|
111
121
|
confluence index
|
|
112
122
|
confluence index --space POL --space COMPLY
|
|
@@ -73,9 +73,19 @@ confluence diff 9268920323
|
|
|
73
73
|
confluence sync POL
|
|
74
74
|
confluence sync COMPLY --workers 20 --force
|
|
75
75
|
|
|
76
|
-
#
|
|
76
|
+
# Bulk-render pages as markdown (instead of ADF JSON) — handy for AI ingestion
|
|
77
|
+
confluence sync POL --md # writes pages/POL/<id>.md
|
|
78
|
+
confluence get 9268920323 --md # single page, as markdown
|
|
79
|
+
|
|
80
|
+
# Search local page index by title (instant, no API call)
|
|
77
81
|
confluence search "risk assessment"
|
|
78
82
|
|
|
83
|
+
# Content-level search via Confluence's CQL (hits the live API)
|
|
84
|
+
confluence cql 'text ~ "risk register"'
|
|
85
|
+
confluence cql 'text ~ "risk"' --space POL --limit 10
|
|
86
|
+
confluence cql 'text ~ "risk"' --md # markdown bullet list with deep links
|
|
87
|
+
confluence --json cql 'text ~ "risk"' # JSON array
|
|
88
|
+
|
|
79
89
|
# Rebuild the page index
|
|
80
90
|
confluence index
|
|
81
91
|
confluence index --space POL --space COMPLY
|
|
@@ -251,15 +251,29 @@ def _ver_ts(page):
|
|
|
251
251
|
return v.get('createdAt', '') if isinstance(v, dict) else ''
|
|
252
252
|
|
|
253
253
|
|
|
254
|
-
def save_page(page_data, space_key, pages_dir):
|
|
254
|
+
def save_page(page_data, space_key, pages_dir, *, output_format='adf'):
|
|
255
|
+
"""Save a fetched page to ``pages_dir/<SPACE>/``.
|
|
256
|
+
|
|
257
|
+
output_format:
|
|
258
|
+
- 'adf' (default): write <id>.json containing the ADF body.
|
|
259
|
+
- 'md': write <id>.md containing the markdown rendering.
|
|
260
|
+
The <id>.meta.json sidecar is always written.
|
|
261
|
+
"""
|
|
255
262
|
page_id = page_data['id']
|
|
256
263
|
space_dir = os.path.join(pages_dir, space_key)
|
|
257
264
|
os.makedirs(space_dir, exist_ok=True)
|
|
258
265
|
|
|
259
266
|
body = page_data.get('body', {}).get('atlas_doc_format', {}).get('value', {})
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
267
|
+
|
|
268
|
+
if output_format == 'md':
|
|
269
|
+
from atlassian_cli.adf import adf_to_markdown
|
|
270
|
+
body_path = os.path.join(space_dir, f'{page_id}.md')
|
|
271
|
+
with open(body_path, 'w') as f:
|
|
272
|
+
f.write(adf_to_markdown(body))
|
|
273
|
+
else:
|
|
274
|
+
body_path = os.path.join(space_dir, f'{page_id}.json')
|
|
275
|
+
with open(body_path, 'w') as f:
|
|
276
|
+
json.dump(body, f, indent=2)
|
|
263
277
|
|
|
264
278
|
meta = {
|
|
265
279
|
'id': page_id,
|
|
@@ -274,7 +288,7 @@ def save_page(page_data, space_key, pages_dir):
|
|
|
274
288
|
with open(meta_path, 'w') as f:
|
|
275
289
|
json.dump(meta, f, indent=2)
|
|
276
290
|
|
|
277
|
-
return
|
|
291
|
+
return body_path, meta_path
|
|
278
292
|
|
|
279
293
|
|
|
280
294
|
def _find_page_file(page_id, pages_dir, suffix):
|
|
@@ -312,8 +326,9 @@ def cmd_get(args):
|
|
|
312
326
|
page = get_page(session, base, args.page_id)
|
|
313
327
|
space = get_space(session, base, space_id=page['spaceId'])
|
|
314
328
|
space_key = space.get('key', str(page['spaceId']))
|
|
315
|
-
|
|
316
|
-
|
|
329
|
+
output_format = 'md' if getattr(args, 'md', False) else 'adf'
|
|
330
|
+
body_path, _ = save_page(page, space_key, args.dir, output_format=output_format)
|
|
331
|
+
emit('OK', f'{page["title"]} (v{_ver(page)}) -> {body_path}')
|
|
317
332
|
|
|
318
333
|
|
|
319
334
|
def cmd_create(args):
|
|
@@ -709,12 +724,14 @@ def cmd_sync(args):
|
|
|
709
724
|
|
|
710
725
|
to_fetch = []
|
|
711
726
|
skipped = 0
|
|
727
|
+
body_suffix = '.md' if getattr(args, 'md', False) else '.json'
|
|
712
728
|
for page in pages:
|
|
713
729
|
page_id = page['id']
|
|
714
730
|
remote_ver = _ver(page)
|
|
715
731
|
if not args.force:
|
|
716
732
|
meta = load_meta(page_id, args.dir)
|
|
717
|
-
|
|
733
|
+
body_file = os.path.join(args.dir, space_key, f'{page_id}{body_suffix}')
|
|
734
|
+
if meta and meta.get('version', 0) >= remote_ver and os.path.isfile(body_file):
|
|
718
735
|
skipped += 1
|
|
719
736
|
continue
|
|
720
737
|
to_fetch.append(page)
|
|
@@ -729,13 +746,14 @@ def cmd_sync(args):
|
|
|
729
746
|
print(f'Fetching {len(to_fetch)} pages ({args.workers} workers)…', file=sys.stderr)
|
|
730
747
|
|
|
731
748
|
errors = 0
|
|
749
|
+
output_format = 'md' if getattr(args, 'md', False) else 'adf'
|
|
732
750
|
|
|
733
751
|
def fetch_one(page):
|
|
734
752
|
nonlocal errors
|
|
735
753
|
page_id = page['id']
|
|
736
754
|
try:
|
|
737
755
|
full_page = get_page(session, base, page_id)
|
|
738
|
-
save_page(full_page, space_key, args.dir)
|
|
756
|
+
save_page(full_page, space_key, args.dir, output_format=output_format)
|
|
739
757
|
return f'GET {page_id} {full_page.get("title", "")} (v{_ver(full_page)})'
|
|
740
758
|
except Exception as e:
|
|
741
759
|
errors += 1
|
|
@@ -749,6 +767,57 @@ def cmd_sync(args):
|
|
|
749
767
|
emit('DONE', f'{space_key}: {len(to_fetch)} fetched, {skipped} skipped, {errors} errors')
|
|
750
768
|
|
|
751
769
|
|
|
770
|
+
def cmd_cql(args):
|
|
771
|
+
"""Run a CQL query against the Confluence Search API.
|
|
772
|
+
|
|
773
|
+
Output modes:
|
|
774
|
+
- default: plain-text table (id [space] title)
|
|
775
|
+
- global --json: JSON array of {id, title, space, url, excerpt}
|
|
776
|
+
- --md: markdown bullet list with deep links
|
|
777
|
+
"""
|
|
778
|
+
session, base = setup()
|
|
779
|
+
query = args.query
|
|
780
|
+
if args.space:
|
|
781
|
+
query = f'space = "{args.space}" AND ({query})'
|
|
782
|
+
data = api_get(session, base, f'{V1}/search', cql=query, limit=args.limit)
|
|
783
|
+
raw = data.get('results', [])
|
|
784
|
+
|
|
785
|
+
results = []
|
|
786
|
+
for r in raw:
|
|
787
|
+
content = r.get('content') or {}
|
|
788
|
+
space_key = ''
|
|
789
|
+
if isinstance(content.get('space'), dict):
|
|
790
|
+
space_key = content['space'].get('key', '')
|
|
791
|
+
results.append({
|
|
792
|
+
'id': content.get('id', ''),
|
|
793
|
+
'title': r.get('title') or content.get('title', ''),
|
|
794
|
+
'space': space_key,
|
|
795
|
+
'url': r.get('url', ''),
|
|
796
|
+
'excerpt': r.get('excerpt', ''),
|
|
797
|
+
})
|
|
798
|
+
|
|
799
|
+
if getattr(args, 'md', False):
|
|
800
|
+
if not results:
|
|
801
|
+
print('No results.', file=sys.stderr)
|
|
802
|
+
else:
|
|
803
|
+
for r in results:
|
|
804
|
+
title = r['title'] or '(untitled)'
|
|
805
|
+
space = r['space'] or '?'
|
|
806
|
+
page_id = r['id']
|
|
807
|
+
print(f'- [{title}]({r["url"]}) — {space} ({page_id})')
|
|
808
|
+
return
|
|
809
|
+
|
|
810
|
+
if is_json_mode():
|
|
811
|
+
print(json.dumps(results, indent=2))
|
|
812
|
+
return
|
|
813
|
+
|
|
814
|
+
if not results:
|
|
815
|
+
print('No results.', file=sys.stderr)
|
|
816
|
+
return
|
|
817
|
+
for r in results:
|
|
818
|
+
print(f'{r["id"]} [{r["space"] or "?"}] {r["title"]}')
|
|
819
|
+
|
|
820
|
+
|
|
752
821
|
def cmd_search(args):
|
|
753
822
|
if not os.path.isfile(args.index):
|
|
754
823
|
emit_error(f'Index not found: {args.index}')
|
|
@@ -1297,6 +1366,7 @@ def main():
|
|
|
1297
1366
|
p = sub.add_parser('get', help='Download a page (ADF + metadata)')
|
|
1298
1367
|
p.add_argument('page_id', help='Confluence page ID')
|
|
1299
1368
|
p.add_argument('--dir', default='pages', help='Output directory (default: pages)')
|
|
1369
|
+
p.add_argument('--md', action='store_true', help='Save as markdown (<id>.md) instead of ADF JSON')
|
|
1300
1370
|
p.set_defaults(func=cmd_get)
|
|
1301
1371
|
|
|
1302
1372
|
p = sub.add_parser('create', help='Create a new page')
|
|
@@ -1389,8 +1459,16 @@ def main():
|
|
|
1389
1459
|
p.add_argument('--dir', default='pages', help='Output directory (default: pages)')
|
|
1390
1460
|
p.add_argument('--workers', type=int, default=10, help='Parallel workers (default: 10)')
|
|
1391
1461
|
p.add_argument('--force', action='store_true', help='Re-download all, ignore cache')
|
|
1462
|
+
p.add_argument('--md', action='store_true', help='Render each page as markdown (<id>.md) instead of ADF JSON')
|
|
1392
1463
|
p.set_defaults(func=cmd_sync)
|
|
1393
1464
|
|
|
1465
|
+
p = sub.add_parser('cql', help='Content-level search using CQL (Confluence Query Language)')
|
|
1466
|
+
p.add_argument('query', help='CQL query string, e.g. \'text ~ "risk register"\'')
|
|
1467
|
+
p.add_argument('--space', help='Restrict to a space (prepends space = "<KEY>" AND ... to the query)')
|
|
1468
|
+
p.add_argument('--limit', type=int, default=25, help='Max results (default: 25)')
|
|
1469
|
+
p.add_argument('--md', action='store_true', help='Markdown bullet-list output instead of plain text')
|
|
1470
|
+
p.set_defaults(func=cmd_cql)
|
|
1471
|
+
|
|
1394
1472
|
p = sub.add_parser('search', help='Search local page index')
|
|
1395
1473
|
p.add_argument('query', help='Search term (title or ID)')
|
|
1396
1474
|
p.add_argument('--index', default='page-index.json', help='Index file path')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: atlassian-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Fast CLI tools for Atlassian Cloud (Confluence + Jira) — optimized for AI agents
|
|
5
5
|
Author-email: Alex Fishlock <alex.fishlock@catapult.cx>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -104,9 +104,19 @@ confluence diff 9268920323
|
|
|
104
104
|
confluence sync POL
|
|
105
105
|
confluence sync COMPLY --workers 20 --force
|
|
106
106
|
|
|
107
|
-
#
|
|
107
|
+
# Bulk-render pages as markdown (instead of ADF JSON) — handy for AI ingestion
|
|
108
|
+
confluence sync POL --md # writes pages/POL/<id>.md
|
|
109
|
+
confluence get 9268920323 --md # single page, as markdown
|
|
110
|
+
|
|
111
|
+
# Search local page index by title (instant, no API call)
|
|
108
112
|
confluence search "risk assessment"
|
|
109
113
|
|
|
114
|
+
# Content-level search via Confluence's CQL (hits the live API)
|
|
115
|
+
confluence cql 'text ~ "risk register"'
|
|
116
|
+
confluence cql 'text ~ "risk"' --space POL --limit 10
|
|
117
|
+
confluence cql 'text ~ "risk"' --md # markdown bullet list with deep links
|
|
118
|
+
confluence --json cql 'text ~ "risk"' # JSON array
|
|
119
|
+
|
|
110
120
|
# Rebuild the page index
|
|
111
121
|
confluence index
|
|
112
122
|
confluence index --space POL --space COMPLY
|
|
@@ -17,10 +17,12 @@ from atlassian_cli.confluence import (
|
|
|
17
17
|
cmd_blog_get,
|
|
18
18
|
cmd_blog_list,
|
|
19
19
|
cmd_blog_update,
|
|
20
|
+
cmd_cql,
|
|
20
21
|
cmd_diff,
|
|
21
22
|
cmd_get,
|
|
22
23
|
cmd_index,
|
|
23
24
|
cmd_search,
|
|
25
|
+
cmd_sync,
|
|
24
26
|
create_blogpost,
|
|
25
27
|
get_blogpost,
|
|
26
28
|
get_page,
|
|
@@ -39,6 +41,7 @@ from atlassian_cli.confluence import (
|
|
|
39
41
|
from atlassian_cli.output import set_json_mode
|
|
40
42
|
|
|
41
43
|
BASE = "https://test.atlassian.net"
|
|
44
|
+
V1 = "/wiki/rest/api"
|
|
42
45
|
V2 = "/wiki/api/v2"
|
|
43
46
|
|
|
44
47
|
SAMPLE_PAGE = {
|
|
@@ -165,6 +168,19 @@ class TestSavePage:
|
|
|
165
168
|
assert meta["title"] == "Test Page"
|
|
166
169
|
assert meta["version"] == 3
|
|
167
170
|
|
|
171
|
+
def test_md_format_writes_md_only(self, tmp_path):
|
|
172
|
+
body_path, meta_path = save_page(SAMPLE_PAGE, "TEST", str(tmp_path), output_format="md")
|
|
173
|
+
assert body_path.endswith(".md")
|
|
174
|
+
assert os.path.isfile(body_path)
|
|
175
|
+
assert os.path.isfile(meta_path)
|
|
176
|
+
|
|
177
|
+
with open(body_path) as f:
|
|
178
|
+
md = f.read()
|
|
179
|
+
assert "Hello" in md
|
|
180
|
+
|
|
181
|
+
# No ADF JSON file when --md
|
|
182
|
+
assert not os.path.isfile(os.path.join(str(tmp_path), "TEST", "12345.json"))
|
|
183
|
+
|
|
168
184
|
|
|
169
185
|
class TestLoadMeta:
|
|
170
186
|
def test_loads_existing(self, tmp_path):
|
|
@@ -194,11 +210,22 @@ class TestCmdGet:
|
|
|
194
210
|
responses.GET, f"{BASE}{V2}/spaces/100",
|
|
195
211
|
json=SAMPLE_SPACE,
|
|
196
212
|
)
|
|
197
|
-
cmd_get(Namespace(page_id="12345", dir=str(tmp_path)))
|
|
213
|
+
cmd_get(Namespace(page_id="12345", dir=str(tmp_path), md=False))
|
|
198
214
|
out = capsys.readouterr().out
|
|
199
215
|
assert "Test Page" in out
|
|
200
216
|
assert os.path.isfile(os.path.join(str(tmp_path), "TEST", "12345.json"))
|
|
201
217
|
|
|
218
|
+
@responses.activate
|
|
219
|
+
def test_downloads_page_as_md(self, capsys, tmp_path):
|
|
220
|
+
responses.add(responses.GET, f"{BASE}{V2}/pages/12345", json=SAMPLE_PAGE)
|
|
221
|
+
responses.add(
|
|
222
|
+
responses.GET, f"{BASE}{V2}/spaces/100",
|
|
223
|
+
json=SAMPLE_SPACE,
|
|
224
|
+
)
|
|
225
|
+
cmd_get(Namespace(page_id="12345", dir=str(tmp_path), md=True))
|
|
226
|
+
assert os.path.isfile(os.path.join(str(tmp_path), "TEST", "12345.md"))
|
|
227
|
+
assert not os.path.isfile(os.path.join(str(tmp_path), "TEST", "12345.json"))
|
|
228
|
+
|
|
202
229
|
|
|
203
230
|
class TestCmdDiff:
|
|
204
231
|
@responses.activate
|
|
@@ -589,3 +616,130 @@ class TestUpdateBlogpost:
|
|
|
589
616
|
title="New Title", body="New body")
|
|
590
617
|
assert title == "New Title"
|
|
591
618
|
assert ver == 2
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
class TestCmdSync:
|
|
622
|
+
@responses.activate
|
|
623
|
+
def test_renders_pages_as_md(self, capsys, tmp_path):
|
|
624
|
+
# Space lookup
|
|
625
|
+
responses.add(
|
|
626
|
+
responses.GET, f"{BASE}{V2}/spaces",
|
|
627
|
+
json={"results": [SAMPLE_SPACE]},
|
|
628
|
+
)
|
|
629
|
+
# Page list
|
|
630
|
+
responses.add(
|
|
631
|
+
responses.GET, f"{BASE}{V2}/spaces/100/pages",
|
|
632
|
+
json={"results": [{"id": "12345", "version": {"number": 3}}], "_links": {}},
|
|
633
|
+
)
|
|
634
|
+
# Page fetch
|
|
635
|
+
responses.add(responses.GET, f"{BASE}{V2}/pages/12345", json=SAMPLE_PAGE)
|
|
636
|
+
|
|
637
|
+
cmd_sync(Namespace(
|
|
638
|
+
space_key="TEST",
|
|
639
|
+
dir=str(tmp_path),
|
|
640
|
+
workers=1,
|
|
641
|
+
force=True,
|
|
642
|
+
md=True,
|
|
643
|
+
))
|
|
644
|
+
|
|
645
|
+
assert os.path.isfile(os.path.join(str(tmp_path), "TEST", "12345.md"))
|
|
646
|
+
assert not os.path.isfile(os.path.join(str(tmp_path), "TEST", "12345.json"))
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
SAMPLE_CQL_RESPONSE = {
|
|
650
|
+
"results": [
|
|
651
|
+
{
|
|
652
|
+
"content": {
|
|
653
|
+
"id": "12345",
|
|
654
|
+
"type": "page",
|
|
655
|
+
"title": "Risk Policy",
|
|
656
|
+
"space": {"key": "POL"},
|
|
657
|
+
},
|
|
658
|
+
"title": "Risk Policy",
|
|
659
|
+
"url": "/spaces/POL/pages/12345",
|
|
660
|
+
"excerpt": "Risks are managed via …",
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
"content": {
|
|
664
|
+
"id": "67890",
|
|
665
|
+
"type": "page",
|
|
666
|
+
"title": "Risk Register",
|
|
667
|
+
"space": {"key": "COMPLY"},
|
|
668
|
+
},
|
|
669
|
+
"title": "Risk Register",
|
|
670
|
+
"url": "/spaces/COMPLY/pages/67890",
|
|
671
|
+
"excerpt": "Open risks: …",
|
|
672
|
+
},
|
|
673
|
+
],
|
|
674
|
+
"totalSize": 2,
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
class TestCmdCql:
|
|
679
|
+
@responses.activate
|
|
680
|
+
def test_text_output(self, capsys):
|
|
681
|
+
responses.add(
|
|
682
|
+
responses.GET, f"{BASE}{V1}/search",
|
|
683
|
+
json=SAMPLE_CQL_RESPONSE,
|
|
684
|
+
)
|
|
685
|
+
cmd_cql(Namespace(query='text ~ "risk"', space=None, limit=25, md=False))
|
|
686
|
+
out = capsys.readouterr().out
|
|
687
|
+
assert "Risk Policy" in out
|
|
688
|
+
assert "12345" in out
|
|
689
|
+
assert "POL" in out
|
|
690
|
+
|
|
691
|
+
@responses.activate
|
|
692
|
+
def test_json_output(self, capsys):
|
|
693
|
+
from atlassian_cli.output import set_json_mode
|
|
694
|
+
responses.add(
|
|
695
|
+
responses.GET, f"{BASE}{V1}/search",
|
|
696
|
+
json=SAMPLE_CQL_RESPONSE,
|
|
697
|
+
)
|
|
698
|
+
set_json_mode(True)
|
|
699
|
+
try:
|
|
700
|
+
cmd_cql(Namespace(query='text ~ "risk"', space=None, limit=25, md=False))
|
|
701
|
+
finally:
|
|
702
|
+
set_json_mode(False)
|
|
703
|
+
out = capsys.readouterr().out
|
|
704
|
+
parsed = json.loads(out)
|
|
705
|
+
assert isinstance(parsed, list)
|
|
706
|
+
assert parsed[0]["id"] == "12345"
|
|
707
|
+
assert parsed[0]["space"] == "POL"
|
|
708
|
+
assert parsed[0]["title"] == "Risk Policy"
|
|
709
|
+
|
|
710
|
+
@responses.activate
|
|
711
|
+
def test_md_output(self, capsys):
|
|
712
|
+
responses.add(
|
|
713
|
+
responses.GET, f"{BASE}{V1}/search",
|
|
714
|
+
json=SAMPLE_CQL_RESPONSE,
|
|
715
|
+
)
|
|
716
|
+
cmd_cql(Namespace(query='text ~ "risk"', space=None, limit=25, md=True))
|
|
717
|
+
out = capsys.readouterr().out
|
|
718
|
+
assert "Risk Policy" in out
|
|
719
|
+
assert "- " in out
|
|
720
|
+
assert "12345" in out
|
|
721
|
+
|
|
722
|
+
@responses.activate
|
|
723
|
+
def test_space_filter_prepends_clause(self):
|
|
724
|
+
captured = {}
|
|
725
|
+
|
|
726
|
+
def callback(request):
|
|
727
|
+
captured["url"] = request.url
|
|
728
|
+
return (200, {}, json.dumps(SAMPLE_CQL_RESPONSE))
|
|
729
|
+
|
|
730
|
+
responses.add_callback(
|
|
731
|
+
responses.GET, f"{BASE}{V1}/search", callback=callback,
|
|
732
|
+
)
|
|
733
|
+
cmd_cql(Namespace(query='text ~ "risk"', space="POL", limit=25, md=False))
|
|
734
|
+
# The space clause should be present in the URL-encoded CQL
|
|
735
|
+
assert 'space+%3D+%22POL%22' in captured["url"] or 'space%3D%22POL%22' in captured["url"]
|
|
736
|
+
|
|
737
|
+
@responses.activate
|
|
738
|
+
def test_no_results(self, capsys):
|
|
739
|
+
responses.add(
|
|
740
|
+
responses.GET, f"{BASE}{V1}/search",
|
|
741
|
+
json={"results": [], "totalSize": 0},
|
|
742
|
+
)
|
|
743
|
+
cmd_cql(Namespace(query='text ~ "nothing"', space=None, limit=25, md=False))
|
|
744
|
+
err = capsys.readouterr().err
|
|
745
|
+
assert "No results" in err
|
|
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
|
{atlassian_cli-0.10.4 → atlassian_cli-0.11.0}/src/atlassian_cli.egg-info/dependency_links.txt
RENAMED
|
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
|