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