Skip to content

Commit 9383384

Browse files
authored
Merge pull request #34 from fossology/feat/support-1.1.0
Feat/support 1.1.0
2 parents a38cb80 + a965354 commit 9383384

20 files changed

+1509
-768
lines changed

.github/workflows/fossologytests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
run: nmap fossology -p 80
3333
- name: Run tests
3434
run: |
35-
poetry run coverage run --source=fossology tests/tests.py
35+
poetry run coverage run --source=fossology -m pytest
3636
poetry run coverage report -m
3737
poetry run codecov -t ${{ secrets.CODECOV_TOKEN }}
3838

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ A simple wrapper for the Fossology REST API.
2222

2323
See `the OpenAPI specification <https://raw.githubusercontent.com/fossology/fossology/master/src/www/ui/api/documentation/openapi.yaml>`_ used to implement this library.
2424

25-
Compatible with API version 1.0.16
25+
Compatible with API version 1.1.0
2626

2727
Documentation
2828
=============
@@ -151,6 +151,6 @@ The testsuite available in this project expects a running Fossology instance und
151151

152152
.. code:: shell
153153
154-
poetry run coverage run --source=fossology tests/tests.py
154+
poetry run coverage run --source=fossology -m pytest
155155
poetry run coverage report -m
156156
poetry run coverage html

fossology/__init__.py

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@
66
import requests
77
from datetime import date, timedelta
88

9-
from .obj import Agents, User, TokenScope, SearchTypes
10-
from .folders import Folders
11-
from .uploads import Uploads
12-
from .jobs import Jobs
13-
from .report import Report
14-
from .exceptions import AuthenticationError, FossologyApiError
9+
from typing import List
10+
from fossology.obj import Agents, User, File, TokenScope, SearchTypes, get_options
11+
from fossology.folders import Folders
12+
from fossology.uploads import Uploads
13+
from fossology.jobs import Jobs
14+
from fossology.report import Report
15+
from fossology.exceptions import (
16+
AuthenticationError,
17+
AuthorizationError,
18+
FossologyApiError,
19+
)
1520

