Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jira token auth #66

Open
wants to merge 61 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
e4437bc
b64 encode username:token for jira request auth
mawieland Feb 17, 2023
1f290d5
fix closing bracket
mawieland Feb 17, 2023
228c1fa
fix type in str.join
mawieland Feb 17, 2023
7c82918
convert str to bytes for b64.encode
mawieland Feb 17, 2023
e754b93
decode bytes for str concat
mawieland Feb 17, 2023
38a4f50
change request param naming
mawieland Feb 17, 2023
52ab1fc
test custom class for Bearer auth
mawieland Feb 20, 2023
f44721e
add option for BearerAuth
mawieland Feb 20, 2023
3ac5cc1
options debug
mawieland Feb 20, 2023
f343bda
debug pt 2
mawieland Feb 20, 2023
29e083b
add changes to sne script
mawieland Feb 20, 2023
8fc3d19
click.echo in sne
mawieland Feb 20, 2023
81ca53a
click.echo on all statements
mawieland Feb 20, 2023
42fc618
fix options != params
mawieland Feb 20, 2023
95828c6
rework Bearer auth approach
mawieland Feb 20, 2023
54dbe91
added codebeamer API pagination
mawieland Mar 14, 2023
4fbda04
fix poetry version in tool.poetry.extras
mawieland Mar 14, 2023
ed7090e
move delay to beginning of loop for codebeamer
mawieland Mar 14, 2023
4ae6724
lower default pageSize to 100
mawieland Mar 14, 2023
d77353e
debugging statements
mawieland Mar 14, 2023
7b57c01
edit page value in request params
mawieland Mar 14, 2023
4d41160
set correct number of pages needed
mawieland Mar 14, 2023
40c76b2
furter increase wiki2html delay
mawieland Mar 15, 2023
8dddddd
test new settings for codebeamer API
mawieland Mar 20, 2023
8bbbaff
add single retry after 90 s
mawieland Mar 22, 2023
4dbef15
lower delay between error response retries
mawieland Mar 22, 2023
96c08b2
add multiple retry attempts
mawieland Mar 22, 2023
91b29fa
increase cb pagesize to 250
mawieland Mar 27, 2023
9c86ea0
reenable wiki2html requests
mawieland Mar 28, 2023
49fe891
fix CI errors
mawieland Mar 28, 2023
92f9b9b
add pygls to doc requirements
mawieland Mar 28, 2023
c5a55fb
move sphinx requirement to poetry.extras
mawieland Mar 28, 2023
84c3bf0
add sphinx var to top of pyproject
mawieland Mar 28, 2023
f320b8b
move version array to poetry.extras
mawieland Mar 28, 2023
75a0174
revert changes to poetry.extrax
mawieland Mar 28, 2023
80f3f1a
remove version in poetry.extras
mawieland Mar 28, 2023
c750b3a
linting
mawieland Mar 28, 2023
334025a
pin pygls to version <=1.0
mawieland Mar 28, 2023
de7a91e
bump sphinx-needs to 1.2.2
mawieland Mar 29, 2023
cd05f71
move delay in wiki2html requests, update changelog
mawieland Mar 29, 2023
d2c484b
msg if using bearer auth
mawieland Apr 11, 2023
c0e8009
test pagination for jira
mawieland Apr 11, 2023
cee045f
refactoring
mawieland Apr 11, 2023
611d5f0
move pagesize in request params
mawieland Apr 11, 2023
b5ecf79
add fields to jira request params
mawieland Apr 11, 2023
953db39
rename response object
mawieland Apr 11, 2023
0ca012a
add current_page to jira.py
mawieland Apr 11, 2023
973976d
test page numbers
mawieland Apr 12, 2023
624b640
refine extract_data
mawieland Apr 12, 2023
2becbf6
add more fields to jira query
mawieland Apr 13, 2023
528974c
increase fields to query
mawieland Apr 13, 2023
0d49abd
correct request param for jira pagination
mawieland Apr 13, 2023
2c7b9f4
set default when no title present
mawieland Apr 14, 2023
1a11653
add error catch in replace_content
mawieland Apr 18, 2023
3897799
check for replace_content option
mawieland Apr 19, 2023
dea6220
fixed typo
mawieland Apr 19, 2023
673c2b9
test wiki2html output
mawieland Apr 19, 2023
10471c9
rework replacement logic
mawieland Jun 23, 2023
1f0458b
debug replacement
mawieland Jun 23, 2023
d3ef288
assign new value in replace
mawieland Jun 23, 2023
ecee04a
remove debug statements
mawieland Jun 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,4 @@ needs.rst

!/presentations/_static/dynamic_background/js/lib
/needs_save.json
!/tests/roots/test-sne-import/needs.json
4 changes: 4 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Changelog
-----
**Released**: under developement

* Improvement: Added pagination and retry attemps for codebeamer API requests
* Improvement: Added bearer auth


