Skip to content

Commit 048db59

Browse files
committed
feat: add moderationrequest endpoints
1 parent 7daf274 commit 048db59

3 files changed

Lines changed: 322 additions & 1 deletion

File tree

sw360/moderationrequests.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# -------------------------------------------------------------------------------
2+
# Copyright (c) 2024 Siemens
3+
# All Rights Reserved.
4+
# Authors: thomas.graf@siemens.com
5+
#
6+
# Licensed under the MIT license.
7+
# SPDX-License-Identifier: MIT
8+
# -------------------------------------------------------------------------------
9+
10+
from typing import Any, Dict, List, Optional
11+
12+
from .base import BaseMixin
13+
14+
15+
class ModerationRequestMixin(BaseMixin):
16+
def get_all_moderation_requests(self, page: int = -1, page_size: int = -1,
17+
sort: str = "") -> List[Dict[str, Any]]:
18+
"""Get information of about all moderation requests
19+
20+
API endpoint: GET /moderationrequest
21+
22+
:param page: page to retrieve
23+
:type page: int
24+
:param page_size: page size to use
25+
:type page_size: int
26+
:param sort: sort order for the packages ("name,desc"; "name,asc")
27+
:type sort: str
28+
:return: list of moderation requests
29+
:rtype: list of JSON moderation requests objects
30+
:raises SW360Error: if there is a negative HTTP response
31+
"""
32+
33+
full_url = self.url + "resource/api/moderationrequest"
34+
if page > -1:
35+
full_url = self._add_param(full_url, "page=" + str(page))
36+
full_url = self._add_param(full_url, "page_entries=" + str(page_size))
37+
38+
if sort:
39+
# ensure HTML encoding
40+
sort = sort.replace(",", "%2C")
41+
full_url = self._add_param(full_url, "sort=" + sort)
42+
43+
resp = self.api_get(full_url)
44+
return resp
45+
46+
def get_moderation_requests_by_state(self, state: str, all_details: bool = False,
47+
page: int = -1, page_size: int = -1,
48+
sort: str = "") -> List[Dict[str, Any]]:
49+
"""Get information of about all moderation requests
50+
51+
API endpoint: GET /moderationrequest/byState
52+
53+
:param all_details: retrieve all package details (optional))
54+
:type all_details: bool
55+
:param page: page to retrieve
56+
:type page: int
57+
:param page_size: page size to use
58+
:type page_size: int
59+
:param sort: sort order for the packages ("name,desc"; "name,asc")
60+
:type sort: str
61+
:return: list of moderation requests
62+
:rtype: list of JSON moderation requests objects
63+
:raises SW360Error: if there is a negative HTTP response
64+
"""
65+
66+
full_url = self.url + "resource/api/moderationrequest/byState?state=" + state
67+
if all_details:
68+
full_url = self._add_param(full_url, "allDetails=true")
69+
70+
if page > -1:
71+
full_url = self._add_param(full_url, "page=" + str(page))
72+
full_url = self._add_param(full_url, "page_entries=" + str(page_size))
73+
74+
if sort:
75+
# ensure HTML encoding
76+
sort = sort.replace(",", "%2C")
77+
full_url = self._add_param(full_url, "sort=" + sort)
78+
79+
resp = self.api_get(full_url)
80+
return resp
81+
82+
def get_moderation_request(self, mr_id: str) -> Optional[Dict[str, Any]]:
83+
"""Get information of about a moderation request
84+
85+
API endpoint: GET /moderationrequest/{id}
86+
87+
:param mr_id: the id of the moderation request to be requested
88+
:type mr_id: string
89+
:return: a moderation request
90+
:rtype: JSON moderation request object
91+
:raises SW360Error: if there is a negative HTTP response
92+
"""
93+
94+
resp = self.api_get(self.url + "resource/api/moderationrequest/" + mr_id)
95+
return resp

sw360/sw360_api.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from .sw360error import SW360Error
2626
from .vendor import VendorMixin
2727
from .vulnerabilities import VulnerabilitiesMixin
28+
from .moderationrequests import ModerationRequestMixin
2829

