Skip to content

Commit 5987785

Browse files
committed
feat: Attachment() and base class SW360Resource
1 parent 5967310 commit 5987785

2 files changed

Lines changed: 143 additions & 47 deletions

File tree

sw360/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@
1414
from .sw360oauth2 import SW360OAuth2 # noqa: F401
1515
from .sw360_objects import Component, Release
1616

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

sw360/sw360_objects.py

Lines changed: 141 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,40 @@
66
# Licensed as Siemens Inner Source, see top-level License.md file for details.
77
# -------------------------------------------------------------------------------
88

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

1414

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

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

5374
def from_json(self, json):
5475
"""Parse release JSON object from SW360 REST API. The component it
@@ -57,17 +78,19 @@ def from_json(self, json):
5778
5879
All details not directly supported by this class will be stored as-is
5980
in the `details` instance attribute. For now, this also includes
60-
attachment information and external ids which will be stored as-is in
61-
`details['_embedded']['sw360:attachments']` and
62-
`details['externalIds']. Please note that this might change in future
63-
if better abstractions will be added in this Python library."""
81+
external ids which will be stored as-is in `details['externalIds'].
82+
Please note that this might change in future if better abstractions
83+
will be added in this Python library."""
84+
attachments = []
6485
for key, value in json.items():
6586
if key in ("name", "version", "downloadurl"):
6687
self.__setattr__(key, value)
67-
elif key == "_links":
88+
elif key in ("_links", "_embedded"):
6889
for links_key, links_value in value.items():
6990
if links_key == "sw360:component":
7091
self.component_id = links_value["href"].split("/")[-1]
92+
elif links_key == "sw360:attachments":
93+
attachments = links_value
7194
elif links_key == "self":
7295
self.id = links_value["href"].split("/")[-1]
7396
else:
@@ -76,12 +99,85 @@ def from_json(self, json):
7699
else:
77100
self.details[key] = value
78101

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

83111

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

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

128218
def from_json(self, json):
129219
"""Parse component JSON object from SW360 REST API. Information for
@@ -137,7 +227,8 @@ def from_json(self, json):
137227
as-is in `details['_embedded']['sw360:vendors']` and
138228
`details['externalIds']. Please note that this might change in future
139229
if better abstractions will be added in this Python library."""
140-
releases = None
230+
releases = []
231+
attachments = []
141232
for key, value in json.items():
142233
if key in ("name", "description", "homepage"):
143234
self.__setattr__(key, value)
@@ -149,20 +240,24 @@ def from_json(self, json):
149240
self.id = links_value["href"].split("/")[-1]
150241
elif links_key == "sw360:releases":
151242
releases = links_value
243+
elif links_key == "sw360:attachments":
244+
attachments = links_value
152245
else:
153246
self.details.setdefault(key, {})
154247
self.details[key][links_key] = links_value
155248
else:
156249
self.details[key] = value
157250

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

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

0 commit comments

Comments
 (0)