Skip to content

Commit 58f90ee

Browse files
committed
feat: Attachment() and base class SW360Resource
1 parent a2e7971 commit 58f90ee

2 files changed

Lines changed: 144 additions & 48 deletions

File tree

sw360/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
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
16-
__all__ = ["SW360", "SW360Error", "SW360OAuth2", "Component", "Release"]
15+
from .sw360_objects import Component, Release, Attachment
16+
17+
__all__ = ["SW360", "SW360Error", "SW360OAuth2", "Component", "Release", "Attachment"]

sw360/sw360_objects.py

Lines changed: 141 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,40 @@
77
# SPDX-License-Identifier: MIT
88
# -------------------------------------------------------------------------------
99

10-
"""High-Level Python interface to the SW360 REST API. This is a set of Python
11-
classes abstracting most important aspects of the SW360 REST API. For now, this
12-
does NOT strive to provide a complete API abstraction, just provide a more
13-
convenient abstraction for some (important) objects."""
10+
"""Preview of High-Level, object oriented Python interface to the SW360 REST API.
11+
For now, this does NOT strive to be stable or complete. Feel free to use it as
12+
a more convenient abstraction for some (important) objects, but be prepared for
13+
changes."""
1414

1515

16-
class Release:
16+
class SW360Resource:
17+
"""Base class representing an SW360 resource like project, component,
18+
release etc.
19+
"""
20+
21+
def __init__(self, json=None, resource_id=None, **kwargs):
22+
self.details = {}
23+
"""All resource details which are not explicitely supported by the
24+
constructor parameters of the derived objexts are stored in the
25+
`details` attribute. Shall use names and types as specified in SW360
26+
REST API."""
27+
28+
self.id = resource_id
29+
"""All SW360 resource instances have an `id`. If it is set to `None`,
30+
the object is yet unknown to SW360 - otherwise, it stores the SW360
31+
id (release_id, component_id, etc.)."""
32+
33+
for key, value in kwargs:
34+
self.details[key] = value
35+
36+
if json is not None:
37+
self.from_json(json)
38+
39+
def from_json(self, json):
40+
pass
41+
42+
43+
class Release(SW360Resource):
1744
"""A release is the SW360 abstraction for a single version of a component.
1845
1946
You can either create it from a SW360 `json` object or by specifying the
@@ -27,29 +54,23 @@ class Release:
2754
:param component_id: SW360 id of the component the release belongs to
2855
:param version: the actual version
2956
:param downloadurl: URL the release was downloaded from
57+
:param release_id: id of the release (if exists in SW360 already)
3058
:param kwargs: additional relase details as specified in the SW360 REST API
3159
:type json: SW360 JSON object
3260
:type component_id: string
3361
:type version: string
3462
:type downloadurl: string
63+
:type release_id: string
3564
:type kwargs: dictionary
36-
3765
"""
38-
def __init__(self, json=None, component_id=None, version=None,
39-
downloadurl=None, **kwargs):
40-
self.details = {}
41-
"""All release details which are not explicitely supported by the
42-
constructor parameters are stored in the `details` attribute. Shall use
43-
names and types as specified in SW360 REST API."""
66+
def __init__(self, json=None, release_id=None, component_id=None,
67+
version=None, downloadurl=None, **kwargs):
68+
self.attachments = {}
4469

45-
if json is not None:
46-
self.from_json(json)
47-
else:
48-
self.component_id = component_id
49-
self.version = version
50-
self.downloadurl = downloadurl
51-
for key, value in kwargs:
52-
self.details[key] = value
70+
self.component_id = component_id
71+
self.version = version
72+
self.downloadurl = downloadurl
73+
super().__init__(json, release_id, **kwargs)
5374

