66# Licensed as Siemens Inner Source, see top-level License.md file for details.
77# -------------------------------------------------------------------------------
88
9+ import re
10+
911"""Preview of High-Level, object oriented Python interface to the SW360 REST API.
1012For now, this does NOT strive to be stable or complete. Feel free to use it as
1113a more convenient abstraction for some (important) objects, but be prepared for
@@ -35,8 +37,63 @@ def __init__(self, json=None, resource_id=None, **kwargs):
3537 if json is not None :
3638 self .from_json (json )
3739
38- def from_json (self , json ):
39- pass
40+ def _parse_release_list (self , json_list , component_id = None ):
41+ """Parse a JSON list of releases, create according objects and add
42+ them to `container`."""
43+ releases = {}
44+ for release_json in json_list :
45+ release = Release (component_id = component_id )
46+ release .from_json (release_json )
47+ releases [release .id ] = release
48+ return releases
49+
50+ def _parse_attachment_list (self , json_list , resources = []):
51+ """Parse a JSON list of releases, create according objects and add
52+ them to `container`."""
53+ attachments = {}
54+ for attachment_json in json_list :
55+ attachment = Attachment (resources = resources )
56+ attachment .from_json (attachment_json )
57+ attachments [attachment .id ] = attachment
58+ return attachments
59+
60+ def _parse_link (self , key , links_key , links_value ):
61+ """Parse a _links or _embedded section in JSON"""
62+ if links_key == "sw360:component" :
63+ self .component_id = links_value ["href" ].split ("/" )[- 1 ]
64+ elif links_key == "sw360:downloadLink" :
65+ self .download_link = links_value ["href" ]
66+ elif links_key == "sw360:attachments" :
67+ self .attachments = self ._parse_attachment_list (
68+ links_value ,
69+ resources = [self ])
70+ elif links_key == "sw360:releases" :
71+ self .releases = self ._parse_release_list (
72+ links_value ,
73+ component_id = self .id )
74+ elif links_key == "self" :
75+ self .id = links_value ["href" ].split ("/" )[- 1 ]
76+ else :
77+ self .details .setdefault (key , {})
78+ self .details [key ][links_key ] = links_value
79+
80+ _camel_case_pattern = re .compile (r'(?<!^)(?=[A-Z])' )
81+
82+ def from_json (self , json , copy_attributes = list (), snake_case = True ):
83+ """`copy_attributes` will be copied as-is between this instance's
84+ attributes and JSON members. If `snake_case` is set, more Python-ish
85+ snake_case names will be used (project_type instead of projectType).
86+ """
87+ for key , value in json .items ():
88+ if key in copy_attributes :
89+ if snake_case :
90+ key = self ._camel_case_pattern .sub ('_' , key ).lower ()
91+ self .__setattr__ (key , value )
92+ elif key in ("_links" , "_embedded" ):
93+ for links_key , links_value in value .items ():
94+ self ._parse_link (key , links_key , links_value )
95+ else :
96+ self .details [key ] = value
4097
4198
4299class Release (SW360Resource ):
@@ -81,28 +138,9 @@ def from_json(self, json):
81138 external ids which will be stored as-is in `details['externalIds'].
82139 Please note that this might change in future if better abstractions
83140 will be added in this Python library."""
84- attachments = []
85- for key , value in json .items ():
86- if key in ("name" , "version" , "downloadurl" ):
87- self .__setattr__ (key , value )
88- elif key in ("_links" , "_embedded" ):
89- for links_key , links_value in value .items ():
90- if links_key == "sw360:component" :
91- self .component_id = links_value ["href" ].split ("/" )[- 1 ]
92- elif links_key == "sw360:attachments" :
93- attachments = links_value
94- elif links_key == "self" :
95- self .id = links_value ["href" ].split ("/" )[- 1 ]
96- else :
97- self .details .setdefault (key , {})
98- self .details [key ][links_key ] = links_value
99- else :
100- self .details [key ] = value
101-
102- for attachment_json in attachments :
103- attachment = Attachment (resources = [self ])
104- attachment .from_json (attachment_json )
105- self .attachments [attachment .id ] = attachment
141+ super ().from_json (
142+ json ,
143+ copy_attributes = ("name" , "version" , "downloadurl" ))
106144
107145 def __repr__ (self ):
108146 """Representation string."""
@@ -146,6 +184,7 @@ def __init__(self, json=None, attachment_id=None, resources={},
146184 self .filename = filename
147185 self .sha1 = sha1
148186 self .attachment_type = attachment_type
187+ self .download_link = None
149188 super ().__init__ (json , attachment_id , ** kwargs )
150189
151190 def from_json (self , json ):
@@ -155,22 +194,14 @@ def from_json(self, json):
155194
156195 All details not directly supported by this class will be stored as-is
157196 in the `details` instance attribute.
158- Please note that this might change in future if better abstractions
197+ Please note that this might change in future if more abstractions
159198 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
199+ super ().from_json (
200+ json ,
201+ copy_attributes = ("filename" , "sha1" , "attachmentType" ,
202+ "createdBy" , "createdTeam" , "createdComment" , "createdOn" ,
203+ "checkedBy" , "checkedTeam" , "checkedComment" , "checkedOn" ,
204+ "checkStatus" ))
174205
175206 def __repr__ (self ):
176207 """Representation string."""
@@ -227,37 +258,76 @@ def from_json(self, json):
227258 as-is in `details['_embedded']['sw360:vendors']` and
228259 `details['externalIds']. Please note that this might change in future
229260 if better abstractions will be added in this Python library."""
230- releases = []
231- attachments = []
232- for key , value in json .items ():
233- if key in ("name" , "description" , "homepage" ):
234- self .__setattr__ (key , value )
235- elif key == "componentType" :
236- self .component_type = value
237- elif key in ("_links" , "_embedded" ):
238- for links_key , links_value in value .items ():
239- if key == "_links" and links_key == "self" :
240- self .id = links_value ["href" ].split ("/" )[- 1 ]
241- elif links_key == "sw360:releases" :
242- releases = links_value
243- elif links_key == "sw360:attachments" :
244- attachments = links_value
245- else :
246- self .details .setdefault (key , {})
247- self .details [key ][links_key ] = links_value
248- else :
249- self .details [key ] = value
261+ super ().from_json (
262+ json ,
263+ copy_attributes = ("name" , "description" , "homepage" ,
264+ "componentType" ))
250265
251- for release_json in releases :
252- release = Release (component_id = self .id )
253- release .from_json (release_json )
254- self .releases [release .id ] = release
266+ def __repr__ (self ):
267+ """Representation string."""
268+ return "<Component %s id:%s>" % (self .name , self .id )
255269
256- for attachment_json in attachments :
257- attachment = Attachment (resources = [self ])
258- attachment .from_json (attachment_json )
259- self .attachments [attachment .id ] = attachment
270+
271+ class Project (SW360Resource ):
272+ """A project is SW360 abstraction for a collection of software components
273+ used in a project/product. It can contain links to other `Project`s or
274+ `Release`s.
275+
276+ You can either create it from a SW360 `json` object or by specifying the
277+ details via the constructor parameters, see list below. Only the most
278+ important attributes are supported, rest hast be provided via `kwargs` and
279+ is stored in the `details` attribute of instances.
280+
281+ For JSON parsing, please read documentation of from_json() method.
282+
283+ :param json: create component from SW360 JSON object by calling from_json()
284+ :param project_id: id of the project (if exists in SW360 already)
285+ :param name: name of the project
286+ :param version: version of the project
287+ :param description: short description for project
288+ :param visibility: project visibility in SW360, one of "PRIVATE",
289+ "ME_AND_MODERATORS", "BUISNESSUNIT_AND_MODERATORS",
290+ "EVERYONE"
291+ :param project_type: one of "CUSTOMER", "INTERNAL", "PRODUCT", "SERVICE",
292+ "INNER_SOURCE"
293+ :param kwargs: additional project details as specified in the SW360 REST API
294+ :type json: SW360 JSON object
295+ :type project_id: string
296+ :type name: string
297+ :type version: string
298+ :type description: string
299+ :type visibility: string
300+ :type project_type: string
301+ :type kwargs: dictionary
302+ """
303+ def __init__ (self , json = None , project_id = None , name = None , version = None ,
304+ description = None , visibility = None , project_type = None ,
305+ ** kwargs ):
306+ self .releases = {}
307+
308+ self .name = name
309+ self .version = version
310+ self .description = description
311+ self .visibility = visibility
312+ self .project_type = project_type
313+ super ().__init__ (json , project_id , ** kwargs )
314+
315+ def from_json (self , json ):
316+ """Parse project JSON object from SW360 REST API. Information for
317+ its releases will be extracted, Release() objects created for them
318+ and stored in the `releases` instance attribue. Please note that
319+ the REST API will only provide basic information for the releases.
320+
321+ All details not directly supported by this class will be
322+ stored as-is in the `details` instance attribute. For now, this also
323+ includes linked projects and external ids. Please note that this might
324+ change in future if better abstractions will be added in this Python
325+ library."""
326+ super ().from_json (
327+ json ,
328+ copy_attributes = ("name" , "description" , "version" , "visibility" ,
329+ "projectType" ))
260330
261331 def __repr__ (self ):
262332 """Representation string."""
263- return "<Component %s id:%s>" % (self .name , self .id )
333+ return "<Project %s id:%s>" % (self .name , self .id )
0 commit comments