10
10
from . import constants as C
11
11
from .sap_api_common import _request , https_session
12
12
from .sap_id_sso import _get_sso_endpoint_meta
13
+ from .sap_launchpad_software_center_download_search_fuzzy import *
13
14
14
15
logger = logging .getLogger (__name__ )
15
16
16
17
_HAS_DOWNLOAD_AUTHORIZATION = None
17
18
MAX_RETRY_TIMES = 3
18
19
19
20
20
- def search_software_filename (name , deduplicate ):
21
- """Return a single software that matched the filename
21
+ def search_software_filename (name , deduplicate , search_alternatives ):
22
22
"""
23
- search_results = _search_software (name )
24
- softwares = [r for r in search_results if r ['Title' ] == name or r ['Description' ] == name ]
25
- num_files = len (softwares )
26
- if num_files == 0 :
27
- raise ValueError (f'no result found for { name } ' )
28
- elif num_files > 1 and deduplicate == '' :
29
- names = [s ['Title' ] for s in softwares ]
30
- raise ValueError ('more than one results were found: %s. '
23
+ Execute search for SAP Software or its alternative when search_alternatives is true.
24
+
25
+ Args:
26
+ name: The filename name to check (e.g. 'SAPCAR_1115-70006178.EXE').
27
+ deduplicate: Select deduplication logic from 'first', 'last'
28
+ search_alternatives: Boolean for enabling fuzzy search.
29
+
30
+ Returns:
31
+ download_link: Download link of matched SAP Software.
32
+ filename: File name of matched SAP Software.
33
+ alternative_found: True if alternative search was successful.
34
+ """
35
+
36
+ alternative_found = False
37
+ software_search = _search_software (name )
38
+ software_filtered = [r for r in software_search if r ['Title' ] == name or r ['Description' ] == name ]
39
+
40
+ files_count = len (software_filtered )
41
+ if files_count == 0 :
42
+ # Run fuzzy search if search_alternatives was selected
43
+ if search_alternatives :
44
+ software_fuzzy_found = search_software_fuzzy (name )
45
+ software_fuzzy_filtered , suggested_filename = filter_fuzzy_search (software_fuzzy_found , name )
46
+ if len (software_fuzzy_filtered ) == 0 :
47
+ raise ValueError (f'File { name } is not available to download and has no alternatives' )
48
+
49
+ software_fuzzy_alternatives = software_fuzzy_filtered [0 ].get ('Title' )
50
+
51
+ # Search has to be filtered again, because API call can get
52
+ # duplicates like 70SWPM10SP43_2-20009701.sar for SWPM10SP43_2-20009701.SAR
53
+ software_search_alternatives = _search_software (software_fuzzy_alternatives )
54
+ software_search_alternatives_filtered = [
55
+ file for file in software_search_alternatives
56
+ if file .get ('Title' , '' ).startswith (suggested_filename )
57
+ ]
58
+ alternatives_count = len (software_search_alternatives_filtered )
59
+ if alternatives_count == 0 :
60
+ raise ValueError (f'File { name } is not available to download and has no alternatives' )
61
+ elif alternatives_count > 1 and deduplicate == '' :
62
+ names = [s ['Title' ] for s in software_search_alternatives_filtered ]
63
+ raise ValueError ('More than one results were found: %s. '
64
+ 'please use the correct full filename' % names )
65
+ elif alternatives_count > 1 and deduplicate == 'first' :
66
+ software_found = software_search_alternatives_filtered [0 ]
67
+ alternative_found = True
68
+ elif alternatives_count > 1 and deduplicate == 'last' :
69
+ software_found = software_search_alternatives_filtered [alternatives_count - 1 ]
70
+ alternative_found = True
71
+ else :
72
+ software_found = software_search_alternatives_filtered [0 ]
73
+ alternative_found = True
74
+ else :
75
+ raise ValueError (f'File { name } is not available to download. Enable "search_alternatives" to search for alternatives.' )
76
+
77
+ elif files_count > 1 and deduplicate == '' :
78
+ names = [s ['Title' ] for s in software_filtered ]
79
+ raise ValueError ('More than one results were found: %s. '
31
80
'please use the correct full filename' % names )
32
- elif num_files > 1 and deduplicate == 'first' :
33
- software = softwares [0 ]
34
- elif num_files > 1 and deduplicate == 'last' :
35
- software = softwares [ num_files - 1 ]
81
+ elif files_count > 1 and deduplicate == 'first' :
82
+ software_found = software_filtered [0 ]
83
+ elif files_count > 1 and deduplicate == 'last' :
84
+ software_found = software_filtered [ files_count - 1 ]
36
85
else :
37
- software = softwares [0 ]
86
+ software_found = software_filtered [0 ]
38
87
39
- download_link , filename = software ['DownloadDirectLink' ], name
40
- return (download_link , filename )
88
+ download_link = software_found ['DownloadDirectLink' ]
89
+ filename = _get_valid_filename (software_found )
90
+
91
+ return (download_link , filename , alternative_found )
41
92
42
93
43
94
def download_software (download_link , filename , output_dir , retry = 0 ):
@@ -151,6 +202,7 @@ def download_software_via_legacy_api(username, password, download_link,
151
202
152
203
153
204
def _search_software (keyword ):
205
+
154
206
url = C .URL_SOFTWARE_CENTER_SERVICE + '/SearchResultSet'
155
207
params = {
156
208
'SEARCH_MAX_RESULT' : 500 ,
@@ -167,7 +219,7 @@ def _search_software(keyword):
167
219
j = json .loads (res .text )
168
220
results = j ['d' ]['results' ]
169
221
except json .JSONDecodeError :
170
- # When use has no authority to search some specified softwares ,
222
+ # When use has no authority to search some specified files ,
171
223
# it will return non-json response, which is actually expected.
172
224
# So just return an empty list.
173
225
logger .warning ('Non-JSON response returned for software searching' )
@@ -246,3 +298,27 @@ def _is_checksum_matched(f, etag):
246
298
for chunk in iter (lambda : f .read (4096 * hash .block_size ), b"" ):
247
299
hash .update (chunk )
248
300
return hash .hexdigest () == checksum
301
+
302
+
303
+ def _get_valid_filename (software_found ):
304
+ """
305
+ Ensure that CD Media have correct filenames from description.
306
+ Example: S4CORE105_INST_EXPORT_1.zip downloads as 19118000000000004323
307
+
308
+ Args:
309
+ software_found: List[0] with dictionary of file.
310
+
311
+ Returns:
312
+ Valid filename for CD Media files, where applicable.
313
+ """
314
+
315
+ # Check if Title contains filename and extension
316
+ if re .match (r'^[^/\\\0]+\.[^/\\\0]+$' , software_found ['Title' ]):
317
+ return software_found ['Title' ]
318
+ else :
319
+ # Check if Description contains filename and extension
320
+ if re .match (r'^[^/\\\0]+\.[^/\\\0]+$' , software_found ['Description' ]):
321
+ return software_found ['Description' ]
322
+ else :
323
+ # Default to Title if Description does not help
324
+ return software_found ['Title' ]
0 commit comments