1.0.1
-----
**Released**: 01.02.2023
Expand Down
5 changes: 3 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
import os
import sys

from sphinx_needs_enterprise.version import VERSION

# from docutils.parsers.rst import directives

from sphinx_needs_enterprise.version import VERSION

sys.path.insert(0, os.path.abspath("../sphinx_needs_enterprise"))

Expand Down Expand Up @@ -303,4 +304,4 @@ def setup(app):
}
}

linkcheck_workers = 5
linkcheck_workers = 5
3 changes: 2 additions & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
sphinx>=3.4
sphinxcontrib-plantuml
sphinx-needs==1.0.1
sphinx-needs==1.2.2
sphinx-copybutton
pygls

# Sphinx-Material Theme + Fix to support Sphinx-Needs
sphinx-design
Expand Down
9 changes: 9 additions & 0 deletions docs/services/jira.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ notation.

See also :ref:`conf_query` for more details.

.. _jira_enable_bearer_auth:

enable_bearer_auth
~~~~~~~~~~~~~~~~~~

**Default** False

If specified, enables bearer auth for jira API service. See `Jira API docs https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html` for more details.


id_prefix
~~~~~~~~~
Expand Down
3 changes: 2 additions & 1 deletion presentations/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
sphinx
sphinx-needs==1.0.1
sphinx-needs==1.2.2
sphinxcontrib-plantuml
sphinx-revealjs
pygls<=1.0
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ documentation = "https://useblocks.com/sphinx-needs-enterprise"
[tool.poetry.dependencies]
python = ">3.7,<4.0"
sphinx = ">=4.2"
sphinx-needs = ">=1.0.1"
sphinx-needs = ">=1.2.2"
licensing = ">=0.31"
requests = ">=2"
jinja2 = ">=2"
Expand Down Expand Up @@ -50,7 +50,7 @@ line-length = 120
profile = "black"

[tool.poetry.extras]
docs = ["sphinx>=4.0"]
docs = ["sphinx"]

[build-system]
requires = ["poetry>=0.12"]
Expand Down
69 changes: 67 additions & 2 deletions sphinx_needs_enterprise/extensions/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@
from sphinx_needs_enterprise.util import dict_get, jinja_parse


class BearerAuth(requests.auth.AuthBase):
def __init__(self, token):
self.token = token

def __call__(self, r):
r.headers["authorization"] = "Bearer " + self.token
return r


