Skip to content

Commit 251cc07

Browse files
authored
[1.0.3] add trim query option (#1)
* [feature] add trim_query option for every API class * [docs] add trim_query option description to readme * [test] add test cases with query * [CI] add test * [CI] change python test versions * [CI] remove python 3.10 test * [CI] add publish workflow * [1.0.3] update changelog and setup for version 1.0.3
1 parent e1b697b commit 251cc07

File tree

10 files changed

+169
-18
lines changed

10 files changed

+169
-18
lines changed

.github/workflows/python-publish.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# This workflow will upload a Python Package using Twine when a release is created
2+
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
3+
4+
# This workflow uses actions that are not certified by GitHub.
5+
# They are provided by a third-party and are governed by
6+
# separate terms of service, privacy policy, and support
7+
# documentation.
8+
9+
name: Upload Python Package
10+
11+
on:
12+
release:
13+
types: [published]
14+
workflow_run:
15+
workflows: [Python package]
16+
types: [completed]
17+
18+
permissions:
19+
contents: read
20+
21+
jobs:
22+
on-success:
23+
runs-on: ubuntu-latest
24+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
25+
steps:
26+
- run: echo 'The test workflow passed'
27+
- uses: actions/checkout@v3
28+
- name: Set up Python
29+
uses: actions/setup-python@v3
30+
with:
31+
python-version: '3.x'
32+
- name: Install dependencies
33+
run: |
34+
python -m pip install --upgrade pip
35+
pip install build
36+
- name: Build package
37+
run: python -m build
38+
- name: Publish package
39+
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
40+
with:
41+
user: __token__
42+
password: ${{ secrets.PYPI_TOKEN }}
43+
44+
on-failure:
45+
runs-on: ubuntu-latest
46+
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
47+
steps:
48+
- run: echo 'The test workflow failed'

.github/workflows/python-test.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Python package
2+
3+
on: [push]
4+
5+
jobs:
6+
build:
7+
8+
runs-on: ubuntu-latest
9+
strategy:
10+
matrix:
11+
python-version: ["3.8", "3.9"]
12+
13+
steps:
14+
- uses: actions/checkout@v3
15+
- name: Set up Python ${{ matrix.python-version }}
16+
uses: actions/setup-python@v3
17+
with:
18+
python-version: ${{ matrix.python-version }}
19+
- name: Install dependencies
20+
run: |
21+
python -m pip install --upgrade pip
22+
pip3 install .
23+
pip3 install --upgrade foliantcontrib.test_framework
24+
- name: Test with unittest
25+
run: |
26+
python3 -m unittest discover

README.md

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ The list of options and some default values differ for each mode.
312312
`endpoint_prefix`
313313
: *(optional)* The endpoint prefix from the server root to API methods. If is stated — APIReferences can divide the command in the reference and search for it more accurately. Also, you could use it in templates. More info in the **Commands and Endpoint Prefixes** section. Default: `''`
314314

315+
`trim_query`
316+
: *(optional)* Cut a query part after `?` character from command while searching for an API link. Default: `True`
317+
315318
**`find_by_anchor` mode**
316319

317320
`url`
@@ -335,6 +338,9 @@ The list of options and some default values differ for each mode.
335338
`password`
336339
: *(optional)* Password for basic authentication if present on your API site.
337340

341+
`trim_query`
342+
: *(optional)* Cut a query part after `?` character from command while searching for an API link. Default: `True`
343+
338344
**`find_by_tag_content` mode**
339345

340346
`url`
@@ -355,6 +361,9 @@ The list of options and some default values differ for each mode.
355361
`password`
356362
: *(optional)* Password for basic authentication if present on your API site.
357363

364+
`trim_query`
365+
: *(optional)* Cut a query part after `?` character from command while searching for an API link. Default: `True`
366+
358367
**`find_for_swagger` mode**
359368

360369
`url`
@@ -375,6 +384,9 @@ The list of options and some default values differ for each mode.
375384
`password`
376385
: *(optional)* Password for basic authentication if present on your API site.
377386

387+
`trim_query`
388+
: *(optional)* Cut a query part after `?` character from command while searching for an API link. Default: `True`
389+
378390
**`find_for_redoc` mode**
379391

380392
`url`
@@ -395,6 +407,9 @@ The list of options and some default values differ for each mode.
395407
`password`
396408
: *(optional)* Password for basic authentication if present on your API site.
397409

410+
`trim_query`
411+
: *(optional)* Cut a query part after `?` character from command while searching for an API link. Default: `True`
412+
398413
# User guide
399414

400415
The purpose of APIReferences is to convert *references* into Markdown links.
@@ -899,22 +914,22 @@ preprocessors:
899914

900915
With the default template, the reference string will be replaced by something like that:
901916

902-
```
917+
```md
903918
[GET user/info](http://example.com/api/#get-user-info)
904919
```
905920

906921
If you want references to be transformed into something else, create your own template. You can use placeholders from the reference regular expression along with some additional:
907922

908-
placeholder | description | example
909-
-------- | ----------- | -------
910-
source | Full original reference string | \``Client-API: GET user/info`\`
911-
url | Full url to the method description | `http://example.com/api/#get-user-info`
912-
endpoint_prefix | API endpoint prefix from API configuration | `/api/v2`
923+
| placeholder | description | example |
924+
|-----------------|--------------------------------------------|-----------------------------------------|
925+
| source | Full original reference string | \``Client-API: GET user/info`\` |
926+
| url | Full url to the method description | `http://example.com/api/#get-user-info` |
927+
| endpoint_prefix | API endpoint prefix from API configuration | `/api/v2` |
913928

914929
Placeholders from the default regex are:
915930

916-
placeholder | description | example
917-
-------- | ----------- | -------
918-
prefix | API Prefix used in the reference | `Client-API`
919-
verb | HTTP verb used in the reference | `GET`
920-
command | API command being referenced with endpoint prefix removed | `/user/info`
931+
| placeholder | description | example |
932+
|-------------|-----------------------------------------------------------|--------------|
933+
| prefix | API Prefix used in the reference | `Client-API` |
934+
| verb | HTTP verb used in the reference | `GET` |
935+
| command | API command being referenced with endpoint prefix removed | `/user/info` |

changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.0.3
2+
3+
- New: `trim_query` option (default: True) for every API class
4+
15
# 1.0.2
26

37
- New utils module.

foliant/preprocessors/apireferences/classes.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,13 @@ def rel_command(self):
131131
"""
132132
return self.command.lstrip('/')
133133

134+
@property
135+
def trim_query(self):
136+
"""
137+
Return command without a query.
138+
"""
139+
return self.command.split('?')[0]
140+
134141

135142
class APIBase:
136143
"""
@@ -187,12 +194,14 @@ def __init__(self,
187194
name: str,
188195
url: str,
189196
content_template: str,
197+
trim_query: bool = True,
190198
endpoint_prefix: str = '',
191199
tags: list = DEFAULT_TAG_LIST,
192200
login: str or None = None,
193201
password: str or None = None):
194202
super().__init__(name, url)
195203
self.content_template = content_template
204+
self.trim_query = trim_query
196205
self.tags = tags
197206
self.registry = {}
198207
self.endpoint_prefix = endpoint_prefix
@@ -312,8 +321,9 @@ def generate_content_by_reference(self,
312321
api_ref = Reference(**ref.__dict__) # copy of original ref
313322
if self.endpoint_prefix:
314323
api_ref.endpoint_prefix = self.endpoint_prefix
315-
316324
ref_dict = api_ref.__dict__
325+
if self.trim_query:
326+
ref_dict['command'] = api_ref.trim_query
317327
if rel_command:
318328
ref_dict['command'] = api_ref.rel_command
319329
if with_ep and self.endpoint_prefix:
@@ -336,13 +346,15 @@ def __init__(self,
336346
name: str,
337347
url: str,
338348
anchor_template: str,
349+
trim_query: bool = True,
339350
anchor_converter: str = 'pandoc',
340351
endpoint_prefix: str = '',
341352
tags: list = DEFAULT_TAG_LIST,
342353
login: str or None = None,
343354
password: str or None = None):
344355
super().__init__(name, url)
345356
self.anchor_template = anchor_template
357+
self.trim_query = trim_query
346358
self.anchor_converter = anchor_converter
347359
self.tags = tags
348360
self.registry = {}
@@ -409,7 +421,7 @@ def get_anchor_by_reference(self, ref: Reference) -> str:
409421
Get anchor for the reference from self.registry. The search is performed by
410422
anchor formatted by self.anchor_template. First try with `command from the
411423
reference, then (if that fails), try removing leading slash from command,
412-
then (if that fails), tryadding self.endpoint_prefix if present.
424+
then (if that fails), try adding self.endpoint_prefix if present.
413425
414426
If reference cannot be found — raise ReferenceNotFoundError.
415427
@@ -470,11 +482,12 @@ def generate_anchor_by_reference(self,
470482
(' with endpoint_prefix' if with_ep else '') +
471483
(' with relative command' if rel_command else '')
472484
)
473-
api_ref = Reference(**ref.__dict__) # copy of original ref
485+
api_ref = Reference(**ref.__dict__) # copy of original ref
474486
if self.endpoint_prefix:
475487
api_ref.endpoint_prefix = self.endpoint_prefix
476-
477488
ref_dict = api_ref.__dict__
489+
if self.trim_query:
490+
ref_dict['command'] = api_ref.trim_query
478491
if rel_command:
479492
ref_dict['command'] = api_ref.rel_command
480493
if with_ep and self.endpoint_prefix:
@@ -501,10 +514,12 @@ def __init__(self,
501514
name: str,
502515
url: str,
503516
anchor_template: str,
517+
trim_query: bool = True,
504518
anchor_converter: str = 'pandoc',
505519
endpoint_prefix: str = ''):
506520
super().__init__(name, url)
507521
self.anchor_template = anchor_template
522+
self.trim_query = trim_query
508523
self.anchor_converter = anchor_converter
509524
self.endpoint_prefix = endpoint_prefix
510525

@@ -550,6 +565,8 @@ def generate_anchor_by_reference(self, ref: Reference) -> str:
550565
api_ref.endpoint_prefix = self.endpoint_prefix
551566

552567
ref_dict = api_ref.__dict__
568+
if self.trim_query:
569+
ref_dict['command'] = api_ref.trim_query
553570
anchor_source = self.anchor_template.format(**ref_dict)
554571
return to_id(anchor_source, self.anchor_converter)
555572

@@ -572,13 +589,15 @@ def __init__(
572589
name: str,
573590
url: str,
574591
spec: str or PosixPath,
592+
trim_query: bool = True,
575593
anchor_template: str = DEFAULT_ANCHOR_TEMPLATE,
576594
anchor_converter: str = 'no_transform',
577595
endpoint_prefix: str = '',
578596
login: str or None = None,
579597
password: str or None = None,
580598
):
581599
super().__init__(name, url)
600+
self.trim_query = trim_query
582601
self.anchor_template = anchor_template
583602
self.anchor_converter = anchor_converter
584603
self.endpoint_prefix = endpoint_prefix
@@ -713,8 +732,10 @@ def get_extended_reference(self, ref: Reference) -> dict:
713732
api_ref = Reference(**ref.__dict__) # copy of original ref
714733
if self.endpoint_prefix:
715734
api_ref.endpoint_prefix = self.endpoint_prefix
716-
717-
key = self.REGISTRY_KEY_TEMPLATE.format(verb=api_ref.verb, command=api_ref.command)
735+
command = api_ref.command
736+
if self.trim_query:
737+
command = api_ref.trim_query
738+
key = self.REGISTRY_KEY_TEMPLATE.format(verb=api_ref.verb, command=command)
718739
logger.debug(f'Getting link for reference {ref} by key "{key}"')
719740
if key in self.registry:
720741
logger.debug(f'Found')

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
description=SHORT_DESCRIPTION,
1717
long_description=LONG_DESCRIPTION,
1818
long_description_content_type='text/markdown',
19-
version='1.0.2',
19+
version='1.0.3',
2020
author='Daniil Minukhin',
2121
author_email='[email protected]',
2222
url='https://github.com/foliant-docs/foliantcontrib.apireferences',

tests/test_api_by_anchor.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,13 @@ def test_get_link_by_reference_with_extra_prefix(self):
183183
link = api.get_link_by_reference(ref)
184184
self.assertEqual(link, 'http://example.com/#user-content-get-systemrestart')
185185

186+
def test_get_link_by_reference_with_query(self):
187+
api = self.get_basic_api()
188+
api.endpoint_prefix = '/api/v2'
189+
ref = Reference(verb='GET', command='/admin/status?users=guest,admin')
190+
link = api.get_link_by_reference(ref)
191+
self.assertEqual(link, 'http://example.com/#user-content-get-apiv2adminstatus')
192+
186193
def test_get_nonexistant_link(self):
187194
api = self.get_basic_api()
188195
ref = Reference(verb='GET', command='/wrong/command')

tests/test_api_by_swagger.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,19 @@ def test_get_anchor_by_reference(self):
166166
anchor = api.get_anchor_by_reference(ref)
167167
self.assertEqual(anchor, expected_anchor)
168168

169+
def test_get_anchor_by_reference_with_query(self):
170+
api = APIBySwagger(
171+
name='Test',
172+
url='http://example.com/',
173+
spec=rel_name('data/swagger.json'),
174+
anchor_template='/{tag}/{operation_id}',
175+
)
176+
177+
ref = Reference(verb='PUT', command='/pet?spieces=cat,dog')
178+
expected_anchor = '/pettag/updatePet'
179+
anchor = api.get_anchor_by_reference(ref)
180+
self.assertEqual(anchor, expected_anchor)
181+
169182
def test_get_anchor_by_reference_custom_template(self):
170183
api = APIBySwagger(
171184
name='Test',

tests/test_api_by_tag_content.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,13 @@ def test_get_link_by_reference_with_extra_prefix(self):
180180
link = api.get_link_by_reference(ref)
181181
self.assertEqual(link, 'http://example.com/#user-content-get-systemrestart')
182182

183+
def test_get_link_by_reference_with_query(self):
184+
api = self.get_basic_api()
185+
api.endpoint_prefix = '/api/v2'
186+
ref = Reference(verb='GET', command='/admin/status?users=guest,admin')
187+
link = api.get_link_by_reference(ref)
188+
self.assertEqual(link, 'http://example.com/#user-content-get-apiv2adminstatus')
189+
183190
def test_get_nonexistant_link(self):
184191
api = self.get_basic_api()
185192
ref = Reference(verb='GET', command='/wrong/command')

tests/test_api_gen_anchor.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ def test_generate_anchor_by_reference(self):
2020
anchor = api.generate_anchor_by_reference(ref)
2121
self.assertEqual(anchor, 'user-content-get-userinfo')
2222

23+
def test_generate_anchor_by_reference_with_query(self):
24+
api = APIGenAnchor(
25+
name='Test',
26+
url='http://example.com/',
27+
anchor_template='user content {verb} {command}',
28+
)
29+
ref = Reference(verb='GET', command='/user/info?type=common,secret')
30+
anchor = api.generate_anchor_by_reference(ref)
31+
self.assertEqual(anchor, 'user-content-get-userinfo')
32+
2333
def test_generate_anchor_by_reference_custom_field(self):
2434
api = APIGenAnchor(
2535
name='Test',

0 commit comments

Comments
 (0)