2930
# Retry mechanism for rate limiting
3031
adapter = HTTPAdapter(max_retries=Retry(
@@ -48,7 +49,8 @@ class SW360(
4849
ReleasesMixin,
4950
VendorMixin,
5051
VulnerabilitiesMixin,
51-
PackagesMixin
52+
PackagesMixin,
53+
ModerationRequestMixin
5254
):
5355
"""Python interface to the Siemens SW360 platform
5456
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
# -------------------------------------------------------------------------------
2+
# Copyright (c) 2024 Siemens
3+
# All Rights Reserved.
4+
# Author: thomas.graf@siemens.com
5+
#
6+
# Licensed under the MIT license.
7+
# SPDX-License-Identifier: MIT
8+
# -------------------------------------------------------------------------------
9+
10+
import sys
11+
import unittest
12+
import warnings
13+
14+
import responses
15+
16+
from sw360 import SW360
17+
18+
sys.path.insert(1, "..")
19+
20+
21+
class Sw360TestModerationRequests(unittest.TestCase):
22+
MYTOKEN = "MYTOKEN"
23+
MYURL = "https://my.server.com/"
24+
ERROR_MSG_NO_LOGIN = "Unable to login"
25+
26+
def setUp(self) -> None:
27+
warnings.filterwarnings(
28+
"ignore", category=ResourceWarning,
29+
message="unclosed.*<ssl.SSLSocket.*>")
30+
31+
def _add_login_response(self) -> None:
32+
"""
33+
Add the response for a successful login.
34+
"""
35+
responses.add(
36+
method=responses.GET,
37+
url=self.MYURL + "resource/api/",
38+
body="{'status': 'ok'}",
39+
status=200,
40+
content_type="application/json",
41+
adding_headers={"Authorization": "Token " + self.MYTOKEN},
42+
)
43+
44+
@responses.activate
45+
def test_get_all_moderation_requests(self) -> None:
46+
lib = SW360(self.MYURL, self.MYTOKEN, False)
47+
self._add_login_response()
48+
actual = lib.login_api()
49+
self.assertTrue(actual)
50+
51+
responses.add(
52+
method=responses.GET,
53+
url=self.MYURL + "resource/api/moderationrequest?page=2&page_entries=8&sort=timestamp%2Cdesc",
54+
body='''{
55+
"_embedded": {
56+
"sw360:moderationRequests": [
57+
{
58+
"id": "b41ddfb69b40439cabb18538313a036c",
59+
"timestamp": 1729870771747,
60+
"timestampOfDecision": 0,
61+
"documentId": "8d101909d2f24ea592e666e9e9bb9eb0",
62+
"documentType": "RELEASE",
63+
"requestingUser": "gernot.hillier@siemens.com",
64+
"moderators": ["admin2@sw360.org", "thomas.graf@siemens.com"],
65+
"documentName": "@grpc/grpc-js (1.9.15)",
66+
"moderationState": "PENDING",
67+
"requestingUserDepartment": "SI",
68+
"componentType": "OSS",
69+
"moderatorsSize": 157,
70+
"_links": {
71+
"self": {
72+
"href": "https://my.server.com/resource/api/moderationrequest/b4"
73+
}
74+
}
75+
}]
76+
},
77+
"_links": {
78+
"first": {
79+
"href": "https://my.server.com/resource/api/moderationrequest?page=0&page_entries=20"
80+
},
81+
"next": {
82+
"href": "https://my.server.com/resource/api/moderationrequest?page=1&page_entries=20"
83+
},
84+
"last": {
85+
"href": "https://my.server.com/resource/api/moderationrequest?page=2769&page_entries=20"
86+
},
87+
"curies": [
88+
{
89+
"href": "https://my.server.com/resource/docs/{rel}.html",
90+
"name": "sw360",
91+
"templated": true
92+
}
93+
]
94+
},
95+
"page": {
96+
"size": 20,
97+
"totalElements": 5555,
98+
"totalPages": 3333,
99+
"number": 0
100+
}
101+
}''',
102+
status=200,
103+
content_type="application/json",
104+
adding_headers={"Authorization": "Token " + self.MYTOKEN},
105+
)
106+
107+
mrs = lib.get_all_moderation_requests(page=2, page_size=8, sort="timestamp,desc")
108+
self.assertIsNotNone(mrs)
109+
self.assertTrue(len(mrs) > 0)
110+
mr_list = mrs["_embedded"]["sw360:moderationRequests"]
111+
self.assertEqual("@grpc/grpc-js (1.9.15)", mr_list[0]["documentName"])
112+
113+
@responses.activate
114+
def test_get_moderation_requests_by_state(self) -> None:
115+
lib = SW360(self.MYURL, self.MYTOKEN, False)
116+
self._add_login_response()
117+
actual = lib.login_api()
118+
self.assertTrue(actual)
119+
120+
responses.add(
121+
method=responses.GET,
122+
url=self.MYURL + "resource/api/moderationrequest/byState?state=open&allDetails=true&page=2&page_entries=8&sort=timestamp%2Cdesc", # noqa
123+
body='''{
124+
"_embedded": {
125+
"sw360:moderationRequests": [
126+
{
127+
"id": "b41ddfb69b40439cabb18538313a036c",
128+
"timestamp": 1729870771747,
129+
"timestampOfDecision": 0,
130+
"documentId": "8d101909d2f24ea592e666e9e9bb9eb0",
131+
"documentType": "RELEASE",
132+
"requestingUser": "gernot.hillier@siemens.com",
133+
"moderators": ["admin2@sw360.org", "thomas.graf@siemens.com"],
134+
"documentName": "@grpc/grpc-js (1.9.15)",
135+
"moderationState": "PENDING",
136+
"requestingUserDepartment": "SI",
137+
"componentType": "OSS",
138+
"moderatorsSize": 157,
139+
"_links": {
140+
"self": {
141+
"href": "https://my.server.com/resource/api/moderationrequest/b4"
142+
}
143+
}
144+
}]
145+
},
146+
"_links": {
147+
"first": {
148+
"href": "https://my.server.com/resource/api/moderationrequest?page=0&page_entries=20"
149+
},
150+
"next": {
151+
"href": "https://my.server.com/resource/api/moderationrequest?page=1&page_entries=20"
152+
},
153+
"last": {
154+
"href": "https://my.server.com/resource/api/moderationrequest?page=2769&page_entries=20"
155+
},
156+
"curies": [
157+
{
158+
"href": "https://my.server.com/resource/docs/{rel}.html",
159+
"name": "sw360",
160+
"templated": true
161+
}
162+
]
163+
},
164+
"page": {
165+
"size": 20,
166+
"totalElements": 5555,
167+
"totalPages": 3333,
168+
"number": 0
169+
}
170+
}''',
171+
status=200,
172+
content_type="application/json",
173+
adding_headers={"Authorization": "Token " + self.MYTOKEN},
174+
)
175+
176+
mrs = lib.get_moderation_requests_by_state("open", True, page=2, page_size=8, sort="timestamp,desc")
177+
self.assertIsNotNone(mrs)
178+
self.assertTrue(len(mrs) > 0)
179+
mr_list = mrs["_embedded"]["sw360:moderationRequests"]
180+
self.assertEqual("@grpc/grpc-js (1.9.15)", mr_list[0]["documentName"])
181+
182+
@responses.activate
183+
def test_get_license(self) -> None:
184+
lib = SW360(self.MYURL, self.MYTOKEN, False)
185+
self._add_login_response()
186+
actual = lib.login_api()
187+
self.assertTrue(actual)
188+
189+
responses.add(
190+
method=responses.GET,
191+
url=self.MYURL + "resource/api/moderationrequest/0815",
192+
body='''{
193+
"id": "0815",
194+
"timestamp": 1729870771747,
195+
"timestampOfDecision": 0,
196+
"documentId": "8d101909d2f24ea592e666e9e9bb9eb0",
197+
"documentType": "RELEASE",
198+
"requestingUser": "gernot.hillier@siemens.com",
199+
"moderators": ["admin2@sw360.org", "thomas.graf@siemens.com"],
200+
"documentName": "@grpc/grpc-js (1.9.15)",
201+
"moderationState": "PENDING",
202+
"requestingUserDepartment": "SI",
203+
"componentType": "OSS",
204+
"moderatorsSize": 157,
205+
"_links": {
206+
"self": {
207+
"href": "https://my.server.com/resource/api/moderationrequest/b41ddfb69b40439cabb18538313a036c"
208+
}
209+
}
210+
}''',
211+
status=200,
212+
content_type="application/json",
213+
adding_headers={"Authorization": "Token " + self.MYTOKEN},
214+
)
215+
216+
mr = lib.get_moderation_request("0815")
217+
self.assertIsNotNone(license)
218+
if mr: # only for mypy
219+
self.assertEqual("b41ddfb69b40439cabb18538313a036c", mr["id"])
220+
self.assertEqual("@grpc/grpc-js (1.9.15)", mr["documentName"])
221+
222+
223+
if __name__ == "__main__":
224+
unittest.main()

0 commit comments

Comments
 (0)