Skip to content

Commit

Permalink
Codebeamer-lobster supports CB-API V3
Browse files Browse the repository at this point in the history
The used cb-endpoints are updated and one unit-test has been added.

Resolves #49
  • Loading branch information
TannazVhdBMWExt committed Jul 29, 2024
1 parent 14f23a8 commit e38e82d
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 45 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

### 0.9.17-dev

* The `lobster-codebeamer` tool now uses codebeamer api v3

* The `lobster-html-report` tool now supports argument `--dot` to specify
the path to the graphviz dot utility instead of expecting it in PATH

Expand Down
65 changes: 20 additions & 45 deletions lobster/tools/codebeamer/codebeamer.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@
import sys
import argparse
import netrc

from copy import copy

from urllib.parse import quote
import requests

from lobster.items import Tracing_Tag, Requirement
Expand Down Expand Up @@ -84,27 +82,25 @@ def query_cb_single(cb_config, url):
def get_single_item(cb_config, item_id):
assert isinstance(item_id, int) and item_id > 0

url = "%s/item/%u" % (cb_config["base"],
item_id)
url = "%s/items/%u" % (cb_config["base"], item_id)
data = query_cb_single(cb_config, url)
return data


def get_many_items_maybe(cb_config, tracker_id, item_ids):
assert isinstance(tracker_id, int)
def get_many_items(cb_config, item_ids):
assert isinstance(item_ids, set)

rv = []

base_url = "%s/tracker/%u/items/or/%s" % (
cb_config["base"],
tracker_id,
";".join("id=%u" % item_id
for item_id in item_ids))
page_id = 1
query_string = quote(f"item.id IN "
f"({','.join(str(item_id) for item_id in item_ids)})")

while True:
data = query_cb_single(cb_config, "%s/page/%u" % (base_url,
page_id))
base_url = "%s/items/query?page=%u&pageSize=%u&queryString=%s"\
% (cb_config["base"], page_id,
cb_config["page_size"], query_string)
data = query_cb_single(cb_config, base_url)
rv += data["items"]
if len(rv) == data["total"]:
break
Expand All @@ -123,14 +119,13 @@ def get_query(mh, cb_config, query_id):

while total_items is None or len(rv) < total_items:
print("Fetching page %u of query..." % page_id)
url = "%s/query/%u/page/%u?pagesize=%u" % \
url = "%s/reports/%u/items?page=%u&pageSize=%u" % \
(cb_config["base"],
query_id,
page_id,
cb_config["page_size"])
data = query_cb_single(cb_config, url)
assert len(data) == 1
data = data["trackerItems"]
assert len(data) == 4

if page_id == 1 and len(data["items"]) == 0:
print("This query doesn't generate items. Please check:")
Expand All @@ -145,7 +140,7 @@ def get_query(mh, cb_config, query_id):
else:
assert total_items == data["total"]

