99import logging
1010import sys
1111
12+ from typing import List , Tuple
13+
1214import sw360
15+ from cyclonedx .model import ExternalReferenceType , HashAlgorithm
1316from cyclonedx .model .bom import Bom
17+ from cyclonedx .model .component import Component
1418
1519import capycli .common .script_base
1620from capycli import get_logger
17- from capycli .bom . legacy import LegacySupport
18- from capycli . common . capycli_bom_support import CaPyCliBom , SbomCreator
21+ from capycli .common . capycli_bom_support import CaPyCliBom , SbomCreator , CycloneDxSupport
22+
1923from capycli .common .print import print_red , print_text , print_yellow
2024from capycli .main .result_codes import ResultCode
2125
@@ -32,20 +36,21 @@ def get_external_id(self, name: str, release_details: dict):
3236
3337 return release_details ["externalIds" ].get (name , "" )
3438
35- def get_attachment (self , att_type : str , release_details : dict ) -> dict :
36- """Returns the first attachment with the given type or None ."""
39+ def get_attachments (self , att_types : Tuple [ str ] , release_details : dict ) -> List [ dict ] :
40+ """Returns the attachments with the given types or empty list ."""
3741 if "_embedded" not in release_details :
3842 return None
3943
4044 if "sw360:attachments" not in release_details ["_embedded" ]:
4145 return None
4246
47+ found = []
4348 attachments = release_details ["_embedded" ]["sw360:attachments" ]
4449 for attachment in attachments :
45- if attachment ["attachmentType" ] == att_type :
46- return attachment
50+ if attachment ["attachmentType" ] in att_types :
51+ found . append ( attachment )
4752
48- return None
53+ return found
4954
5055 def get_clearing_state (self , proj , href ) -> str :
5156 """Returns the clearing state of the given component/release"""
@@ -64,53 +69,66 @@ def create_project_bom(self, project) -> list:
6469 for release in releases :
6570 print_text (" " , release ["name" ], release ["version" ])
6671 href = release ["_links" ]["self" ]["href" ]
67- state = self .get_clearing_state (project , href )
68-
69- rel_item = {}
70- rel_item ["Name" ] = release ["name" ]
71- rel_item ["Version" ] = release ["version" ]
72- rel_item ["ProjectClearingState" ] = state
73- rel_item ["Id" ] = self .client .get_id_from_href (href )
74- rel_item ["Sw360Id" ] = rel_item ["Id" ]
75- rel_item ["Url" ] = (
76- self .sw360_url
77- + "group/guest/components/-/component/release/detailRelease/"
78- + self .client .get_id_from_href (href ))
7972
8073 try :
8174 release_details = self .client .get_release_by_url (href )
82- # capycli.common.json_support.print_json(release_details)
83- rel_item ["ClearingState" ] = release_details ["clearingState" ]
84- rel_item ["ReleaseMainlineState" ] = release_details .get ("mainlineState" , "" )
8575
86- rel_item ["Language" ] = self .list_to_string (release_details .get ("languages" , "" ))
87- rel_item ["SourceFileUrl" ] = release_details .get ("sourceCodeDownloadurl" , "" )
88- rel_item ["BinaryFileUrl" ] = release_details .get ("binaryDownloadurl" , "" )
89-
90- rel_item ["RepositoryId" ] = self .get_external_id ("package-url" , release_details )
91- if not rel_item ["RepositoryId" ]:
76+ purl = self .get_external_id ("package-url" , release_details )
77+ if not purl :
9278 # try another id name
93- rel_item ["RepositoryId" ] = self .get_external_id ("purl" , release_details )
94- if rel_item ["RepositoryId" ]:
95- rel_item ["RepositoryType" ] = "package-url"
96-
97- if "repository" in release_details :
98- rel_item ["RepositoryUrl" ] = release_details ["repository" ].get ("url" , "" )
99-
100- source_attachment = self .get_attachment ("SOURCE" , release_details )
101- if source_attachment :
102- rel_item ["SourceFile" ] = source_attachment .get ("filename" , "" )
103- rel_item ["SourceFileHash" ] = source_attachment .get ("sha1" , "" )
104-
105- binary_attachment = self .get_attachment ("BINARY" , release_details )
106- if binary_attachment :
107- rel_item ["BinaryFile" ] = binary_attachment .get ("filename" , "" )
108- rel_item ["BinaryFileHash" ] = binary_attachment .get ("sha1" , "" )
79+ purl = self .get_external_id ("purl" , release_details )
80+
81+ if purl :
82+ rel_item = Component (name = release ["name" ], version = release ["version" ], purl = purl , bom_ref = purl )
83+ else :
84+ rel_item = Component (name = release ["name" ], version = release ["version" ])
85+
86+ for key , property in (("clearingState" , CycloneDxSupport .CDX_PROP_CLEARING_STATE ),
87+ ("mainlineState" , CycloneDxSupport .CDX_PROP_REL_STATE )):
88+ if key in release_details and release_details [key ]:
89+ CycloneDxSupport .set_property (rel_item , property , release_details [key ])
90+
91+ if "languages" in release_details and release_details ["languages" ]:
92+ languages = self .list_to_string (release_details ["languages" ])
93+ CycloneDxSupport .set_property (rel_item , CycloneDxSupport .CDX_PROP_LANGUAGE , languages )
94+
95+ for key , comment in (("sourceCodeDownloadurl" , CaPyCliBom .SOURCE_URL_COMMENT ),
96+ ("binaryDownloadurl" , CaPyCliBom .BINARY_URL_COMMENT )):
97+ if key in release_details and release_details [key ]:
98+ # add hash from attachment (see below) also here if same filename?
99+ CycloneDxSupport .set_ext_ref (rel_item , ExternalReferenceType .DISTRIBUTION ,
100+ comment , release_details [key ])
101+
102+ if "repository" in release_details and "url" in release_details ["repository" ]:
103+ CycloneDxSupport .set_ext_ref (rel_item , ExternalReferenceType .VCS , comment = None ,
104+ value = release_details ["repository" ]["url" ])
105+
106+ for at_type , comment in (("SOURCE" , CaPyCliBom .SOURCE_FILE_COMMENT ),
107+ ("BINARY" , CaPyCliBom .BINARY_FILE_COMMENT )):
108+ attachments = self .get_attachments ((at_type , at_type + "_SELF" ), release_details )
109+ for attachment in attachments :
110+ CycloneDxSupport .set_ext_ref (rel_item , ExternalReferenceType .DISTRIBUTION ,
111+ comment , attachment ["filename" ],
112+ HashAlgorithm .SHA_1 , attachment .get ("sha1" ))
109113
110114 except sw360 .SW360Error as swex :
111115 print_red (" ERROR: unable to access project:" + repr (swex ))
112116 sys .exit (ResultCode .RESULT_ERROR_ACCESSING_SW360 )
113117
118+ state = self .get_clearing_state (project , href )
119+ if state :
120+ CycloneDxSupport .set_property (rel_item , CycloneDxSupport .CDX_PROP_PROJ_STATE , state )
121+
122+ sw360_id = self .client .get_id_from_href (href )
123+ CycloneDxSupport .set_property (rel_item , CycloneDxSupport .CDX_PROP_SW360ID , sw360_id )
124+
125+ CycloneDxSupport .set_property (
126+ rel_item ,
127+ CycloneDxSupport .CDX_PROP_SW360_URL ,
128+ self .sw360_url
129+ + "group/guest/components/-/component/release/detailRelease/"
130+ + sw360_id )
131+
114132 bom .append (rel_item )
115133
116134 # sub-projects are not handled at the moment
@@ -126,12 +144,7 @@ def create_project_cdx_bom(self, project_id) -> Bom:
126144
127145 print_text (" Project name: " + project ["name" ] + ", " + project ["version" ])
128146
129- bom = self .create_project_bom (project )
130-
131- cdx_components = []
132- for item in bom :
133- cx_comp = LegacySupport .legacy_component_to_cdx (item )
134- cdx_components .append (cx_comp )
147+ cdx_components = self .create_project_bom (project )
135148
136149 creator = SbomCreator ()
137150 sbom = creator .create (cdx_components , addlicense = True , addprofile = True , addtools = True ,
0 commit comments