1621
logger = logging.getLogger(__name__)
1722
logger.setLevel(logging.DEBUG)
@@ -36,12 +41,12 @@ def fossology_token(
3641
:param password: the password of the user
3742
:param name: the name of the token
3843
:param scope: the scope of the token (default: READ)
39-
:param expire: the expire date of the token (default max. 30 days)
44+
:param expire: the expire date of the token (default: max. 30 days)
4045
:type url: string
4146
:type username: string
4247
:type password: string
4348
:type name: string
44-
:type scope: TokenScope (default TokenScope.READ)
49+
:type scope: TokenScope (default: TokenScope.READ)
4550
:type expire: string, e.g. 2019-12-25
4651
:return: the new token
4752
:rtype: string
@@ -208,7 +213,7 @@ def delete_user(self, user):
208213
:raises FossologyApiError: if the REST call failed
209214
"""
210215
response = self.session.delete(f"{self.api}/users/{user.id}")
211-
print(response.json())
216+
212217
if response.status_code == 202:
213218
return
214219
else:
@@ -224,6 +229,7 @@ def search(
224229
filesizemax=None,
225230
license=None,
226231
copyright=None,
232+
group=None,
227233
):
228234
"""Search for a specific file
229235
@@ -236,16 +242,19 @@ def search(
236242
:param filesizemax: Max filesize in bytes
237243
:param license: License search filter
238244
:param copyright: Copyright search filter
245+
:param group: the group name to choose while performing search (default: None)
239246
:type searchType: SearchType Enum
240247
:type filename: string
241248
:type tag: string
242249
:type filesizemin: int
243250
:type filesizemax: int
244251
:type license: string
245252
:type copyright: string
253+
:type group: string
246254
:return: list of items corresponding to the search criteria
247255
:rtype: JSON
248256
:raises FossologyApiError: if the REST call failed
257+
:raises AuthorizationError: if the user can't access the group
249258
"""
250259
headers = {"searchType": searchType.value}
251260
if filename:
@@ -260,10 +269,61 @@ def search(
260269
headers["license"] = license
261270
if copyright:
262271
headers["copyright"] = copyright
272+
if group:
273+
headers["groupName"] = group
263274

264275
response = self.session.get(f"{self.api}/search", headers=headers)
276+
265277
if response.status_code == 200:
266278
return response.json()
279+
280+
elif response.status_code == 403:
281+
description = f"Searching {get_options(group)}not authorized"
282+
raise AuthorizationError(description, response)
283+
267284
else:
268285
description = "Unable to get a result with the given search criteria"
269286
raise FossologyApiError(description, response)
287+
288+
def filesearch(
289+
self, filelist: List = [], group: str = None,
290+
):
291+
"""Search for files from hash sum
292+
293+
API Endpoint: POST /filesearch
294+
295+
The response does not generate Python objects yet, the plain JSON data is simply returned.
296+
297+
:param filelist: the list of files (or containers) to search for (default: [])
298+
:param group: the group name to choose while performing search (default: None)
299+
:type filelist: list
300+
:return: list of items corresponding to the search criteria
301+
:type group: string
302+
:rtype: JSON
303+
:raises FossologyApiError: if the REST call failed
304+
:raises AuthorizationError: if the user can't access the group
305+
"""
306+
headers = {}
307+
if group:
308+
headers["groupName"] = group
309+
310+
response = self.session.post(
311+
f"{self.api}/filesearch", headers=headers, json=filelist
312+
)
313+
314+
if response.status_code == 200:
315+
all_files = []
316+
for hash_file in response.json():
317+
if hash_file.get("findings"):
318+
all_files.append(File.from_json(hash_file))
319+
else:
320+
return "Unable to get a result with the given filesearch criteria"
321+
return all_files
322+
323+
elif response.status_code == 403:
324+
description = f"Searching {get_options(group)}not authorized"
325+
raise AuthorizationError(description, response)
326+
327+
else:
328+
description = "Unable to get a result with the given filesearch criteria"
329+
raise FossologyApiError(description, response)

fossology/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def __init__(self, description, response):
3535
class FossologyApiError(Error):
3636
"""Error during a Fossology GET request"""
3737

38-
def __init__(self, description, response):
38+
def __init__(self, description, response=None):
3939
try:
4040
message = response.json().get("message")
4141
except JSONDecodeError:

fossology/folders.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
import logging
55

6-
from .obj import Folder
7-
from .exceptions import AuthorizationError, FossologyApiError
6+
from fossology.obj import Folder, get_options
7+
from fossology.exceptions import AuthorizationError, FossologyApiError
88

99
logger = logging.getLogger(__name__)
1010
logger.setLevel(logging.DEBUG)
@@ -58,7 +58,7 @@ def detail_folder(self, folder_id):
5858
description = f"Error while getting details for folder {folder_id}"
5959
raise FossologyApiError(description, response)
6060

61-
def create_folder(self, parent, name, description=None):
61+
def create_folder(self, parent, name, description=None, group=None):
6262
"""Create a new (sub)folder
6363
6464
The name of the new folder must be unique under the same parent.
@@ -67,20 +67,25 @@ def create_folder(self, parent, name, description=None):
6767
6868
:param parent: the parent folder
6969
:param name: the name of the folder
70-
:param description: a meaningful description for the folder (optional)
70+
:param description: a meaningful description for the folder (default: None)
71+
:param group: the name of the group chosen to create the folder (default: None)
7172
:type parent: Folder() object
7273
:type name: str
7374
:type description: str
75+
:type group: string
7476
:return: the folder newly created (or already existing) - or None
7577
:rtype: Folder() object
7678
:raises FossologyApiError: if the REST call failed
77-
:raises AuthorizationError: if the user is not allowed to write in the folder
79+
:raises AuthorizationError: if the user is not allowed to write in the folder or access the group
7880
"""
7981
headers = {
8082
"parentFolder": f"{parent.id}",
8183
"folderName": f"{name}",
8284
"folderDescription": f"{description}",
8385
}
86+
if group:
87+
headers["groupName"] = group
88+
8489
response = self.session.post(f"{self.api}/folders", headers=headers)
8590

8691
if response.status_code == 200:
@@ -96,7 +101,7 @@ def create_folder(self, parent, name, description=None):
96101
return self.detail_folder(response.json()["message"])
97102

98103
elif response.status_code == 403:
99-
description = f"Folder creation in parent {parent} not authorized"
104+
description = f"Folder creation {get_options(group, parent)}not authorized"
100105
raise AuthorizationError(description, response)
101106
else:
102107
description = f"Unable to create folder {name} under {parent}"
@@ -130,7 +135,7 @@ def update_folder(self, folder, name=None, description=None):
130135
logger.info(f"{folder} has been updated")
131136
return folder
132137
else:
133-
description = f"Unable to update folder {folder}"
138+
description = f"Unable to update folder {folder.id}"
134139
raise FossologyApiError(description, response)
135140

136141
def delete_folder(self, folder):

fossology/jobs.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
import time
66
import logging
77

8-
from .obj import Job
9-
from .exceptions import FossologyApiError
8+
from fossology.obj import Job, get_options
9+
from fossology.exceptions import AuthorizationError, FossologyApiError
1010

1111
logger = logging.getLogger(__name__)
1212
logger.setLevel(logging.DEBUG)
@@ -15,30 +15,28 @@
1515
class Jobs:
1616
"""Class dedicated to all "jobs" related endpoints"""
1717

18-
def list_jobs(self, page_size=20, pages=1, upload=None):
18+
def list_jobs(self, page_size=20, page=1, upload=None):
1919
"""Get all available jobs
2020
2121
API Endpoint: GET /jobs
2222
2323
The answer is limited to the first page of 20 results by default
2424
2525
:param page_size: the maximum number of results per page
26-
:param pages: the number of pages to be retrieved
26+
:param page: the number of pages to be retrieved
2727
:param upload: list only jobs of the given upload (default: None)
2828
:type page_size: int (default: 20)
29-
:type pages: int (default: 1)
29+
:type page: int (default: 1)
3030
:type upload: Upload
3131
:return: the jobs data
3232
:rtype: list of Job
3333
:raises FossologyApiError: if the REST call failed
3434
"""
35-
headers = {"limit": str(page_size), "pages": str(pages)}
35+
params = {}
36+
headers = {"limit": str(page_size), "page": str(page)}
3637
if upload:
37-
response = self.session.get(
38-
f"{self.api}/jobs?upload={upload.id}", headers=headers
39-
)
40-
else:
41-
response = self.session.get(f"{self.api}/jobs", headers=headers)
38+
params["upload"] = upload.id
39+
response = self.session.get(f"{self.api}/jobs", params=params, headers=headers)
4240
if response.status_code == 200:
4341
jobs_list = list()
4442
for job in response.json():
@@ -54,7 +52,7 @@ def detail_job(self, job_id, wait=False, timeout=30):
5452
API Endpoint: GET /jobs/{id}
5553
5654
:param job_id: the id of the job
57-
:param wait: wait until the job is finisched (default: False)
55+
:param wait: wait until the job is finished (default: False)
5856
:param timeout: stop waiting after x seconds (default: 30)
5957
:type: int
6058
:type wait: boolean
@@ -84,7 +82,7 @@ def detail_job(self, job_id, wait=False, timeout=30):
8482
description = f"Error while getting details for job {job_id}"
8583
raise FossologyApiError(description, response)
8684

87-
def schedule_jobs(self, folder, upload, spec, wait=False, timeout=30):
85+
def schedule_jobs(self, folder, upload, spec, group=None, wait=False, timeout=30):
8886
"""Schedule jobs for a specific upload
8987
9088
API Endpoint: POST /jobs
@@ -123,30 +121,42 @@ def schedule_jobs(self, folder, upload, spec, wait=False, timeout=30):
123121
:param folder: the upload folder
124122
:param upload: the upload for which jobs will be scheduled
125123
:param spec: the job specification
124+
:param group: the group name to choose while scheduling jobs (default: None)
126125
:param wait: wait for the scheduled job to finish (default: False)
127126
:param timeout: stop waiting after x seconds (default: 30)
128127
:type upload: Upload
129128
:type folder: Folder
130129
:type spec: dict
130+
:type group: string
131131
:type wait: boolean
132132
:type timeout: 30
133133
:return: the job id
134134
:rtype: Job
135135
:raises FossologyApiError: if the REST call failed
136+
:raises AuthorizationError: if the user can't access the group
136137
"""
137138
headers = {
138139
"folderId": str(folder.id),
139140
"uploadId": str(upload.id),
140141
"Content-Type": "application/json",
141142
}
143+
if group:
144+
headers["groupName"] = group
145+
142146
response = self.session.post(
143147
f"{self.api}/jobs", headers=headers, data=json.dumps(spec)
144148
)
149+
145150
if response.status_code == 201:
146151
detailled_job = self.detail_job(
147152
response.json()["message"], wait=wait, timeout=timeout
148153
)
149154
return detailled_job
155+
156+
elif response.status_code == 403:
157+
description = f"Scheduling job {get_options(group)}not authorized"
158+
raise AuthorizationError(description, response)
159+
150160
else:
151-
description = "Scheduling jobs for upload {upload.uploadname} failed"
161+
description = f"Scheduling jobs for upload {upload.uploadname} failed"
152162
raise FossologyApiError(description, response)

0 commit comments

Comments
 (0)