7
7
from botocore .exceptions import ClientError
8
8
9
9
from .application_metadata import ApplicationMetadata
10
- from .parser import (
11
- yaml_dump , parse_template , get_app_metadata ,
12
- parse_application_id , strip_app_metadata
10
+ from .parser import yaml_dump , parse_template , get_app_metadata , strip_app_metadata
11
+ from .exceptions import (
12
+ MultipleMatchingApplicationsError ,
13
+ ServerlessRepoClientError ,
14
+ S3PermissionsRequired ,
15
+ InvalidS3UriError ,
13
16
)
14
- from .exceptions import ServerlessRepoClientError , S3PermissionsRequired , InvalidS3UriError
15
17
16
- CREATE_APPLICATION = ' CREATE_APPLICATION'
17
- UPDATE_APPLICATION = ' UPDATE_APPLICATION'
18
- CREATE_APPLICATION_VERSION = ' CREATE_APPLICATION_VERSION'
18
+ CREATE_APPLICATION = " CREATE_APPLICATION"
19
+ UPDATE_APPLICATION = " UPDATE_APPLICATION"
20
+ CREATE_APPLICATION_VERSION = " CREATE_APPLICATION_VERSION"
19
21
20
22
21
23
def publish_application (template , sar_client = None ):
@@ -31,10 +33,10 @@ def publish_application(template, sar_client=None):
31
33
:raises ValueError
32
34
"""
33
35
if not template :
34
- raise ValueError (' Require SAM template to publish the application' )
36
+ raise ValueError (" Require SAM template to publish the application" )
35
37
36
38
if not sar_client :
37
- sar_client = boto3 .client (' serverlessrepo' )
39
+ sar_client = boto3 .client (" serverlessrepo" )
38
40
39
41
template_dict = _get_template_dict (template )
40
42
app_metadata = get_app_metadata (template_dict )
@@ -43,15 +45,14 @@ def publish_application(template, sar_client=None):
43
45
try :
44
46
request = _create_application_request (app_metadata , stripped_template )
45
47
response = sar_client .create_application (** request )
46
- application_id = response [' ApplicationId' ]
48
+ application_id = response [" ApplicationId" ]
47
49
actions = [CREATE_APPLICATION ]
48
50
except ClientError as e :
49
51
if not _is_conflict_exception (e ):
50
52
raise _wrap_client_error (e )
51
53
52
54
# Update the application if it already exists
53
- error_message = e .response ['Error' ]['Message' ]
54
- application_id = parse_application_id (error_message )
55
+ application_id = _get_application_id (sar_client , app_metadata )
55
56
try :
56
57
request = _update_application_request (app_metadata , application_id )
57
58
sar_client .update_application (** request )
@@ -62,17 +63,19 @@ def publish_application(template, sar_client=None):
62
63
# Create application version if semantic version is specified
63
64
if app_metadata .semantic_version :
64
65
try :
65
- request = _create_application_version_request (app_metadata , application_id , stripped_template )
66
+ request = _create_application_version_request (
67
+ app_metadata , application_id , stripped_template
68
+ )
66
69
sar_client .create_application_version (** request )
67
70
actions .append (CREATE_APPLICATION_VERSION )
68
71
except ClientError as e :
69
72
if not _is_conflict_exception (e ):
70
73
raise _wrap_client_error (e )
71
74
72
75
return {
73
- ' application_id' : application_id ,
74
- ' actions' : actions ,
75
- ' details' : _get_publish_details (actions , app_metadata .template_dict )
76
+ " application_id" : application_id ,
77
+ " actions" : actions ,
78
+ " details" : _get_publish_details (actions , app_metadata .template_dict ),
76
79
}
77
80
78
81
@@ -89,10 +92,12 @@ def update_application_metadata(template, application_id, sar_client=None):
89
92
:raises ValueError
90
93
"""
91
94
if not template or not application_id :
92
- raise ValueError ('Require SAM template and application ID to update application metadata' )
95
+ raise ValueError (
96
+ "Require SAM template and application ID to update application metadata"
97
+ )
93
98
94
99
if not sar_client :
95
- sar_client = boto3 .client (' serverlessrepo' )
100
+ sar_client = boto3 .client (" serverlessrepo" )
96
101
97
102
template_dict = _get_template_dict (template )
98
103
app_metadata = get_app_metadata (template_dict )
@@ -116,7 +121,7 @@ def _get_template_dict(template):
116
121
if isinstance (template , dict ):
117
122
return copy .deepcopy (template )
118
123
119
- raise ValueError (' Input template should be a string or dictionary' )
124
+ raise ValueError (" Input template should be a string or dictionary" )
120
125
121
126
122
127
def _create_application_request (app_metadata , template ):
@@ -130,21 +135,21 @@ def _create_application_request(app_metadata, template):
130
135
:return: SAR CreateApplication request body
131
136
:rtype: dict
132
137
"""
133
- app_metadata .validate ([' author' , ' description' , ' name' ])
138
+ app_metadata .validate ([" author" , " description" , " name" ])
134
139
request = {
135
- ' Author' : app_metadata .author ,
136
- ' Description' : app_metadata .description ,
137
- ' HomePageUrl' : app_metadata .home_page_url ,
138
- ' Labels' : app_metadata .labels ,
139
- ' LicenseBody' : app_metadata .license_body ,
140
- ' LicenseUrl' : app_metadata .license_url ,
141
- ' Name' : app_metadata .name ,
142
- ' ReadmeBody' : app_metadata .readme_body ,
143
- ' ReadmeUrl' : app_metadata .readme_url ,
144
- ' SemanticVersion' : app_metadata .semantic_version ,
145
- ' SourceCodeUrl' : app_metadata .source_code_url ,
146
- ' SpdxLicenseId' : app_metadata .spdx_license_id ,
147
- ' TemplateBody' : template
140
+ " Author" : app_metadata .author ,
141
+ " Description" : app_metadata .description ,
142
+ " HomePageUrl" : app_metadata .home_page_url ,
143
+ " Labels" : app_metadata .labels ,
144
+ " LicenseBody" : app_metadata .license_body ,
145
+ " LicenseUrl" : app_metadata .license_url ,
146
+ " Name" : app_metadata .name ,
147
+ " ReadmeBody" : app_metadata .readme_body ,
148
+ " ReadmeUrl" : app_metadata .readme_url ,
149
+ " SemanticVersion" : app_metadata .semantic_version ,
150
+ " SourceCodeUrl" : app_metadata .source_code_url ,
151
+ " SpdxLicenseId" : app_metadata .spdx_license_id ,
152
+ " TemplateBody" : template ,
148
153
}
149
154
# Remove None values
150
155
return {k : v for k , v in request .items () if v }
@@ -162,13 +167,13 @@ def _update_application_request(app_metadata, application_id):
162
167
:rtype: dict
163
168
"""
164
169
request = {
165
- ' ApplicationId' : application_id ,
166
- ' Author' : app_metadata .author ,
167
- ' Description' : app_metadata .description ,
168
- ' HomePageUrl' : app_metadata .home_page_url ,
169
- ' Labels' : app_metadata .labels ,
170
- ' ReadmeBody' : app_metadata .readme_body ,
171
- ' ReadmeUrl' : app_metadata .readme_url
170
+ " ApplicationId" : application_id ,
171
+ " Author" : app_metadata .author ,
172
+ " Description" : app_metadata .description ,
173
+ " HomePageUrl" : app_metadata .home_page_url ,
174
+ " Labels" : app_metadata .labels ,
175
+ " ReadmeBody" : app_metadata .readme_body ,
176
+ " ReadmeUrl" : app_metadata .readme_url ,
172
177
}
173
178
return {k : v for k , v in request .items () if v }
174
179
@@ -186,12 +191,12 @@ def _create_application_version_request(app_metadata, application_id, template):
186
191
:return: SAR CreateApplicationVersion request body
187
192
:rtype: dict
188
193
"""
189
- app_metadata .validate ([' semantic_version' ])
194
+ app_metadata .validate ([" semantic_version" ])
190
195
request = {
191
- ' ApplicationId' : application_id ,
192
- ' SemanticVersion' : app_metadata .semantic_version ,
193
- ' SourceCodeUrl' : app_metadata .source_code_url ,
194
- ' TemplateBody' : template
196
+ " ApplicationId" : application_id ,
197
+ " SemanticVersion" : app_metadata .semantic_version ,
198
+ " SourceCodeUrl" : app_metadata .source_code_url ,
199
+ " TemplateBody" : template ,
195
200
}
196
201
return {k : v for k , v in request .items () if v }
197
202
@@ -204,8 +209,8 @@ def _is_conflict_exception(e):
204
209
:type e: ClientError
205
210
:return: True if e is ConflictException
206
211
"""
207
- error_code = e .response [' Error' ][ ' Code' ]
208
- return error_code == ' ConflictException'
212
+ error_code = e .response [" Error" ][ " Code" ]
213
+ return error_code == " ConflictException"
209
214
210
215
211
216
def _wrap_client_error (e ):
@@ -216,12 +221,12 @@ def _wrap_client_error(e):
216
221
:type e: ClientError
217
222
:return: S3PermissionsRequired or InvalidS3UriError or general ServerlessRepoClientError
218
223
"""
219
- error_code = e .response [' Error' ][ ' Code' ]
220
- message = e .response [' Error' ][ ' Message' ]
224
+ error_code = e .response [" Error" ][ " Code" ]
225
+ message = e .response [" Error" ][ " Message" ]
221
226
222
- if error_code == ' BadRequestException' :
227
+ if error_code == " BadRequestException" :
223
228
if "Failed to copy S3 object. Access denied:" in message :
224
- match = re .search (' bucket=(.+?), key=(.+?)$' , message )
229
+ match = re .search (" bucket=(.+?), key=(.+?)$" , message )
225
230
if match :
226
231
return S3PermissionsRequired (bucket = match .group (1 ), key = match .group (2 ))
227
232
if "Invalid S3 URI" in message :
@@ -250,11 +255,36 @@ def _get_publish_details(actions, app_metadata_template):
250
255
ApplicationMetadata .HOME_PAGE_URL ,
251
256
ApplicationMetadata .LABELS ,
252
257
ApplicationMetadata .README_URL ,
253
- ApplicationMetadata .README_BODY
258
+ ApplicationMetadata .README_BODY ,
254
259
]
255
260
256
261
if CREATE_APPLICATION_VERSION in actions :
257
262
# SemanticVersion and SourceCodeUrl can only be updated by creating a new version
258
- additional_keys = [ApplicationMetadata .SEMANTIC_VERSION , ApplicationMetadata .SOURCE_CODE_URL ]
263
+ additional_keys = [
264
+ ApplicationMetadata .SEMANTIC_VERSION ,
265
+ ApplicationMetadata .SOURCE_CODE_URL ,
266
+ ]
259
267
include_keys .extend (additional_keys )
260
268
return {k : v for k , v in app_metadata_template .items () if k in include_keys and v }
269
+
270
+
271
+ def _get_application_id (sar_client , metadata ):
272
+ """
273
+ Gets the application ID of rhte matching application name.
274
+
275
+ :param sar_client: The boto3 SAR client.
276
+ :param metadata: The application meta data.
277
+ :return: The matching application ID.
278
+ :rtype: str
279
+ :raises: MultipleMatchingApplicationsError
280
+ """
281
+ application_ids = []
282
+ pager = sar_client .get_paginator ("list_applications" )
283
+ for application in pager .paginate ():
284
+ if application ["Name" ] == metadata .name :
285
+ application_ids .append (application ["ApplicationId" ])
286
+ if len (application_ids ) > 1 :
287
+ raise MultipleMatchingApplicationsError (
288
+ message = 'Multiple applications with the name "%s"' % metadata .name
289
+ )
290
+ return application_ids [0 ] if len (application_ids ) == 1 else None
0 commit comments