rv += [to_lobster(cb_config, cb_item)
rv += [to_lobster(cb_config, cb_item["item"])
for cb_item in data["items"]]

page_id += 1
Expand All @@ -162,8 +157,8 @@ def to_lobster(cb_config, cb_item):
# This looks like it's business logic, maybe we should make this
# configurable?

if "type" in cb_item:
kind = cb_item["type"].get("name", "codebeamer item")
if "typeName" in cb_item:
kind = cb_item["typeName"]
else:
kind = "codebeamer item"

Expand Down Expand Up @@ -206,32 +201,12 @@ def import_tagged(mh, cb_config, items_to_import):
assert isinstance(mh, Message_Handler)
assert isinstance(cb_config, dict)
assert isinstance(items_to_import, set)
work_list = copy(items_to_import)
rv = []

tracker_id = None
while work_list:
if tracker_id is None or len(work_list) < 3:
target = work_list.pop()
print("Fetching single item %u" % target)

cb_item = get_single_item(cb_config, target)
l_item = to_lobster(cb_config, cb_item)
tracker_id = l_item.location.tracker
rv.append(l_item)

else:
print("Attempting to fetch %u items from %s" %
(len(work_list), tracker_id))
cb_items = get_many_items_maybe(cb_config, tracker_id, work_list)

for cb_item in cb_items:
l_item = to_lobster(cb_config, cb_item)
assert tracker_id == l_item.location.tracker
rv.append(l_item)
work_list.remove(l_item.location.item)

tracker_id = None
cb_items = get_many_items(cb_config, items_to_import)
for cb_item in cb_items:
l_item = to_lobster(cb_config, cb_item)
rv.append(l_item)

return rv

Expand Down Expand Up @@ -272,7 +247,7 @@ def main():

cb_config = {
"root" : options.cb_root,
"base" : "%s/cb/rest" % options.cb_root,
"base" : "%s/cb/api/v3" % options.cb_root,
"user" : options.cb_user,
"pass" : options.cb_pass,
"verify_ssl" : not options.ignore_ssl_errors,
Expand Down
96 changes: 96 additions & 0 deletions test-unit/lobster-codebeamer/test_codebeamer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import unittest
from unittest.mock import Mock, patch

from lobster.tools.codebeamer.codebeamer import get_single_item, get_many_items, to_lobster, \
import_tagged
from lobster.errors import Message_Handler

list_of_compared_attributes = ['name', 'kind', 'status', 'just_down', 'just_up', 'just_global']


class QueryCodebeamerTest(unittest.TestCase):

def _assertListEqualByAttributes(self, list1, list2):
self.assertEqual(len(list1), len(list2), "Lists length are not the same")
for obj1, obj2 in zip(list1, list2):
for attr in list_of_compared_attributes:
self.assertEqual(getattr(obj1, attr), getattr(obj2, attr), f"{obj1} is not like {obj2} in {attr}")

@patch('lobster.tools.codebeamer.codebeamer.query_cb_single')
def test_get_single_item(self, mock_get):
_item_id = 11693324
_cb_config = {'base': 'https://test.com'}
_moch_response = Mock()
_expected_test_result = {
'page': 1,
'pageSize': 100,
'total': 1,
'items': [{'item': {'id': 11693324, 'name': 'test name'}}]
}
_moch_response.return_value = _expected_test_result

mock_get.return_value = _moch_response

query_result = get_single_item(_cb_config, _item_id)
self.assertEqual(query_result, _moch_response)

@patch('lobster.tools.codebeamer.codebeamer.query_cb_single')
def test_get_many_items(self, mock_get):
_item_ids = {24406947, 21747817}
_cb_config = {'base': 'https://test.com', 'page_size': 100}
_response_items = [
{'id': 24406947, 'name': 'Test name 1'},
{'id': 21747817, 'name': 'Test name 2'}
]
_moch_response = {
'page': 1,
'pageSize': 100,
'total': 2,
'items': _response_items
}

mock_get.return_value = _moch_response

query_result = get_many_items(_cb_config, _item_ids)
self.assertEqual(query_result, _response_items)

@patch('lobster.tools.codebeamer.codebeamer.query_cb_single')
def test_import_tagged(self, mock_get):
_mh = Message_Handler()
_item_ids = {24406947, 21747817}
_cb_config = {'root': 'https://test.com/', 'base': 'https://test.com/base', 'page_size': 100}
_response_items = [
{
'id': 24406947,
'name': 'Test name 1',
'typeName': 'Requirement',
'version': 7,
'status': {'name': 'status'},
'tracker': {'id': 123}
},
{
'id': 21747817,
'name': 'Test name 2',
'typeName': 'Requirement',
'version': 10,
'status': {'name': 'status'},
'tracker': {'id': 123}
}
]
_mock_response = {
'page': 1,
'pageSize': 100,
'total': 2,
'items': _response_items
}
mock_get.return_value = _mock_response

_expected_result = [to_lobster(_cb_config, items) for items in _response_items]

import_tagged_result = import_tagged(_mh, _cb_config, _item_ids)

self._assertListEqualByAttributes(import_tagged_result, _expected_result)


if __name__ == '__main__':
unittest.main()

0 comments on commit e38e82d

Please sign in to comment.