class ServiceExtension(BaseService):
def __init__(
self,
Expand All @@ -31,6 +40,7 @@ def __init__(
mappings_replaces=None,
extra_data=None,
ssl_location=None,
bearer_auth=None,
**kwargs,
):

Expand All @@ -55,6 +65,7 @@ def __init__(
self.extra_data = extra_data or config.get("extra_data", {})

self.ssl_location = ssl_location or config.get("ssl_cert_abspath", "")
self.bearer_auth = ssl_location or config.get("enable_bearer_auth", "")

self.license_key = None
self.product_id = None
Expand Down Expand Up @@ -108,7 +119,16 @@ def _prepare_request(self, options):
url = options.get("url", self.url)
url = url + self.url_postfix

auth = (options.get("user", self.user), options.get("password", self.password))
bearer_auth = options.get("enable_bearer_auth", self.bearer_auth)

if bearer_auth:
print("using bearer auth")

auth = BearerAuth(options.get("password", self.password))

else:

auth = (options.get("user", self.user), options.get("password", self.password))
query = options.get("query", self.query)
query = query + self.query_postfix

Expand Down Expand Up @@ -145,7 +165,30 @@ def _send_request(self, request, cert_abspath=""):
result = requests.request(**request)

if result.status_code >= 300:
raise CommunicationException(f"Problems accessing {result.url}.\nReason: {result.text}")
from time import sleep

delay = 15
retry_limit = 3
retries = 0

print(f"HTTP status code {result.status_code}")
print(f"retrying {retry_limit} times with {delay} s delay")

while result.status_code != 200 and retries < retry_limit:
retries += 1
print(f"retry #{retries} in {delay} seconds")
sleep(delay)
print(f"HTTP status code {result.status_code}")

if cert_abspath:
result = requests.request(**request, verify=cert_abspath)

else:
result = requests.request(**request)

if result.status_code >= 300:

raise CommunicationException(f"Problems accessing {result.url}.\nReason: {result.text}")

return result

Expand Down Expand Up @@ -215,8 +258,30 @@ def _extract_data(self, data, options):
if name == "id":
need_values[name] = prefix + need_values.get(name, "")

if "title" not in need_values.keys():
need_values["title"] = "no title provided"

finale_data = {"content": content}
finale_data.update(need_values)

need_data.append(finale_data)
return need_data

def replace_content(self, options, data):
replace_dict = options.get("replace_content", self.config["replace_content"])
options["replace_content"] = replace_dict

if not replace_dict:
return data

# TODO invert logic, first parse datum in data, then replace if present, saves compute time

for field, replacements in replace_dict.items(): # entries in service_config["replace_content"], low amount
for string, replacement in replacements.items(): # string to replace
for datum in data: # entries

if field in datum.keys():

datum[field] = datum[field].replace(string, replacement)

return data
14 changes: 13 additions & 1 deletion sphinx_needs_enterprise/scripts/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import click
import elasticsearch
import jinja2
import requests
from tqdm import tqdm

# API has changed with Sphinx-Needs version 1.0.1
Expand All @@ -18,6 +19,15 @@
from sphinx_needs_enterprise.scripts.loader import service_loader


class BearerAuth(requests.auth.AuthBase):
def __init__(self, token):
self.token = token

def __call__(self, r):
r.headers["authorization"] = "Bearer " + self.token
return r


@click.group()
def cli():
pass
Expand Down Expand Up @@ -55,6 +65,9 @@ def import_cmd(service, conf, outdir, query, old_needfile, version, wipe):
click.echo("Done")
click.echo(f"Retrieved {len(data)} elements")

if "replace_content" in service_obj.config:
data = service_obj.replace_content(options, data)

# Storing data
os.makedirs(outdir, exist_ok=True)
needlist = NeedsList(sphinx_config, outdir, ".")
Expand Down Expand Up @@ -95,7 +108,6 @@ def import_cmd(service, conf, outdir, query, old_needfile, version, wipe):
f"Warning: new created needs from imported data do not have such needs options: \
{not_included_options}"
)

for datum in data:
needlist.add_need(version, datum)

Expand Down
105 changes: 100 additions & 5 deletions sphinx_needs_enterprise/services/codebeamer.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,29 +74,118 @@ def request(self, options=None):

params = self._prepare_request(options)

delay = 1
current_page = 1
retry_limit = 3

# query 250 objects at once
request_params = {
"method": "GET",
"url": params["url"],
"auth": params["auth"],
"params": {
"queryString": params["query"],
"pageSize": 250,
"page": current_page,
"descriptionFormat": "HTML",
"descFormat": "HTML",
},
}
answer = self._send_request(request_params, params["cert_abspath"])
data = answer.json()["items"]
result = self._send_request(request_params, params["cert_abspath"])

response_json = result.json()

combined_objects = response_json["items"]
print(len(combined_objects))

# if first request fails, try RETRY_LIMIT times again
retries = 0
if result.status_code != 200:
while retries < retry_limit:
print("retrying connection")
result = self._send_request(request_params, params["cert_abspath"])
retries += 1
time.sleep(3)

# check return code

if result.status_code == 200:

print("start pagination")

total = response_json["total"]
page_size = response_json["pageSize"]

# there are more items than shown, request more pages
if total > page_size:

# minimum amount of pages needed for example if pageSize = 100 and 1000 objects
min_pages = total // page_size

# if page_size multiple of amount of objects, no additional query needed
if total % page_size == 0:

total_page_count = min_pages

else:
# one more pagination request needed to get remainder of data
total_page_count = min_pages + 1

# request pages 2 - last page
for i in range(total_page_count):

time.sleep(delay)

current_page += 1
request_params["params"]["page"] = current_page

print(f"querying page {current_page}")

result = self._send_request(request_params, params["cert_abspath"])

status = result.status_code

retries = 0

if status != 200:

while retries < retry_limit:
print("retrying connection")
result = self._send_request(request_params, params["cert_abspath"])
retries += 1
time.sleep(3)

response_json = result.json()

[combined_objects.append(item) for item in response_json["items"]]

print(len(combined_objects))

retries += 1

# print("starting wiki2html requests")
# print(len(combined_objects))

data = combined_objects

for datum in data:
delay = cb_request_delay_ms / 1000
if delay:
time.sleep(delay)

# Be sure "description" is set and valid
if "description" not in datum or datum["description"] is None:
datum["description"] = ""
elif datum["descriptionFormat"] == "Wiki" and wiki2html == "True":
# Transform the Codebeamer wiki syntax to HTML.
# Must be done by an API request for each item.

print("format is WIKI")
print("pre translation")
print("--------------------")
print(datum["description"])

delay = cb_request_delay_ms / 1000
if delay:
time.sleep(delay)

url = options.get("url", self.url)
wiki2html_id = options.get("wiki2html_id", 2)

Expand All @@ -114,9 +203,15 @@ def request(self, options=None):

wiki2html_answer = self._send_request(wiki2html_params, params["cert_abspath"])
datum["description"] = wiki2html_answer.text

print("--------------------")
print("post translation")
print(datum["description"])

need_data = self._extract_data(data, options)

print(len(need_data))

return need_data

def debug(self, options):
Expand Down
Loading
Loading