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