Skip to content

Commit 2fafc62

Browse files
committed
feat(sw360_objects): add Project class plus refactoring
Refactor the from_json() methods to one common version.
1 parent 58f90ee commit 2fafc62

2 files changed

Lines changed: 126 additions & 69 deletions

File tree

sw360/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .sw360_api import SW360 # noqa: F401
1313
from .sw360error import SW360Error # noqa: F401
1414
from .sw360oauth2 import SW360OAuth2 # noqa: F401
15-
from .sw360_objects import Component, Release, Attachment
15+
from .sw360_objects import Component, Release, Attachment, Project
1616

17-
__all__ = ["SW360", "SW360Error", "SW360OAuth2", "Component", "Release", "Attachment"]
17+
__all__ = ["SW360", "SW360Error", "SW360OAuth2", "Component", "Release",
18+
"Attachment", "Project"]

sw360/sw360_objects.py

Lines changed: 123 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,54 @@ def __init__(self, json=None, resource_id=None, **kwargs):
3636
if json is not None:
3737
self.from_json(json)
3838

39-
def from_json(self, json):
40-
pass
39+
def parse_release_list(self, json_list, component_id=None):
40+
"""Parse a JSON list of releases, create according objects and add
41+
them to `container`."""
42+
releases = {}
43+
for release_json in json_list:
44+
release = Release(component_id=component_id)
45+
release.from_json(release_json)
46+
releases[release.id] = release
47+
return releases
48+
49+
def parse_attachment_list(self, json_list, resources=[]):
50+
"""Parse a JSON list of releases, create according objects and add
51+
them to `container`."""
52+
attachments = {}
53+
for attachment_json in json_list:
54+
attachment = Attachment(resources=resources)
55+
attachment.from_json(attachment_json)
56+
attachments[attachment.id] = attachment
57+
return attachments
58+
59+
def from_json(self, json, copy_attributes=list(), rename_attributes={}):
60+
"""`copy_attributes` will be copied as-is between this instance's
61+
attributes and JSON members. `rename_attributes` may contain a
62+
dictionary to map JSON attributes to instance attributes."""
63+
for key, value in json.items():
64+
if key in copy_attributes:
65+
self.__setattr__(key, value)
66+
elif key in rename_attributes:
67+
self.__setattr__(rename_attributes[key], value)
68+
elif key in ("_links", "_embedded"):
69+
for links_key, links_value in value.items():
70+
if links_key == "sw360:component":
71+
self.component_id = links_value["href"].split("/")[-1]
72+
elif links_key == "sw360:attachments":
73+
self.attachments = self.parse_attachment_list(
74+
links_value,
75+
resources=[self])
76+
elif links_key == "sw360:releases":
77+
self.releases = self.parse_release_list(
78+
links_value,
79+
component_id=self.id)
80+
elif links_key == "self":
81+
self.id = links_value["href"].split("/")[-1]
82+
else:
83+
self.details.setdefault(key, {})
84+
self.details[key][links_key] = links_value
85+
else:
86+
self.details[key] = value
4187

4288