5475
def from_json(self, json):
5576
"""Parse release JSON object from SW360 REST API. The component it
@@ -58,17 +79,19 @@ def from_json(self, json):
5879
5980
All details not directly supported by this class will be stored as-is
6081
in the `details` instance attribute. For now, this also includes
61-
attachment information and external ids which will be stored as-is in
62-
`details['_embedded']['sw360:attachments']` and
63-
`details['externalIds']. Please note that this might change in future
64-
if better abstractions will be added in this Python library."""
82+
external ids which will be stored as-is in `details['externalIds'].
83+
Please note that this might change in future if better abstractions
84+
will be added in this Python library."""
85+
attachments = []
6586
for key, value in json.items():
6687
if key in ("name", "version", "downloadurl"):
6788
self.__setattr__(key, value)
68-
elif key == "_links":
89+
elif key in ("_links", "_embedded"):
6990
for links_key, links_value in value.items():
7091
if links_key == "sw360:component":
7192
self.component_id = links_value["href"].split("/")[-1]
93+
elif links_key == "sw360:attachments":
94+
attachments = links_value
7295
elif links_key == "self":
7396
self.id = links_value["href"].split("/")[-1]
7497
else:
@@ -77,12 +100,85 @@ def from_json(self, json):
77100
else:
78101
self.details[key] = value
79102

103+
for attachment_json in attachments:
104+
attachment = Attachment(resources=[self])
105+
attachment.from_json(attachment_json)
106+
self.attachments[attachment.id] = attachment
107+
80108
def __repr__(self):
81109
"""Representation string."""
82110
return "<Release %s %s id:%s>" % (self.name, self.version, self.id)
83111

84112

85-
class Component:
113+
class Attachment(SW360Resource):
114+
"""An attachment is used to store all kinds of files in SW360, for example
115+
upstream source files (attachment_type "SOURCE"), self-created source file bundles
116+
("SOURCE_SELF"), clearing reports ("CLEARING_REPORT") or CLI files
117+
("COMPONENT_LICENSE_INFO_XML").
118+
119+
You can either create it from a SW360 `json` object or by specifying the
120+
details via the constructor parameters, see list below. Only the most
121+
important attributes are supported, rest hast be provided via `kwargs` and
122+
is stored in the `details` attribute of instances.
123+
124+
For JSON parsing, please read documentation of from_json() method.
125+
126+
:param json: create release from SW360 JSON object by calling from_json()
127+
:param attachment_id: SW360 id of the attachment (if it exists already)
128+
:param resources: dictionary of SW360 resource objects the attachment belongs to
129+
(instances of Release(), Component() or Project() with id as key)
130+
:param filename: the filename of the attachment
131+
:param sha1: SHA1 sum of the file to check its integrity
132+
:param attachment_type: one of "DOCUMENT", "SOURCE", "SOURCE_SELF"
133+
"CLEARING_REPORT", "COMPONENT_LICENSE_INFO_XML", "BINARY",
134+
"BINARY_SELF", "LICENSE_AGREEMENT", "README_OSS"
135+
:param kwargs: additional relase details as specified in the SW360 REST API
136+
:type json: SW360 JSON object
137+
:type attachment_id: string
138+
:type release_id: string
139+
:type filename: string
140+
:type sha1: string
141+
:type attachment_type: string
142+
:type kwargs: dictionary
143+
"""
144+
def __init__(self, json=None, attachment_id=None, resources={},
145+
filename=None, sha1=None, attachment_type=None, **kwargs):
146+
self.resources = resources
147+
self.filename = filename
148+
self.sha1 = sha1
149+
self.attachment_type = attachment_type
150+
super().__init__(json, attachment_id, **kwargs)
151+
152+
def from_json(self, json):
153+
"""Parse attachment JSON object from SW360 REST API. For now, we don't
154+
support parsing the resource the attachment belongs to, so this needs
155+
to be set via constructur.
156+
157+
All details not directly supported by this class will be stored as-is
158+
in the `details` instance attribute.
159+
Please note that this might change in future if better abstractions
160+
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
175+
176+
def __repr__(self):
177+
"""Representation string."""
178+
return "<Attachment %s id:%s>" % (self.filename, self.id)
179+
180+
181+
class Component(SW360Resource):
86182
"""A component is the SW360 abstraction for a single software
87183
package/library/program/etc.
88184
@@ -94,37 +190,31 @@ class Component:
94190
For JSON parsing, please read documentation of from_json() method.
95191
96192
:param json: create component from SW360 JSON object by calling from_json()
193+
:param component_id: id of the component (if exists in SW360 already)
97194
:param name: name of the component
98195
:param description: short description for component
99196
:param homepage: homepage of the component
100197
:param component_type: one of "INTERNAL", "OSS", "COTS", "FREESOFTWARE",
101198
"INNER_SOURCE", "SERVICE", "CODE_SNIPPET"
102199
:param kwargs: additional component details as specified in the SW360 REST API
103200
:type json: SW360 JSON object
201+
:type component_id: string
104202
:type name: string
105203
:type description: string
106204
:type homepage: string
107205
:type component_type: string
108206
:type kwargs: dictionary
109207
"""
110-
def __init__(self, json=None, name=None, description=None, homepage=None,
111-
component_type=None, **kwargs):
112-
self.details = {}
113-
"""All release details which are not explicitely supported by the
114-
constructor parameters are stored in the `details` attribute. Shall use
115-
names and types as specified in SW360 REST API."""
116-
208+
def __init__(self, json=None, component_id=None, name=None, description=None,
209+
homepage=None, component_type=None, **kwargs):
117210
self.releases = {}
211+
self.attachments = {}
118212

119-
if json is not None:
120-
self.from_json(json)
121-
else:
122-
self.name = name
123-
self.description = description
124-
self.homepage = homepage
125-
self.component_type = component_type
126-
for key, value in kwargs:
127-
self.details[key] = value
213+
self.name = name
214+
self.description = description
215+
self.homepage = homepage
216+
self.component_type = component_type
217+
super().__init__(json, component_id, **kwargs)
128218

129219
def from_json(self, json):
130220
"""Parse component JSON object from SW360 REST API. Information for
@@ -138,7 +228,8 @@ def from_json(self, json):
138228
as-is in `details['_embedded']['sw360:vendors']` and
139229
`details['externalIds']. Please note that this might change in future
140230
if better abstractions will be added in this Python library."""
141-
releases = None
231+
releases = []
232+
attachments = []
142233
for key, value in json.items():
143234
if key in ("name", "description", "homepage"):
144235
self.__setattr__(key, value)
@@ -150,20 +241,24 @@ def from_json(self, json):
150241
self.id = links_value["href"].split("/")[-1]
151242
elif links_key == "sw360:releases":
152243
releases = links_value
244+
elif links_key == "sw360:attachments":
245+
attachments = links_value
153246
else:
154247
self.details.setdefault(key, {})
155248
self.details[key][links_key] = links_value
156249
else:
157250
self.details[key] = value
158251

159-
if releases is None:
160-
return
161-
162252
for release_json in releases:
163253
release = Release(component_id=self.id)
164254
release.from_json(release_json)
165255
self.releases[release.id] = release
166256

257+
for attachment_json in attachments:
258+
attachment = Attachment(resources=[self])
259+
attachment.from_json(attachment_json)
260+
self.attachments[attachment.id] = attachment
261+
167262
def __repr__(self):
168263
"""Representation string."""
169264
return "<Component %s id:%s>" % (self.name, self.id)

0 commit comments

Comments
 (0)