4389
class Release(SW360Resource):
@@ -82,28 +128,9 @@ def from_json(self, json):
82128
external ids which will be stored as-is in `details['externalIds'].
83129
Please note that this might change in future if better abstractions
84130
will be added in this Python library."""
85-
attachments = []
86-
for key, value in json.items():
87-
if key in ("name", "version", "downloadurl"):
88-
self.__setattr__(key, value)
89-
elif key in ("_links", "_embedded"):
90-
for links_key, links_value in value.items():
91-
if links_key == "sw360:component":
92-
self.component_id = links_value["href"].split("/")[-1]
93-
elif links_key == "sw360:attachments":
94-
attachments = links_value
95-
elif links_key == "self":
96-
self.id = links_value["href"].split("/")[-1]
97-
else:
98-
self.details.setdefault(key, {})
99-
self.details[key][links_key] = links_value
100-
else:
101-
self.details[key] = value
102-
103-
for attachment_json in attachments:
104-
attachment = Attachment(resources=[self])
105-
attachment.from_json(attachment_json)
106-
self.attachments[attachment.id] = attachment
131+
super().from_json(
132+
json,
133+
copy_attributes=("name", "version", "downloadurl"))
107134

108135
def __repr__(self):
109136
"""Representation string."""
@@ -158,20 +185,10 @@ def from_json(self, json):
158185
in the `details` instance attribute.
159186
Please note that this might change in future if better abstractions
160187
will be added in this Python library."""
161-
for key, value in json.items():
162-
if key in ("filename", "sha1"):
163-
self.__setattr__(key, value)
164-
elif key == "attachmentType":
165-
self.attachment_type = value
166-
elif key == "_links":
167-
for links_key, links_value in value.items():
168-
if links_key == "self":
169-
self.id = links_value["href"].split("/")[-1]
170-
else:
171-
self.details.setdefault(key, {})
172-
self.details[key][links_key] = links_value
173-
else:
174-
self.details[key] = value
188+
super().from_json(
189+
json,
190+
copy_attributes=("filename", "sha1"),
191+
rename_attributes={"attachmentType": "attachment_type"})
175192

176193
def __repr__(self):
177194
"""Representation string."""
@@ -228,37 +245,76 @@ def from_json(self, json):
228245
as-is in `details['_embedded']['sw360:vendors']` and
229246
`details['externalIds']. Please note that this might change in future
230247
if better abstractions will be added in this Python library."""
231-
releases = []
232-
attachments = []
233-
for key, value in json.items():
234-
if key in ("name", "description", "homepage"):
235-
self.__setattr__(key, value)
236-
elif key == "componentType":
237-
self.component_type = value
238-
elif key in ("_links", "_embedded"):
239-
for links_key, links_value in value.items():
240-
if key == "_links" and links_key == "self":
241-
self.id = links_value["href"].split("/")[-1]
242-
elif links_key == "sw360:releases":
243-
releases = links_value
244-
elif links_key == "sw360:attachments":
245-
attachments = links_value
246-
else:
247-
self.details.setdefault(key, {})
248-
self.details[key][links_key] = links_value
249-
else:
250-
self.details[key] = value
248+
super().from_json(
249+
json,
250+
copy_attributes=("name", "description", "homepage"),
251+
rename_attributes={"componentType": "component_type"})
251252

252-
for release_json in releases:
253-
release = Release(component_id=self.id)
254-
release.from_json(release_json)
255-
self.releases[release.id] = release
253+
def __repr__(self):
254+
"""Representation string."""
255+
return "<Component %s id:%s>" % (self.name, self.id)
256256

257-
for attachment_json in attachments:
258-
attachment = Attachment(resources=[self])
259-
attachment.from_json(attachment_json)
260-
self.attachments[attachment.id] = attachment
257+
258+
class Project(SW360Resource):
259+
"""A project is SW360 abstraction for a collection of software components
260+
used in a project/product. It can contain links to other `Project`s or
261+
`Release`s.
262+
263+
You can either create it from a SW360 `json` object or by specifying the
264+
details via the constructor parameters, see list below. Only the most
265+
important attributes are supported, rest hast be provided via `kwargs` and
266+
is stored in the `details` attribute of instances.
267+
268+
For JSON parsing, please read documentation of from_json() method.
269+
270+
:param json: create component from SW360 JSON object by calling from_json()
271+
:param project_id: id of the project (if exists in SW360 already)
272+
:param name: name of the project
273+
:param version: version of the project
274+
:param description: short description for project
275+
:param visibility: project visibility in SW360, one of "PRIVATE",
276+
"ME_AND_MODERATORS", "BUISNESSUNIT_AND_MODERATORS",
277+
"EVERYONE"
278+
:param project_type: one of "CUSTOMER", "INTERNAL", "PRODUCT", "SERVICE",
279+
"INNER_SOURCE"
280+
:param kwargs: additional project details as specified in the SW360 REST API
281+
:type json: SW360 JSON object
282+
:type project_id: string
283+
:type name: string
284+
:type version: string
285+
:type description: string
286+
:type visibility: string
287+
:type project_type: string
288+
:type kwargs: dictionary
289+
"""
290+
def __init__(self, json=None, project_id=None, name=None, version=None,
291+
description=None, visibility=None, project_type=None,
292+
**kwargs):
293+
self.releases = {}
294+
295+
self.name = name
296+
self.version = version
297+
self.description = description
298+
self.visibility = visibility
299+
self.project_type = project_type
300+
super().__init__(json, project_id, **kwargs)
301+
302+
def from_json(self, json):
303+
"""Parse project JSON object from SW360 REST API. Information for
304+
its releases will be extracted, Release() objects created for them
305+
and stored in the `releases` instance attribue. Please note that
306+
the REST API will only provide basic information for the releases.
307+
308+
All details not directly supported by this class will be
309+
stored as-is in the `details` instance attribute. For now, this also
310+
includes linked projects and external ids. Please note that this might
311+
change in future if better abstractions will be added in this Python
312+
library."""
313+
super().from_json(
314+
json,
315+
copy_attributes=("name", "description", "version", "visibility"),
316+
rename_attributes={"projectType": "project_type"})
261317

262318
def __repr__(self):
263319
"""Representation string."""
264-
return "<Component %s id:%s>" % (self.name, self.id)
320+
return "<Project %s id:%s>" % (self.name, self.id)

0 commit comments

Comments
 (0)