From 5dccae46cc0b0930a80f1bccf35354e05fd35a65 Mon Sep 17 00:00:00 2001 From: Matt Rutkowski Date: Thu, 30 Jan 2020 10:45:27 -0600 Subject: [PATCH 1/8] Enable Web Secure token for sequences on API create --- deployers/servicedeployer.go | 11 +++-- parsers/manifest_parser.go | 12 +----- ...b_annotation_on_api_action_or_sequence.yml | 40 ++++++++++++++++++ ...est_require_whisk_auth_sequence_valid.yaml | 41 +++++++++++++++++++ tests/src/integration/webaction/src/hello.js | 24 +++++++++++ .../webaction/src/hello_http_validate.js | 36 ++++++++++++++++ .../webaction/src/hello_http_wrap.js | 28 +++++++++++++ webaction/webaction.go | 26 ++++++++++-- 8 files changed, 202 insertions(+), 16 deletions(-) create mode 100644 tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml create mode 100644 tests/src/integration/webaction/manifest_require_whisk_auth_sequence_valid.yaml create mode 100644 tests/src/integration/webaction/src/hello.js create mode 100644 tests/src/integration/webaction/src/hello_http_validate.js create mode 100644 tests/src/integration/webaction/src/hello_http_wrap.js diff --git a/deployers/servicedeployer.go b/deployers/servicedeployer.go index 47f603640..1bba64533 100644 --- a/deployers/servicedeployer.go +++ b/deployers/servicedeployer.go @@ -1012,15 +1012,18 @@ func (deployer *ServiceDeployer) createAction(pkgname string, action *whisk.Acti return nil } -func (deployer *ServiceDeployer) getAnnotationsFromPackageAction(packageActionName string) *whisk.KeyValueArr { +func (deployer *ServiceDeployer) getAnnotationsFromPackageActionOrSequence(packageActionName string) *whisk.KeyValueArr { if len(packageActionName)!=0 { // Split the package name and action name being searched for - aActionName := strings.Split(packageActionName,"/") + aActionName := strings.Split(packageActionName, parsers.PATH_SEPARATOR) + // Attempt to locate the named action (or sequence) to return its annotations if pkg, found := deployer.Deployment.Packages[aActionName[0]]; found { if atemp, found := pkg.Actions[aActionName[1]]; found { return &(atemp.Action.Annotations) + } else if atemp, found := pkg.Sequences[aActionName[1]]; found { + return &(atemp.Action.Annotations) } } } @@ -1041,9 +1044,11 @@ func (deployer *ServiceDeployer) createApi(api *whisk.ApiCreateRequest) error { // Retrieve annotations on the action we are attempting to create an API for var actionAnnotations *whisk.KeyValueArr - actionAnnotations = deployer.getAnnotationsFromPackageAction(api.ApiDoc.Action.Name) + actionAnnotations = deployer.getAnnotationsFromPackageActionOrSequence(api.ApiDoc.Action.Name) // Process any special annotations (e.g., "require-whisk-auth") on the associated Action + // NOTE: we do not throw an error if annotations are NOT found (nil) since this is already done in + // the parsing phase and would be redundant. if actionAnnotations != nil { wskprint.PrintlnOpenWhiskVerbose(utils.Flags.Verbose, fmt.Sprintf("Processing action annotations: %v", actionAnnotations)) diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go index b1ece3e66..eb8ac02c0 100644 --- a/parsers/manifest_parser.go +++ b/parsers/manifest_parser.go @@ -1223,11 +1223,7 @@ func (dm *YAMLParser) ComposeApiRecords(client *whisk.Config, packageName string // web or web-export set to any of [true, yes, raw] a := pkg.Actions[actionName] if !webaction.IsWebAction(a.GetWeb()) { - warningString := wski18n.T(wski18n.ID_WARN_API_MISSING_WEB_ACTION_X_action_X_api_X, - map[string]interface{}{ - wski18n.KEY_ACTION: actionName, - wski18n.KEY_API: apiName}) - wskprint.PrintOpenWhiskWarning(warningString) + webaction.WarnWebAnnotationMissingFromActionOrSequence(apiName,actionName,false) if a.Annotations == nil { a.Annotations = make(map[string]interface{}, 0) } @@ -1240,11 +1236,7 @@ func (dm *YAMLParser) ComposeApiRecords(client *whisk.Config, packageName string // web set to any of [true, yes, raw] a := pkg.Sequences[actionName] if !webaction.IsWebSequence(a.Web) { - warningString := wski18n.T(wski18n.ID_WARN_API_MISSING_WEB_SEQUENCE_X_sequence_X_api_X, - map[string]interface{}{ - wski18n.KEY_SEQUENCE: actionName, - wski18n.KEY_API: apiName}) - wskprint.PrintOpenWhiskWarning(warningString) + webaction.WarnWebAnnotationMissingFromActionOrSequence(apiName,actionName,true) if a.Annotations == nil { a.Annotations = make(map[string]interface{}, 0) } diff --git a/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml b/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml new file mode 100644 index 000000000..d61d0e2a8 --- /dev/null +++ b/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml @@ -0,0 +1,40 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# TODO: test for value conflicts when both `web` and `web-export` supplied +packages: + TestMissingWebAnnotation: + actions: + hello_validate: + function: src/hello_http_validate.js + hello: + function: src/hello.js + hello_wrap: + function: src/hello_http_wrap.js + sequences: + hellosequence: + actions: hello_validate, hello, hello_wrap + apis: + actionmissingweb: + helloworlds: + hellosequence: + hello: + method: GET + response: http + hellosequence: + method: GET + response: http diff --git a/tests/src/integration/webaction/manifest_require_whisk_auth_sequence_valid.yaml b/tests/src/integration/webaction/manifest_require_whisk_auth_sequence_valid.yaml new file mode 100644 index 000000000..4a8b8dfcb --- /dev/null +++ b/tests/src/integration/webaction/manifest_require_whisk_auth_sequence_valid.yaml @@ -0,0 +1,41 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +packages: + hellosequencespkg: + version: 1.0 + license: Apache-2.0 + actions: + hello_validate: + function: src/hello_http_validate.js + hello: + function: src/hello.js + hello_wrap: + function: src/hello_http_wrap.js + sequences: + hellosequence: + actions: hello_validate, hello, hello_wrap + web: true + annotations: + require-whisk-auth: "mytoken" + apis: + web-secure-sequences: + helloworlds: + hellosequence: + hellosequence: + method: GET + response: http diff --git a/tests/src/integration/webaction/src/hello.js b/tests/src/integration/webaction/src/hello.js new file mode 100644 index 000000000..d0ba58413 --- /dev/null +++ b/tests/src/integration/webaction/src/hello.js @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Hello, world + */ +function main(params) { + msg = "Hello, " + params.name + " from " + params.place; + return { greeting: msg }; +} diff --git a/tests/src/integration/webaction/src/hello_http_validate.js b/tests/src/integration/webaction/src/hello_http_validate.js new file mode 100644 index 000000000..9f9c7bef2 --- /dev/null +++ b/tests/src/integration/webaction/src/hello_http_validate.js @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This action validates the parameters passed to the hello world action and + * returns an error if any of them is missing. + */ +function main(params) { + if(params.name && params.place) { + return params; + } else { + return { + error: { + body: { + message: 'Attributes name and place are mandatory' + }, + statusCode: 400, + headers: {'Content-Type': 'application/json'} + } + } + } +} diff --git a/tests/src/integration/webaction/src/hello_http_wrap.js b/tests/src/integration/webaction/src/hello_http_wrap.js new file mode 100644 index 000000000..ba786fe30 --- /dev/null +++ b/tests/src/integration/webaction/src/hello_http_wrap.js @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This action take a result from the previous action and wraps it into a + * HTTP response structure. + */ +function main(params) { + return { + body: params, + statusCode: 200, + headers: {'Content-Type': 'application/json'} + }; +} diff --git a/webaction/webaction.go b/webaction/webaction.go index b02b1b098..cc5a5d28c 100644 --- a/webaction/webaction.go +++ b/webaction/webaction.go @@ -82,9 +82,7 @@ func SetWebActionAnnotations(filePath string, action string, webMode string, ann type WebActionAnnotationMethod func(annotations whisk.KeyValueArr) whisk.KeyValueArr -func webActionAnnotations( - fetchAnnotations bool, - annotations whisk.KeyValueArr, +func webActionAnnotations( fetchAnnotations bool, annotations whisk.KeyValueArr, webActionAnnotationMethod WebActionAnnotationMethod) (whisk.KeyValueArr, error) { if annotations != nil || !fetchAnnotations { @@ -155,6 +153,28 @@ func HasAnnotation(annotations *whisk.KeyValueArr, key string) bool { return (annotations.FindKeyValue(key) >= 0) } +func CreateWebAnnotationsAsMap() map[string]interface{} { + annotations := make(map[string]interface{}, 3) + annotations[WEB_EXPORT_ANNOT] = true + annotations[RAW_HTTP_ANNOT] = false + annotations[FINAL_ANNOT] = true + return annotations +} + +func WarnWebAnnotationMissingFromActionOrSequence(apiName string, actionName string, isSequence bool){ + i18nWarningID := wski18n.ID_WARN_API_MISSING_WEB_ACTION_X_action_X_api_X + + if isSequence { + i18nWarningID = wski18n.ID_WARN_API_MISSING_WEB_SEQUENCE_X_sequence_X_api_X + } + + warningString := wski18n.T(i18nWarningID, + map[string]interface{}{ + wski18n.KEY_SEQUENCE: actionName, + wski18n.KEY_API: apiName}) + wskprint.PrintOpenWhiskWarning(warningString) +} + func ValidateRequireWhiskAuthAnnotationValue(actionName string, value interface{}) (string, error) { var isValid = false var enabled = wski18n.FEATURE_DISABLED From e8e2c23b454dbc81d923ee9ed9b15ecad40986a1 Mon Sep 17 00:00:00 2001 From: Matt Rutkowski Date: Thu, 30 Jan 2020 12:21:02 -0600 Subject: [PATCH 2/8] Enable Web Secure token for sequences on API create --- parsers/manifest_parser.go | 32 ++++++++++++++++++++++---------- webaction/webaction.go | 25 ++++++++++++++++++++++--- wskderrors/wskdeployerror.go | 32 ++++++++++++++++++++++++++++++++ wski18n/i18n_ids.go | 2 ++ wski18n/i18n_resources.go | 4 ++-- wski18n/resources/en_US.all.json | 12 ++++++++++-- 6 files changed, 90 insertions(+), 17 deletions(-) diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go index eb8ac02c0..c4cc1dec2 100644 --- a/parsers/manifest_parser.go +++ b/parsers/manifest_parser.go @@ -1223,12 +1223,18 @@ func (dm *YAMLParser) ComposeApiRecords(client *whisk.Config, packageName string // web or web-export set to any of [true, yes, raw] a := pkg.Actions[actionName] if !webaction.IsWebAction(a.GetWeb()) { - webaction.WarnWebAnnotationMissingFromActionOrSequence(apiName,actionName,false) - if a.Annotations == nil { - a.Annotations = make(map[string]interface{}, 0) + if !utils.Flags.Strict { + webaction.WarnWebAnnotationMissingFromActionOrSequence(apiName,actionName,false) + if a.Annotations == nil { + a.Annotations = make(map[string]interface{}, 0) + } + a.Annotations[webaction.WEB_EXPORT_ANNOT] = true + //a.Annotations = webaction.CreateWebAnnotationsAsMap() + pkg.Actions[actionName] = a + } else { + err := wskderrors.NewInvalidWebActionError(apiName,actionName,false) + return requests, requestOptions, err } - a.Annotations[webaction.WEB_EXPORT_ANNOT] = true - pkg.Actions[actionName] = a } // verify that the sequence is defined under sequences sections } else if _, ok := pkg.Sequences[actionName]; ok { @@ -1236,12 +1242,18 @@ func (dm *YAMLParser) ComposeApiRecords(client *whisk.Config, packageName string // web set to any of [true, yes, raw] a := pkg.Sequences[actionName] if !webaction.IsWebSequence(a.Web) { - webaction.WarnWebAnnotationMissingFromActionOrSequence(apiName,actionName,true) - if a.Annotations == nil { - a.Annotations = make(map[string]interface{}, 0) + if !utils.Flags.Strict { + webaction.WarnWebAnnotationMissingFromActionOrSequence(apiName,actionName,true) + if a.Annotations == nil { + a.Annotations = make(map[string]interface{}, 0) + } + a.Annotations[webaction.WEB_EXPORT_ANNOT] = true + //a.Annotations = webaction.CreateWebAnnotationsAsMap() + pkg.Sequences[actionName] = a + } else { + err := wskderrors.NewInvalidWebActionError(apiName,actionName,true) + return requests, requestOptions, err } - a.Annotations[webaction.WEB_EXPORT_ANNOT] = true - pkg.Sequences[actionName] = a } // return failure since action or sequence are not defined in the manifest } else { diff --git a/webaction/webaction.go b/webaction/webaction.go index cc5a5d28c..a6c25ddd4 100644 --- a/webaction/webaction.go +++ b/webaction/webaction.go @@ -153,8 +153,11 @@ func HasAnnotation(annotations *whisk.KeyValueArr, key string) bool { return (annotations.FindKeyValue(key) >= 0) } -func CreateWebAnnotationsAsMap() map[string]interface{} { - annotations := make(map[string]interface{}, 3) +func CreateWebAnnotationsAsMap(annotations map[string]interface{}) map[string]interface{} { + if annotations == nil { + annotations = make(map[string]interface{}, 3) + } + annotations[WEB_EXPORT_ANNOT] = true annotations[RAW_HTTP_ANNOT] = false annotations[FINAL_ANNOT] = true @@ -162,19 +165,35 @@ func CreateWebAnnotationsAsMap() map[string]interface{} { } func WarnWebAnnotationMissingFromActionOrSequence(apiName string, actionName string, isSequence bool){ + nameKey := wski18n.KEY_ACTION i18nWarningID := wski18n.ID_WARN_API_MISSING_WEB_ACTION_X_action_X_api_X if isSequence { + nameKey = wski18n.KEY_SEQUENCE i18nWarningID = wski18n.ID_WARN_API_MISSING_WEB_SEQUENCE_X_sequence_X_api_X } warningString := wski18n.T(i18nWarningID, map[string]interface{}{ - wski18n.KEY_SEQUENCE: actionName, + nameKey: actionName, wski18n.KEY_API: apiName}) wskprint.PrintOpenWhiskWarning(warningString) } +func ErrorWebAnnotationMissingFromActionOrSequence(apiName string, actionName string, isSequence bool){ + i18nErrorID := wski18n.ID_ERR_API_MISSING_WEB_ACTION_X_action_X_api_X + + if isSequence { + i18nErrorID = wski18n.ID_ERR_API_MISSING_WEB_SEQUENCE_X_sequence_X_api_X + } + + errString := wski18n.T(i18nErrorID, + map[string]interface{}{ + wski18n.KEY_SEQUENCE: actionName, + wski18n.KEY_API: apiName}) + wskprint.PrintOpenWhiskWarning(errString) +} + func ValidateRequireWhiskAuthAnnotationValue(actionName string, value interface{}) (string, error) { var isValid = false var enabled = wski18n.FEATURE_DISABLED diff --git a/wskderrors/wskdeployerror.go b/wskderrors/wskdeployerror.go index de06d99d3..70e0bcd0d 100644 --- a/wskderrors/wskdeployerror.go +++ b/wskderrors/wskdeployerror.go @@ -19,6 +19,8 @@ package wskderrors import ( "fmt" + "github.com/apache/openwhisk-wskdeploy/wski18n" + "github.com/apache/openwhisk-wskdeploy/wskprint" "io/ioutil" "net/http" "path/filepath" @@ -63,6 +65,7 @@ const ( ERROR_YAML_INVALID_PARAMETER_TYPE = "ERROR_YAML_INVALID_PARAMETER_TYPE" ERROR_YAML_INVALID_RUNTIME = "ERROR_YAML_INVALID_RUNTIME" ERROR_YAML_INVALID_WEB_EXPORT = "ERROR_YAML_INVALID_WEB_EXPORT" + ERROR_YAML_INVALID_API = "ERROR_YAML_INVALID_API" ERROR_YAML_INVALID_API_GATEWAY_METHOD = "ERROR_YAML_INVALID_API_GATEWAY_METHOD" ERROR_RUNTIME_PARSER_FAILURE = "ERROR_RUNTIME_PARSER_FAILURE" ERROR_ACTION_ANNOTATION = "ERROR_ACTION_ANNOTATION" @@ -445,6 +448,35 @@ func NewInvalidAPIGatewayMethodError(fpath string, api string, method string, su return err } +/* + * Invalid Web Action API + */ +type InvalidWebActionAPIError struct { + WskDeployBaseErr +} + +func NewInvalidWebActionError(apiName string, actionName string, isSequence bool) *InvalidWebActionAPIError { + var err = &InvalidWebActionAPIError{ + } + + i18nErrorID := wski18n.ID_ERR_API_MISSING_WEB_ACTION_X_action_X_api_X + + if isSequence { + i18nErrorID = wski18n.ID_ERR_API_MISSING_WEB_SEQUENCE_X_sequence_X_api_X + } + + errString := wski18n.T(i18nErrorID, + map[string]interface{}{ + wski18n.KEY_SEQUENCE: actionName, + wski18n.KEY_API: apiName}) + wskprint.PrintOpenWhiskWarning(errString) + + err.SetErrorType(ERROR_YAML_INVALID_API) + err.SetCallerByStackFrameSkip(2) + err.SetMessage(errString) + return err +} + /* * Failed to Retrieve/Parse Runtime */ diff --git a/wski18n/i18n_ids.go b/wski18n/i18n_ids.go index 9fda5f7c7..8b15f57d5 100644 --- a/wski18n/i18n_ids.go +++ b/wski18n/i18n_ids.go @@ -217,6 +217,8 @@ const ( ID_ERR_INVALID_PARAM_FILE_X_file_X = "msg_err_invalid_param_file" ID_ERR_REQUIRED_INPUTS_MISSING_VALUE_X_inputs_X = "msg_err_required_inputs_missing_value" ID_ERR_API_GATEWAY_BASE_PATH_INVALID_X_api_X = "msg_err_api_gateway_base_path_invalid" + ID_ERR_API_MISSING_WEB_ACTION_X_action_X_api_X = "msg_err_api_missing_web_action" + ID_ERR_API_MISSING_WEB_SEQUENCE_X_sequence_X_api_X = "msg_err_api_missing_web_sequence" ID_ERR_RUNTIME_PARSER_ERROR = "msg_err_runtime_parser_error" ID_ERR_WEB_ACTION_REQUIRE_AUTH_TOKEN_INVALID_X_action_X_key_X_value = "msg_err_web_action_require_auth_token_invalid" diff --git a/wski18n/i18n_resources.go b/wski18n/i18n_resources.go index aae595c62..d26c1e536 100644 --- a/wski18n/i18n_resources.go +++ b/wski18n/i18n_resources.go @@ -97,7 +97,7 @@ func wski18nResourcesDe_deAllJson() (*asset, error) { return a, nil } -var _wski18nResourcesEn_usAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x3c\x6b\x6f\x1b\x39\x92\xdf\xe7\x57\x14\x06\x0b\x64\x16\x90\xe5\xec\xe2\x70\x58\xf8\x2e\x07\x78\x13\x67\xc7\x3b\xc9\x38\xe7\xc7\x0e\xe6\x12\xa3\x43\x75\x97\x24\xae\xbb\xc9\x5e\x92\x2d\x45\x63\xe8\xbf\x1f\xaa\x48\x76\xb7\x64\xf5\x43\x4e\x06\x77\xf9\x12\x49\x24\xeb\xc5\x62\xb1\x5e\xf4\xc7\xef\x00\x1e\xbf\x03\x00\xf8\x5e\x66\xdf\x9f\xc1\xf7\x85\x5d\x24\xa5\xc1\xb9\xfc\x92\xa0\x31\xda\x7c\x3f\xf1\xa3\xce\x08\x65\x73\xe1\xa4\x56\x34\xed\x82\xc7\xbe\x03\xd8\x4e\x7a\x20\x48\x35\xd7\x1d\x00\x2e\x69\x68\x68\xbd\xad\xd2\x14\xad\xed\x00\x71\x13\x46\x87\xa0\xac\x85\x51\x52\x2d\x3a\xa0\xfc\x12\x46\x3b\xa1\xa4\x45\x96\x64\x68\xd3\x24\xd7\x6a\x91\x18\x2c\xb5\x71\x1d\xb0\xae\x79\xd0\x82\x56\x90\x61\x99\xeb\x0d\x66\x80\xca\x49\x27\xd1\xc2\x0f\x72\x8a\xd3\x09\x7c\x10\xe9\x83\x58\xa0\x9d\xc0\x79\x4a\xeb\xec\x04\x6e\x8d\x5c\x2c\xd0\xd8\x09\x5c\x57\x39\x8d\xa0\x4b\xa7\x7f\x04\x61\x61\x8d\x79\x4e\xff\x1b\x4c\x51\x39\x5e\xb1\x62\x6c\x16\xa4\x02\xb7\x44\xb0\x25\xa6\x72\x2e\x31\x03\x25\x0a\xb4\xa5\x48\x71\x3a\x9a\x17\xad\xbb\x38\xb9\x5d\x22\x5c\x95\xa8\x7e\x59\x4a\xfb\x00\x6f\x98\x99\x82\x48\xb8\xd5\x3a\xff\xa4\x3e\xa9\x5b\x0d\x33\x5c\x48\x05\x6b\x6d\x1e\xa4\x5a\xc0\x5a\xba\x25\xac\xed\x83\x67\x7c\x02\xa6\xf2\x04\xbe\xa8\x7f\x7b\x01\xa9\x2e\x0a\xa1\xb2\x33\x02\xf0\xc9\xfd\xa1\x99\xce\x10\x97\xd2\xc2\x5a\xe6\x79\x90\x5d\x0b\xbf\xb0\x16\x9d\x6d\xf1\x2a\x15\x14\x42\xc9\x39\x5a\x37\xdd\x88\x22\x07\x6d\x5a\x3f\x14\xf9\x27\x75\x39\x87\xb4\x32\x86\x48\xce\xa4\xc1\xd4\x69\xb3\x81\x4c\xa3\x55\x0e\x96\x62\x85\x20\xd4\xa6\x5e\x02\x73\x99\xe3\xa4\x21\x07\x4a\x23\x95\xb3\xe0\x88\xa4\x25\xe6\x25\x14\x68\xad\x58\xe0\xd4\x13\x8a\x50\x68\xeb\x98\x1d\xad\x60\x2d\x36\x16\xf4\x1c\x2a\xcb\x72\xa8\x81\x38\x1d\x39\x11\x2a\x3b\xd5\x06\x2a\xd5\xc5\x99\x30\xc8\x42\xd9\x11\x49\xeb\x0b\x9c\x14\x50\x0a\xb7\x3c\x75\xfa\x74\x87\xf1\x71\xb3\xe0\x24\xab\x07\xb2\x7a\x2f\x0f\x00\x88\x14\x1e\xfe\x75\x24\x15\x83\xd3\x7b\xc9\xf9\xa4\xce\x2b\xe5\x96\x74\x6c\x52\x56\xc7\xb3\x4f\xaa\x81\x6d\x50\x64\x16\x52\x83\x19\x4d\x10\xb9\x85\xb9\xd1\x05\xfc\xe1\xc7\xab\xf7\x17\xa7\xd3\xb5\x7d\x28\x8d\x2e\x2d\xcc\x36\x90\xe1\x5c\x54\xb9\xfb\xa4\xae\x56\x68\xd6\x46\x3a\x8c\x3f\x41\xaa\xd5\x5c\x2e\x78\xd3\xe9\xa8\xbe\x7e\x77\x79\xf6\x49\x01\xec\x48\xf2\x24\x4c\xfa\xcf\xd6\xe4\xff\xea\x11\xc0\x95\x09\xea\xb9\x01\x91\xe7\xe0\x96\x06\x7b\x80\x8b\x52\x2e\x49\x83\x7e\xbc\xba\xb9\xa5\xaf\x95\x5b\xc2\x4f\x17\xbf\xc2\xc9\x49\x7d\x8a\xe1\xe7\xf3\xf7\x17\x37\x1f\xce\x5f\x5f\x74\x62\x1d\x71\xce\xed\x52\x1b\xd7\x6f\xb4\x3e\x18\xbd\x92\x19\x5a\x10\x60\xab\xa2\x10\x86\xa4\x4c\xf3\x49\xa7\x9f\x68\xea\x0c\x49\xc9\xa3\x75\x3b\x8d\x7b\x8d\x19\xcc\x84\xc5\x8c\x58\x8e\x34\xb6\xf6\x16\x7e\x3d\x7f\xff\x6e\x8c\x5d\x0a\xf4\x76\x1b\xa6\x73\x70\x5a\xe7\x60\xd1\xd1\xf9\xe2\xb3\x19\xa4\xba\xd1\x95\x01\x5d\xa2\x5a\x33\xbd\x65\xb0\xb3\xe1\x58\x8a\xdd\xc3\x3e\x9e\x96\x15\x1a\x4b\xb8\xbb\x84\x27\x95\x63\x3b\x17\xe6\x81\xaa\x8a\x19\x1a\x92\x5d\xbd\xe1\xa3\x71\xd9\x8d\x4a\xfb\xf9\x76\x1a\x68\x92\x67\xb6\xd9\x9c\x9a\xd9\x19\xba\x35\xa2\x82\x34\x97\x24\x76\xa1\x32\xb0\x68\x56\x68\x46\x5f\x0a\xe3\x69\x68\x6d\x2f\xe1\x89\xaa\xc0\x3f\xec\xa8\x4e\xf7\x56\xd0\x3a\x5d\x12\x7c\x91\xb7\xe1\xd1\x16\xc5\xe9\xac\x3a\x64\x17\xde\xc8\xf9\x1c\xd9\xa2\x47\x8b\x6b\x2a\x45\x77\x37\x93\x73\xb6\x6b\x84\xe8\xa7\xa7\xbf\x8c\xb4\x60\xbd\x53\xdb\xd6\xeb\xf9\x30\x4e\x4a\xa3\xff\x89\xa9\xa3\xf3\x0e\x1f\xae\xaf\xfe\x7e\xf1\xfa\x76\xb4\x9e\x44\x51\x77\xec\xd3\x5d\xe7\x3d\xc3\xc6\xd2\x2b\xc4\x58\x7d\x18\x8b\xcb\x60\xa1\x57\x68\x9f\xe2\x5c\x2f\x65\xba\x84\x35\x1a\x6c\x9c\x22\xa6\x83\x4e\xcd\x8e\x26\xec\xdb\x8b\x1d\x3f\x23\xc3\x1c\x1d\x6d\xf6\x61\xa6\x76\x80\xf9\xeb\xdc\x54\xea\xec\xff\xdd\xf5\x76\x18\xd2\x21\x6d\x80\x1f\xb4\xca\x37\xec\x5f\x59\x98\x6b\xd3\x12\x0f\x7b\x7f\xac\x60\x85\xce\xf0\x8f\xa3\xf5\x06\xbf\xf4\xdc\x03\x17\x3c\x08\x81\x92\x1d\xe1\xd6\x22\x1f\xab\x34\x23\x10\x59\xda\x2e\xb1\xc0\xac\x1f\x23\x59\x9b\x1d\x25\x99\x57\x8a\xfd\x66\x6f\x23\x3a\xfc\x31\x5a\x45\x0e\xa8\xa7\x63\x4f\x0b\xfc\x8f\x1d\x42\x6f\x6d\xaa\x9f\x87\xd9\xc9\x11\x97\xee\x3c\x17\x8b\x44\x94\x32\xa1\xeb\xbd\x83\x7f\x7f\x3f\x9d\x7f\xb8\x84\xcf\x74\xff\x7f\x1e\x09\xb1\xff\x22\x6a\x01\xfd\xc7\xc5\xf5\xcd\xe5\xd5\xcf\xa3\xe0\x56\x6e\x99\x3c\x60\xd7\xe1\xa6\x61\x6d\xe4\x6f\xfc\x03\x7c\xfe\xe9\xe2\xd7\x31\x40\x53\x34\x2e\xa1\xdd\xe9\x80\x4a\xf2\x25\xeb\x4d\x47\x76\x4a\x93\x79\x2b\xc7\x00\x66\x57\xac\x03\x6a\xdb\xa9\xfb\x21\x7a\x7a\xd2\xee\xbb\x86\x03\x87\xc5\x4b\x25\xcf\xf5\x3a\x09\x30\xba\xa2\x4f\x9e\x04\xf5\xa4\x61\xa8\xcd\xf1\xed\x93\x4b\x1d\x34\xd4\xf7\xe0\x08\xd0\xa5\xc1\x95\xc4\x75\x07\x5c\xbb\x64\x42\x23\xd0\xd3\x9d\x8b\xba\xcc\x85\x1a\x81\xe1\x01\x37\xa3\xb7\xf4\x01\x37\x63\x09\xf7\x92\x0e\x86\xa0\x57\xd0\xd1\x48\xd4\xe1\xb4\xa3\x8b\x01\x0a\x61\x1e\x30\x8b\xa6\x64\x94\xa8\x18\x4e\x42\x87\xbe\x8b\x99\x80\x8a\xa7\x0c\x43\x8c\xd6\x61\x60\x57\x77\x2e\xa7\x11\x60\xeb\x40\xa0\x03\x6e\x33\x3e\x9a\xe9\x01\x0a\xbd\x5f\x90\xa3\xb5\x51\xda\x23\x40\x5b\x67\x64\x27\x64\xbf\x75\x95\x45\xba\xbc\xe6\x52\x61\x46\x56\xd9\xc9\xa2\x76\x97\x47\x60\x70\xa6\x5b\x08\x3c\x06\xba\x72\x65\x35\x86\x58\xaf\x6e\x2b\x34\x33\x6d\xbb\x40\x86\xd1\x63\x81\x96\xc2\x88\xa2\x53\xc0\x46\x14\xe8\xd0\xc0\x4a\xe4\x15\xf2\xed\x4d\xc6\x14\xfe\x71\xfe\xee\xee\xe2\x33\x5d\xee\x85\x38\x12\x55\xdf\x69\xfc\xfc\xf6\xf2\xdd\xc5\x67\x0a\x73\x9d\x90\xec\x20\x1f\xa2\xe0\xef\x37\x57\x3f\x0f\xa3\x66\xab\x9a\x14\xd2\x92\x2f\xce\xf7\x45\xf7\x75\x41\x17\x31\xcd\x68\x62\x77\x20\x5b\x20\x2d\x28\x1d\xa3\xee\xca\x60\x36\xfd\xd4\xb7\xef\x7b\x18\x7d\xa4\xdc\x83\x91\xee\x3c\x0e\xa6\xbf\x0a\xcf\xd0\x71\x23\x4c\x4d\x6c\xfe\x2c\x54\x81\x95\xbe\xac\xe8\x3e\x3f\x1f\x1f\x1f\xa7\xf4\x79\xbb\xbd\x9f\x78\xc7\xe8\xf1\x71\x6a\x75\x65\x52\xdc\x6e\x47\xe1\xf4\x1b\x36\x84\x93\x13\x10\x61\xaf\x2c\xba\xe7\xe1\xaa\xc5\x33\x84\x6d\x47\x8e\xc4\x62\xfd\xc3\xf3\xf9\x2c\xe5\x62\x9d\x38\x54\x42\xb9\x44\x66\x63\x64\xfc\x37\xe1\x90\x5c\xc5\x5b\x5e\x04\x97\x6f\x22\x35\x55\x25\xb3\xaf\x24\x44\x70\x66\x3a\x71\xfa\x01\xd5\x31\xb4\xf8\x75\xc0\xeb\x9e\xb7\x17\x95\x2a\x84\xb1\x4b\x91\x27\xb9\x4e\x45\xde\x19\xb5\x85\x59\x2d\x47\x3b\x58\xe6\xe0\x80\xf3\xea\x60\x2d\x46\x22\x54\xe8\x28\x58\x79\x36\x4a\xa9\x1c\x1a\x85\x0e\x84\x23\x76\x2b\x93\x0f\xf0\xda\xb8\x31\x49\x2a\x54\x8a\x79\xde\xe9\x44\x5c\xfd\x34\x85\xd7\x7e\x4e\x93\xbf\xe2\xb0\x6c\x24\x82\xb9\x90\xdd\xd0\x5b\xf9\xf1\x4c\x66\xc1\x34\x14\x65\x8e\x0e\xc1\x56\xb4\xa5\xf3\x2a\xcf\x37\x53\xb8\xae\x14\x7c\x7e\x1a\x00\x7e\xe6\x78\x85\x03\x68\x32\xd5\x4e\x8a\x3c\xdf\x34\xd1\xb2\x0f\x8c\xc6\x52\xea\x93\x77\x89\x75\xc2\x55\x5d\xce\xeb\xc9\xc9\xc9\xc9\xab\x57\xaf\x5e\x1d\xce\xf1\xdf\xf0\x52\xa0\x09\x34\x71\x14\x56\x2e\xd5\x60\x36\x46\x44\x51\x34\x19\x84\xfa\x8e\x17\x4e\xbf\x92\x3d\x7f\xaf\xdb\x6b\xc7\x23\xe9\xdd\xef\xbb\xb6\x07\xdd\xbb\xe3\xa3\xf1\x0d\xc9\x6f\x07\xe5\x33\x24\x18\x4a\x2f\x09\xe7\xd4\xd8\x79\x20\xa3\x9b\x08\x97\x90\xfb\xd7\x81\xf4\xf1\x71\x9a\x16\xd9\x76\x1b\x32\x71\x8f\x8f\x53\x5a\xe8\x36\x25\x6e\xb7\x6c\x2a\x69\xed\x76\x7b\x3f\x9d\xf6\xe2\x66\x9f\x7d\x93\x44\x7d\x1e\x28\xeb\x3d\x3e\x52\x04\x11\x10\x10\x91\xdb\xed\x3d\x2c\x85\x85\x19\xa2\xda\x61\xb8\x3e\x21\xe3\xb1\x77\xd7\x01\xdf\xc4\x71\x38\x48\xc0\x74\xda\x93\x41\x0d\x28\x9a\x64\xf8\xb7\x63\xb1\x81\x39\x86\xc9\x38\xbb\x9b\xcd\xbb\x66\xc6\x41\x46\x7b\xf9\xcc\xb0\x44\x95\xa1\x4a\x8f\x11\x67\xb3\xe8\xf9\x78\x9a\x23\xd2\x29\xd3\x37\x07\xd1\x7c\x8d\xe2\x1c\xa6\x82\x0c\x43\x65\xba\xdc\xc4\x37\x3b\x29\xf0\xc3\xac\xff\x1f\xde\x11\x91\x9f\xe3\xf4\xe4\xeb\x76\xf0\xa9\x99\xfb\x36\x7b\x38\xf2\x64\x74\x51\xd2\xbf\x8f\x77\x7b\xc5\x8c\xe7\xec\x64\x1f\x55\x21\x61\xf1\xdc\x3b\x87\x29\xf2\x37\x40\x9d\x10\xe9\xa3\x05\xb2\xca\xd0\x4e\xc6\x94\x6b\xeb\x46\xfc\xfd\xf4\x2d\xf2\x38\xd7\x95\xca\x92\x40\x6f\xb0\x54\x9d\x0a\x10\x92\xfc\x07\x2d\x64\xa8\x24\x70\x3f\x04\xd1\xd5\xaa\x23\xc4\x5a\xff\x7e\x4e\x99\x2f\x29\xff\x99\x20\x08\xcb\xbc\x70\xb5\x7e\xac\x5b\x10\x52\x7c\x49\xa8\x62\x75\x15\x02\xfd\x28\xc7\x36\xd0\x4a\x3f\x1a\xe4\xb4\x4a\x36\xe1\xb2\x70\xe3\x6e\xd5\xdb\x46\x74\x98\x7a\x45\x40\x02\xa2\x55\x2d\x69\x17\x59\x7d\x2f\x43\xd0\x7e\xe3\xcb\x80\x43\x8d\x1f\x17\xd7\xd7\x57\xd7\x37\x1d\x74\xbf\xda\xff\x07\x7e\x3a\x3c\x19\x78\xf5\xaa\xe7\xfa\x31\x66\xf7\xa0\x3d\x28\xbd\x56\x09\x79\x0a\xc3\x47\x9d\x66\x91\xa8\xc2\xaa\x29\xb4\x72\xf5\x5c\x02\xb1\x55\xe9\x2b\x06\xa7\x9c\xe5\x9e\xda\x8d\x75\x58\xc0\x4c\xaa\x4c\xaa\x85\x05\x6d\x60\x21\xdd\xb2\x9a\x4d\x53\x5d\xd4\xd5\xc6\xfe\xfb\xd2\x98\x78\x67\xa6\x06\x85\xeb\x22\x93\xfb\x9c\x80\xa7\xec\xa8\x25\x77\xbb\x70\x83\x54\x6c\x0d\x39\xa3\x41\x34\x66\xbb\xe5\x32\x85\x1f\x4b\x75\xe6\x07\xe8\xc3\x40\x34\xd3\x22\xc9\x9f\x95\x5e\x92\xb2\x27\x27\xe5\x77\x22\x69\x8e\x48\xe1\xf4\x4a\x3f\x74\x11\xf4\x96\xcd\x16\x99\x0b\x3f\x8d\x0f\x24\x2d\x83\xf5\x12\x5b\x85\x3b\xe7\xdb\x9c\xc2\xd0\xef\x43\xed\x03\x6e\xea\x94\x0e\xf9\xbb\xc2\x69\xd3\x97\xae\xaa\xe7\x70\xf6\xe3\x63\x14\xe6\x3d\xe9\x63\x80\x33\x88\x33\x66\x76\x13\xa5\x9d\x37\x76\x1d\x08\xdf\xb7\x53\xc0\x6c\xab\x79\x36\xc5\xbb\x9c\x83\x6d\x7b\xd4\x43\x48\xd9\x7b\x2f\xa4\x2d\x84\x4b\xbb\xdc\x77\x62\xb0\x56\x0f\x5a\x90\x31\x8a\x2c\xda\x53\xa9\xf6\x6b\x0d\x7e\x3c\xd0\xc0\xed\x52\x4c\x26\x23\xe1\x6d\x65\xf3\x46\x93\x8a\x16\x90\x9d\xd4\xb6\x1f\x8d\x6c\xf4\x33\x11\xe2\x7f\x52\x2f\x91\xcb\x2e\xb1\x5d\xfa\x51\xee\xf1\xf2\x5b\x52\x67\x91\x09\x57\xf8\x4c\xb4\x1c\x6c\x10\xe3\xda\x29\xd1\x2e\x7c\xdd\x90\xd6\xf8\x8f\x63\xe4\x1c\x49\x1c\x10\xf5\xf5\x31\x04\xed\xc9\x95\x8f\x82\xa7\xe8\x85\x05\x9f\xe5\xf1\xa2\xc4\x2f\x0e\x95\x8d\x44\xe3\x17\xbe\xc3\x88\x9d\xaf\x61\xc5\x26\x0b\xec\xca\xa7\x36\x47\x79\x81\xbe\xad\x25\xd8\xde\x26\x73\xff\xa4\x40\x4b\xf7\x9b\x4c\x5b\xc7\x77\xb4\x4c\x3d\xe9\x89\xe7\x98\x4f\x4f\x8d\xad\x83\xbe\x1d\x86\xd9\x2f\x24\x31\x36\x52\x16\x6a\x53\xeb\x06\x19\x91\xd6\xb6\x0f\xca\x35\xe4\x74\x6b\x12\x06\xd9\xa8\x4c\x7e\xbc\xe6\xfa\xc4\x56\x08\xa1\xef\xae\xdf\xf9\x8c\xa3\xc9\xc3\x51\xfa\xb8\x13\x63\xdf\xfb\x5e\xa5\x31\x84\x14\x22\x9f\x6b\x53\x74\x4a\xee\x7d\x1c\xef\xa3\x60\x0a\xb7\x66\x03\x62\x21\xa4\x1a\x0a\xe9\x8d\x49\xfe\x69\xb5\xaa\x8d\x6d\x5a\x64\x3d\x85\x64\xae\x35\x48\x55\x56\x0e\x32\xe1\x04\xbc\x0f\xd2\x78\x91\x16\xd9\x0b\x32\xbd\xfd\x98\x44\x29\x9b\x82\x80\x57\x1a\x6d\x12\x8b\xff\xaa\x50\x75\x66\xec\x7d\x7b\xed\xe9\x4d\x98\xb5\x7b\x58\x5a\xf6\xdd\xeb\xf3\x5e\xef\xc8\xf9\x87\x4b\xbf\xa0\x94\x34\x3b\x15\xca\xbb\x22\x33\xf4\xce\x40\xbb\xdf\xad\x51\xb2\xd3\x48\xd2\x01\x98\x53\xf8\x90\xa3\xb0\x08\x55\x99\x09\xb7\xd7\xac\xe2\x2f\xcf\x34\xaf\xb2\x7d\x3a\x85\x05\x01\x6b\x9c\xed\x63\x18\xdc\x9d\x20\xa7\x7e\x05\x3d\x3f\x60\x47\x48\x34\x61\xd5\x14\x2e\x9d\x8f\xbe\xb4\x5b\xf2\x5d\xbc\xdb\x82\x51\x1f\xbc\x89\x97\x8e\x56\x18\xaa\xc0\x05\x41\xc1\x2f\x25\xa6\x63\x4e\x52\xa0\x35\x6e\x71\xb4\x0f\x64\x18\x13\xc2\xfa\x95\xd4\x33\xe1\x8d\x91\x20\xb0\xba\x72\x6d\x63\x31\x85\x5f\x1a\x23\x1c\x4d\x05\x2d\x9b\xd4\xe6\x44\xda\xc6\x59\x18\xb8\xd6\x02\x3b\x51\x4c\x09\x45\x2b\x0e\x93\x4c\x9a\x51\x46\xee\x20\x5b\xc4\x47\x2d\xf7\x52\x4b\xe5\x5d\x2a\x1f\xa2\x39\x6c\x35\x39\x37\xc7\x79\x42\x21\x60\xe4\x8a\x9b\x8c\xf7\x2c\x5c\x3f\x1b\xa9\xa0\x80\x5d\xac\x30\xc9\x74\xfa\x80\x5d\x4f\x01\x5e\x0b\xc5\x50\xc5\x0a\xe1\x0d\x4f\x04\x59\xb0\x03\x3e\xe0\x58\xca\x1c\x13\x91\x1b\x14\xd9\x26\xc1\x2f\xd2\x76\xb6\x5a\xbc\xa5\x13\x12\x66\x82\x9f\x39\x00\x3b\x8b\xad\x82\x4d\x54\x22\xd1\x7a\x85\xb2\xe4\x39\xe5\x62\x86\x5d\xc5\x91\x2b\x85\x40\x7a\x98\xe3\x7e\xd8\xdf\x7c\x8d\x5b\xe2\xd6\x1a\x6a\x64\x5c\x34\xf1\xb2\xa6\xd9\xf1\x9b\x37\xac\x4b\x69\xe1\x41\xaa\x8c\x0e\x48\xd0\xc5\x50\x23\x7d\x72\xf1\xec\x59\x0a\xb2\x2f\x2d\x42\x98\xf4\x03\xe4\x84\x07\x01\x4f\xec\x0a\x2b\x0b\xd7\xf7\xc9\x77\x8b\x44\x41\x0c\x6b\x90\x79\xb0\x58\x0a\x43\x5f\x18\xba\xef\x37\xeb\xe0\x6d\x9c\xf2\x87\x43\x96\x10\xcb\xc7\xea\xb9\xd2\x5e\x52\x16\xdd\x71\xc8\x8e\xb5\x15\x01\x59\xeb\xbc\x0f\xe0\x8b\xd6\x37\x59\x8a\x15\x59\x2a\xd6\x25\x9f\x48\xb7\x81\x98\xae\xc7\x2a\xed\x6b\x28\x82\x09\xf6\x2a\xaa\x76\xec\x91\x20\x9b\xaf\xa2\x31\xf2\x81\x3e\xbb\x62\xb4\x7f\x21\xba\x9d\xc6\xd7\x23\xa1\xc5\xd7\xc3\xb3\x7c\x51\x91\x32\xf1\x13\x07\x5e\xc0\x1e\xbb\x54\x20\xa2\x4e\x47\x08\x03\x87\x5f\xab\x79\x2e\x53\xb2\x32\x49\x08\xdc\x88\x43\xa3\xad\x8d\x99\x90\xae\xe3\xda\x3a\x3f\x31\xe4\x23\xa6\xc3\xe7\xc0\x73\xe4\x95\x9d\xdf\xa2\xca\x9d\x2c\x73\x1f\x35\xfa\xc3\x43\x9f\x82\x47\xe2\x91\xb3\xf9\x8a\x77\xef\x5e\x1a\xc4\xb5\x8b\xca\x13\x90\xce\x9f\xa8\x52\x5b\x2b\x67\xfe\x14\xb0\x40\x22\x23\x1e\x6b\x23\x9e\x19\xf9\x25\xb5\xa6\x33\x11\x4f\x0e\x61\xe0\x84\xd1\x3c\x09\x7a\x8e\x10\xa6\xa9\x72\x7c\x86\x24\x69\x59\x88\x2e\x72\x3c\x24\xc3\x86\xfe\x68\xef\xf7\x1c\x09\xff\x06\xa5\x16\xc1\xee\x96\x4c\xfd\xd3\xa3\x6f\x21\x64\x66\xf0\x90\x84\x85\xb5\x3a\x95\x0c\xfa\x30\xc5\xa7\x91\xb8\x7d\xe1\x33\xf3\xcf\x92\xbc\x30\x4d\x8b\x07\x17\xb3\x3b\x5b\xdb\x43\x81\x0c\x72\xa9\x10\x84\x59\x54\x1c\x14\x93\x08\xcd\x62\xbb\x6d\xfb\x8b\x0c\x67\x02\xa5\x27\x31\xbe\xfa\x20\x79\xf0\xc8\x11\x14\x3d\xe0\xe6\x9b\x51\xf5\x80\x9b\x53\x86\x05\xa5\x90\xe6\x09\x79\xbb\xc3\x6c\xdf\xf1\x8b\x28\x4a\x72\x76\x6b\x70\x0f\xb8\x19\xc5\x43\x70\xb0\x86\x3b\x91\xba\x18\xf8\x21\xa2\xfc\x23\xdb\xe0\x00\xcf\xb7\x29\xf9\x8b\xab\x4e\x85\x4c\x7c\x42\xb2\x15\x5e\x46\xe5\xa8\xdf\xdb\x80\x5f\xcd\x41\x46\x03\x62\x28\xf7\x80\xff\xaa\xa4\xe1\xdc\x56\x59\x39\x3b\x4a\x4b\xae\xc3\x1a\x1f\xca\xf8\xd3\xb2\xa3\x15\x16\x70\x85\x0a\xc4\xdc\xa1\x01\x51\x96\x39\xd7\x4f\xb8\xb1\xa1\xd4\x1e\x4e\xa8\xa5\xa2\x5a\x4d\x61\x25\x8c\x14\xb3\x1c\x1b\x85\xb7\xe8\x6a\x88\xbb\x53\xe2\x01\xf6\x51\x54\xd3\xc6\x75\xe8\xb5\x8d\x7f\xd9\x64\xc2\xfb\x23\xde\xec\xb9\xce\x73\xbd\xf6\xd4\x10\xed\x2c\x4f\xff\x71\xbb\x1d\x8e\xbe\x16\xbe\x41\x25\xa1\xa0\x87\x2b\xc6\x43\x81\x45\xab\xa9\x85\xd6\x34\x09\x2e\x51\x4a\xfa\x21\xe6\x98\x0e\xb8\xeb\x3c\xb5\xee\x58\x8b\x0f\x08\xf6\xbd\xa4\x10\x72\x18\x24\xa4\xab\x80\xa0\xce\x14\xef\xc1\x18\x99\x87\x2a\x85\xb1\x68\x7a\x1f\xbe\x36\x19\x12\x83\xce\x48\x64\x83\x17\x12\x23\xb5\x86\xf6\x63\x5b\xe3\x2c\x3a\x2e\x41\x09\x7d\x1f\x76\xec\x16\xea\x93\xeb\x9d\x12\xc1\xd6\x5a\x4c\x2b\xe3\x9d\xc3\x26\x3c\xfc\x0f\x38\xe8\xe7\x9c\x93\x87\x2e\xea\x81\x90\xe2\x6c\x9f\x3c\x6f\x1a\x68\x90\x3f\x75\xa7\xee\x7e\x39\xbf\xfe\xf9\xf2\xe7\xbf\x8d\x2f\x27\xc4\x05\xc7\x15\x14\xd6\xc2\xa8\xba\x67\x81\x24\xdd\x95\x59\xb8\xa6\x31\x52\xeb\x8f\xb1\x59\xe1\x3e\x1c\x3f\xde\xc5\x33\x9f\xe1\xa1\x5d\xb9\xef\x8b\x42\x03\x3e\x6e\xe1\x3a\x3a\xa7\xd3\x6e\x3d\x6f\xe5\x70\x21\x43\x37\x1c\xff\x32\x66\xba\x08\x32\x2c\x0d\xa6\x74\x3d\x26\x06\xcb\x5c\xa4\x9d\x01\xe2\xed\xd2\xe3\xd1\x79\x16\xb6\x92\x5b\xf7\xbc\xff\xbf\xdb\xa4\xc1\xef\x69\xad\xd6\x0a\x66\x1c\x44\x04\x0c\xf5\xf5\x50\x59\xaf\x42\x5c\x66\xc3\xf5\x0e\x38\xeb\x50\x8c\xa4\x3d\x48\xe2\x39\x89\x76\xbb\xd4\x55\x9e\x11\x79\xe4\xee\xc3\x9d\xf5\x15\x67\x5f\x0e\x3b\xa0\x96\xfd\x79\xa9\x9a\x22\x9e\x3f\xb0\x95\x44\x97\xc7\x40\x16\xf2\x69\x01\x80\xec\x8d\x37\x4d\x47\xa0\xe4\x08\x5f\xac\x7a\x37\x6f\x08\x29\xaf\x8f\x1b\x1a\x4b\x9b\xf1\x81\x61\xfb\x65\xe1\x30\x61\xb9\x2c\xa4\x4b\xe4\x42\x69\xd3\x49\x52\x54\xe9\x10\x71\xf0\x12\x1f\xc1\xd2\xa7\xfd\x24\x3f\x59\x6c\x0f\x6e\x2c\xf6\x74\x29\xd4\x02\xc9\x70\xf5\x07\x67\xef\x6a\xc4\x75\x71\xc1\x46\xf6\xf3\x8d\x2f\x6e\xd7\xa0\xa6\x70\x49\x54\x48\xb5\x18\xa3\x12\x4c\x88\x4d\x72\xbd\x48\xac\xfc\x6d\x80\x0e\x9e\x7c\x06\xb9\x5e\xdc\xc8\xdf\x48\x75\x39\xef\xa7\x2b\x67\x65\x16\xc3\x71\xaf\x9f\x86\xa8\xa1\x1d\xf9\xf8\x72\x02\x7f\x7a\x79\x0f\xef\xff\x5a\x5f\xe5\x2b\x34\xe4\x9d\x70\x89\xb6\xf4\x6f\x6c\x4d\x73\x41\xf1\xd3\x72\xef\x6b\x8d\x25\xbe\xc0\x42\x9b\xcd\x78\xfa\xfd\xfc\xf1\x2c\xfc\xe9\xcf\x7f\x99\xc0\x9f\x5f\xfe\xdb\x5f\x7e\x5f\x36\xe8\xae\xd4\x55\xe7\xfb\xde\x1d\x16\xc2\xdc\x91\xf4\xbf\x7c\x39\x81\x7f\x7f\x49\xff\xee\xa1\x90\x79\x2e\x2d\xa6\x5a\xb5\x62\xb9\x6f\xc7\x0b\x17\xa2\x93\xd2\xe8\x12\x8d\x93\x9d\x01\x5c\xb4\xd4\x2d\xbb\xea\xdb\x17\xbc\xeb\x10\x1a\x18\x7c\x55\xbb\x01\x16\x1b\x1d\x0e\xdb\xee\x68\xba\x33\xcd\x27\x82\x2c\xb8\x74\xb5\x68\xf4\x1c\x6e\x8d\x58\x49\x0b\xb3\x4a\xe6\x59\x7f\x15\x9c\x59\xf1\x66\x8b\xc5\x38\xca\x64\xd5\xc7\x73\xc7\x70\xa9\xbd\x8b\x27\x98\x75\xae\xed\x53\xa4\xe9\x7f\x8d\xcf\x93\x1f\x1f\xa7\x85\x54\xa1\xd2\x4b\x5f\x44\x3a\x50\x37\x62\x52\xa3\x9f\xe6\xad\x40\x97\x39\x8b\xb5\xb8\x30\x8b\x9c\xa5\xbd\xb2\xdc\x81\xd4\x7d\x67\xe5\xed\x59\xe5\x36\xa6\x36\x14\xf3\x39\x3d\xd4\x9b\xdf\x7c\x52\xa7\xdd\xb1\x81\x7b\x89\xcf\x26\x52\xc8\xf9\xd1\xa4\xd2\x6e\x19\xf2\x12\xc3\x24\xc5\x7c\xc3\x60\xa9\xfa\xf6\x49\x26\xb1\xed\xd8\x84\x97\x25\x98\x81\xd2\xe3\xfa\x2d\x18\x7b\xab\xd5\x89\x85\x32\x86\x88\x83\x8d\x40\xe1\x66\xdc\x8f\x78\xd6\xa1\x1e\xe8\xab\xea\x87\xf2\xa1\x23\x24\xd4\x7a\x1f\x96\xe8\x15\x1a\x23\xb3\x0c\xbb\xb2\x7a\x44\x61\xfb\xb9\x58\xd3\xaa\xd6\x2c\x8d\x3e\x4d\xbb\x13\x69\xec\x46\x25\xd2\x26\x65\x35\xcb\x65\xd7\x93\x7c\xbf\x2b\x3c\x37\x56\xb5\xfc\x8b\x38\x8a\xa3\x78\xe1\x93\x8c\xc9\x84\xcc\x05\xdb\x96\x19\xc2\x4a\xfa\xe4\x0d\x9d\xc3\x54\xb0\xa5\xf1\x6f\x10\x30\x83\xd9\x06\x84\xda\x68\xd5\xf3\xc2\x8c\x69\x8d\x49\x58\x9c\x85\x77\xbf\x03\xee\xc6\xd3\x1c\x2c\x97\x97\x38\x8a\x51\x19\xfd\x7f\x12\x9e\xe8\xee\xd7\x97\xe8\x20\xf0\xdf\x58\xc1\xd9\xc4\x3b\x21\xe1\x5b\x58\xd0\x13\x78\x79\x4a\x5b\x75\xc4\x26\x0a\x3b\xb2\xba\x44\x1a\xd6\x2e\xc9\x8d\x2a\x16\x86\xbf\x0d\x53\x2f\x9a\xc2\x6b\xad\x56\x64\xee\x43\xe8\xd2\xa0\x70\x7a\x07\xfc\xb0\xca\xee\x73\x35\x50\x17\xed\xcb\x44\x37\xbc\xc5\x81\x23\xb9\xab\xcb\x93\xfb\xfc\xb5\x11\xd5\x1c\x8e\x2a\x66\xd6\x3c\xc6\xac\x93\x41\x5b\x6a\x65\xb1\xaf\xbd\x6c\x8f\x68\xce\x37\xee\xe7\x15\xc2\x78\xcc\x20\xb4\x32\x12\x31\x37\x54\xe7\x34\x97\xce\x95\xfe\xef\x30\x79\xd4\x7c\xaf\x4d\xe1\x35\xdd\x30\xdc\x8f\xd2\xfe\xdd\x5f\xea\x7c\xe5\x84\x9f\x03\xd3\x0c\x85\xee\x93\x86\xb2\x21\x8d\x8d\xfb\x8a\x6a\x25\x8d\x56\x6c\x3b\x63\x4a\xa8\xab\xd2\x1f\xa2\xd2\x8b\x66\x09\xfc\x23\x2c\x19\x13\xe1\xbf\xb9\xf8\xeb\xdd\xdf\x46\x87\xf7\x3c\xfb\xb8\xd8\x3e\x9b\x2d\x12\x8b\xc2\xa4\x4b\xe2\x2c\x1a\xdc\xba\x80\xd9\xa9\xb6\x61\x45\x6d\x70\x77\x4b\x9e\x71\xfb\xa2\x7c\xbd\x63\x32\x10\x1b\x10\x29\xfb\xb7\xd2\xb7\xbe\x91\x9e\x79\x1b\x11\x69\xf5\x75\xed\x5b\x68\x7b\xfe\x2c\xce\x9b\x03\x7d\x5c\x41\x22\x67\xf0\x96\x29\x68\xfe\x0a\x0b\xa7\xf3\x09\xd8\xb1\x04\xf4\xbf\x23\x3e\x9e\x86\x76\x97\x6e\xec\x2a\x3f\xee\x6d\xe8\xde\x5b\xbb\xbe\x27\x8e\x34\xf9\xc9\x03\xbb\xe3\x5f\x71\x86\xb8\xa1\x6e\x0b\xfe\xe6\x44\x4c\xd8\xa5\x7f\xf1\xf8\x38\xcd\xaa\xa2\xd8\xf0\xac\xed\xf6\x05\x99\x9f\x76\xdc\xa3\x55\xbf\xfe\x84\x77\xcc\xc9\x6f\xb2\x4c\xf0\x0b\xb7\x96\xf8\x92\x7b\xcf\x7b\x9f\x0b\x9e\x47\x67\xec\x83\x70\xcb\xb3\xf6\x0e\x8e\x45\x25\xb2\x2c\x3e\x30\xea\xc3\x74\xce\xd3\x76\x0e\xae\xd3\xf0\x3f\xb2\x84\xb7\x43\x07\xa3\x8d\x2d\xf4\xcc\xc4\x16\xb2\x1e\x84\x6f\x43\x13\xe0\x8d\x77\xf2\x9f\xcd\xdf\x01\x8c\x49\x86\xd6\x49\xc5\xa8\xbe\x86\x04\xf6\x7e\xde\x34\xb0\x5a\x33\x5a\x18\x46\xd2\x1a\x2f\xcb\x48\x2f\xaa\xee\x1c\x6a\x4c\xa4\xc0\x65\x68\x41\xba\xa0\xc9\xa4\x70\xd2\xb5\x12\xf4\x4c\x49\x80\xc7\x25\xc3\x38\x9d\x61\xb3\x63\x80\x92\x83\x11\xbe\x33\x3f\x7a\x3e\xef\x81\x0c\xb2\xff\x3c\x69\xb3\xd7\x7f\xd4\x23\x1f\xb1\xf5\x9a\x85\xdf\x53\x69\x7a\x1d\x5b\xb4\x49\xc2\x51\x8f\x8e\xde\xe1\x5c\x5a\x97\xe8\x39\x23\xb2\x09\xb7\x67\xf2\x1d\x25\x9c\x43\xd3\x75\xb0\xbd\x69\xe3\xa6\xe7\xba\xc8\xe2\xff\x80\x95\x2f\x6e\x07\x28\x71\xdf\xb9\x9b\xe5\x83\xf7\x45\x18\x6c\xaf\x1c\x82\x73\xbd\xfb\xac\xbe\xeb\x50\xed\xbe\xbd\xa7\x9b\xb0\xb3\xed\x81\x83\x94\xb6\x37\x10\x9c\x38\x62\xe3\xfa\xe2\xbf\xef\x2e\xaf\x2f\x92\x5f\x7e\xbc\xbc\xf9\x29\x39\xbf\xbb\xfd\xb1\x55\x41\x88\xd4\x7e\x77\xff\xdd\xff\x06\x00\x00\xff\xff\x58\x2a\x2d\x4f\x3d\x54\x00\x00") +var _wski18nResourcesEn_usAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x7c\x7b\x8f\x1b\x37\x92\xf8\xff\xf9\x14\x85\x60\x01\x27\x80\x46\xf6\x2e\x7e\xf8\x61\x31\x77\x3e\x60\xd6\x1e\x27\xb3\xb1\x33\xbe\x79\x24\xc8\xd9\x83\x36\xd5\x5d\x92\xb8\xd3\x4d\xf6\x92\x6c\xc9\xca\x40\xdf\xfd\x50\x45\xb2\xbb\xa5\x51\x3f\x34\x76\x70\xf1\x3f\xd6\x88\x64\xbd\x58\x2c\xd6\x8b\xfa\xf0\x0d\xc0\xc3\x37\x00\x00\xdf\xca\xec\xdb\x53\xf8\xb6\xb0\x8b\xa4\x34\x38\x97\x9f\x13\x34\x46\x9b\x6f\x27\x7e\xd4\x19\xa1\x6c\x2e\x9c\xd4\x8a\xa6\x9d\xf3\xd8\x37\x00\xdb\x49\x0f\x04\xa9\xe6\xba\x03\xc0\x05\x0d\x0d\xad\xb7\x55\x9a\xa2\xb5\x1d\x20\xae\xc3\xe8\x10\x94\xb5\x30\x4a\xaa\x45\x07\x94\x5f\xc3\x68\x27\x94\xb4\xc8\x92\x0c\x6d\x9a\xe4\x5a\x2d\x12\x83\xa5\x36\xae\x03\xd6\x15\x0f\x5a\xd0\x0a\x32\x2c\x73\xbd\xc1\x0c\x50\x39\xe9\x24\x5a\xf8\x4e\x4e\x71\x3a\x81\xf7\x22\xbd\x17\x0b\xb4\x13\x38\x4b\x69\x9d\x9d\xc0\x8d\x91\x8b\x05\x1a\x3b\x81\xab\x2a\xa7\x11\x74\xe9\xf4\x7b\x10\x16\xd6\x98\xe7\xf4\xbf\xc1\x14\x95\xe3\x15\x2b\xc6\x66\x41\x2a\x70\x4b\x04\x5b\x62\x2a\xe7\x12\x33\x50\xa2\x40\x5b\x8a\x14\xa7\xa3\x79\xd1\xba\x8b\x93\x9b\x25\xc2\x65\x89\xea\xd7\xa5\xb4\xf7\xf0\x9a\x99\x29\x88\x84\x1b\xad\xf3\x8f\xea\xa3\xba\xd1\x30\xc3\x85\x54\xb0\xd6\xe6\x5e\xaa\x05\xac\xa5\x5b\xc2\xda\xde\x7b\xc6\x27\x60\x2a\x4f\xe0\xb3\xfa\xbb\x67\x90\xea\xa2\x10\x2a\x3b\x25\x00\x1f\xdd\x5f\x9a\xe9\x0c\x71\x29\x2d\xac\x65\x9e\x07\xd9\xb5\xf0\x0b\x6b\xd1\xd9\x16\xaf\x52\x41\x21\x94\x9c\xa3\x75\xd3\x8d\x28\x72\xd0\xa6\xf5\x45\x91\x7f\x54\x17\x73\x48\x2b\x63\x88\xe4\x4c\x1a\x4c\x9d\x36\x1b\xc8\x34\x5a\xe5\x60\x29\x56\x08\x42\x6d\xea\x25\x30\x97\x39\x4e\x1a\x72\xa0\x34\x52\x39\x0b\x8e\x48\x5a\x62\x5e\x42\x81\xd6\x8a\x05\x4e\x3d\xa1\x08\x85\xb6\x8e\xd9\xd1\x0a\xd6\x62\x63\x41\xcf\xa1\xb2\x2c\x87\x1a\x88\xd3\x91\x13\xa1\xb2\xe7\xda\x40\xa5\xba\x38\x13\x06\x59\x28\x3b\x22\x69\xfd\x01\x27\x05\x94\xc2\x2d\x9f\x3b\xfd\x7c\x87\xf1\x71\xb3\xe0\x24\xab\x07\xb2\x7a\x2f\x0f\x00\x88\x14\x1e\xfe\x76\x24\x15\x83\xd3\x7b\xc9\xf9\xa8\xce\x2a\xe5\x96\x74\x6c\x52\x56\xc7\xd3\x8f\xaa\x81\x6d\x50\x64\x16\x52\x83\x19\x4d\x10\xb9\x85\xb9\xd1\x05\xfc\xe5\xc7\xcb\x77\xe7\xcf\xa7\x6b\x7b\x5f\x1a\x5d\x5a\x98\x6d\x20\xc3\xb9\xa8\x72\xf7\x51\x5d\xae\xd0\xac\x8d\x74\x18\xbf\x82\x54\xab\xb9\x5c\xf0\xa6\xd3\x51\x7d\xf5\xf6\xe2\xf4\xa3\x02\xd8\x91\xe4\x49\x98\xf4\x9f\xad\xc9\xff\xd5\x23\x80\x4b\x13\xd4\x73\x03\x22\xcf\xc1\x2d\x0d\xf6\x00\x17\xa5\x5c\x92\x06\xfd\x78\x79\x7d\x43\x7f\x56\x6e\x09\x3f\x9d\xff\x06\x27\x27\xf5\x29\x86\x9f\xcf\xde\x9d\x5f\xbf\x3f\x7b\x75\xde\x89\x75\xc4\x39\xb7\x4b\x6d\x5c\xbf\xd1\x7a\x6f\xf4\x4a\x66\x68\x41\x80\xad\x8a\x42\x18\x92\x32\xcd\x27\x9d\x7e\xa4\xa9\x33\x24\x25\x8f\xd6\xed\x79\xdc\x6b\xcc\x60\x26\x2c\x66\xc4\x72\xa4\xb1\xb5\xb7\xf0\xdb\xd9\xbb\xb7\x63\xec\x52\xa0\xb7\xdb\x30\x9d\x81\xd3\x3a\x07\x8b\x8e\xce\x17\x9f\xcd\x20\xd5\x8d\xae\x0c\xe8\x12\xd5\x9a\xe9\x2d\x83\x9d\x0d\xc7\x52\xec\x1e\xf6\xf1\xb4\xac\xd0\x58\xc2\xdd\x25\x3c\xa9\x1c\xdb\xb9\x30\x0f\x54\x55\xcc\xd0\x90\xec\xea\x0d\x1f\x8d\xcb\x6e\x54\xda\xcf\xb7\xd3\x40\x93\x3c\xb3\xcd\xe6\xd4\xcc\xce\xd0\xad\x11\x15\xa4\xb9\x24\xb1\x0b\x95\x81\x45\xb3\x42\x33\xfa\x52\x18\x4f\x43\x6b\x7b\x09\x4f\x54\x05\xfe\x62\x47\x75\xba\xb7\x82\xd6\xe9\x92\xe0\x8b\xbc\x0d\x8f\xb6\x28\x4e\x67\xd5\x21\xbb\xf0\x5a\xce\xe7\xc8\x16\x3d\x5a\x5c\x53\x29\xba\xbb\x99\x9c\xd3\x5d\x23\x44\x5f\x3d\xfe\x66\xa4\x05\xeb\x9d\xda\xb6\x5e\x4f\x87\x71\x52\x1a\xfd\x2f\x4c\x1d\x9d\x77\x78\x7f\x75\xf9\xcf\xf3\x57\x37\xa3\xf5\x24\x8a\xba\x63\x9f\x6e\x3b\xef\x19\x36\x96\x5e\x21\xc6\xea\xc3\x58\x5c\x06\x0b\xbd\x42\xfb\x18\xe7\x7a\x29\xd3\x25\xac\xd1\x60\xe3\x14\x31\x1d\x74\x6a\x76\x34\x61\xdf\x5e\xec\xf8\x19\x19\xe6\xe8\x68\xb3\x0f\x33\xb5\x03\xcc\x5f\xe7\xa6\x52\xa7\x7f\xba\xeb\xed\x30\xa4\x43\xda\x00\xdf\x69\x95\x6f\xd8\xbf\xb2\x30\xd7\xa6\x25\x1e\xf6\xfe\x58\xc1\x0a\x9d\xe1\xf7\xa3\xf5\x06\x3f\xf7\xdc\x03\xe7\x3c\x08\x81\x92\x1d\xe1\xd6\x22\x1f\xab\x34\x23\x10\x59\xda\x2e\xb1\xc0\xac\x1f\x23\x59\x9b\x1d\x25\x99\x57\x8a\xfd\x66\x6f\x23\x3a\xfc\x31\x5a\x45\x0e\xa8\xa7\x63\x4f\x0b\xfc\x97\x1d\x42\x6f\x6d\xaa\x9f\x87\xd9\xc9\x11\x97\xee\x3c\x17\x8b\x44\x94\x32\xa1\xeb\xbd\x83\x7f\x7f\x3f\x9d\xbd\xbf\x80\x4f\x74\xff\x7f\x1a\x09\xb1\xff\x22\x6a\x01\xfd\xe5\xfc\xea\xfa\xe2\xf2\xe7\x51\x70\x2b\xb7\x4c\xee\xb1\xeb\x70\xd3\xb0\x36\xf2\x77\xfe\x02\x3e\xfd\x74\xfe\xdb\x18\xa0\x29\x1a\x97\xd0\xee\x74\x40\x25\xf9\x92\xf5\xa6\x23\x3b\xa5\xc9\xbc\x95\x63\x00\xb3\x2b\xd6\x01\xb5\xed\xd4\x7d\x17\x3d\x3d\x69\xf7\x5d\xc3\x81\xc3\xe2\xa5\x92\xe7\x7a\x9d\x04\x18\x5d\xd1\x27\x4f\x82\x7a\xd2\x30\xd4\xe6\xf8\xf6\xc9\xa5\x0e\x1a\xea\x7b\x70\x04\xe8\xd2\xe0\x4a\xe2\xba\x03\xae\x5d\x32\xa1\x11\xe8\xf3\x9d\x8b\xba\xcc\x85\x1a\x81\xe1\x1e\x37\xa3\xb7\xf4\x1e\x37\x63\x09\xf7\x92\x0e\x86\xa0\x57\xd0\xd1\x48\xd4\xe1\xb4\xa3\x8b\x01\x0a\x61\xee\x31\x8b\xa6\x64\x94\xa8\x18\x4e\x42\x87\xbe\x8b\x99\x80\x8a\xa7\x0c\x43\x8c\xd6\x61\x60\x57\x77\x2e\xa7\x11\x60\xeb\x40\xa0\x03\x6e\x33\x3e\x9a\xe9\x01\x0a\xbd\x5f\x90\xa3\xb5\x51\xda\x23\x40\x5b\x67\x64\x27\x64\xbf\x75\x95\x45\xba\xbc\xe6\x52\x61\x46\x56\xd9\xc9\xa2\x76\x97\x47\x60\x70\xa6\x5b\x08\x3c\x06\xba\x72\x65\x35\x86\x58\xaf\x6e\x2b\x34\x33\x6d\xbb\x40\x86\xd1\x63\x81\x96\xc2\x88\xa2\x53\xc0\x46\x14\xe8\xd0\xc0\x4a\xe4\x15\xf2\xed\x4d\xc6\x14\x7e\x39\x7b\x7b\x7b\xfe\x89\x2e\xf7\x42\x1c\x89\xaa\xef\x34\x7e\x7a\x73\xf1\xf6\xfc\x13\x85\xb9\x4e\x48\x76\x90\x0f\x51\xf0\xcf\xeb\xcb\x9f\x87\x51\xb3\x55\x4d\x0a\x69\xc9\x17\xe7\xfb\xa2\xfb\xba\xa0\x8b\x98\x66\x34\xb1\x3b\x90\x2d\x90\x16\x94\x8e\x51\x77\x65\x30\x9b\x7e\xec\xdb\xf7\x3d\x8c\x3e\x52\xee\xc1\x48\x77\x1e\x07\xd3\x5f\x84\x67\xe8\xb8\x11\xa6\x26\x36\x7f\x12\xaa\xc0\x4a\x5f\x56\x74\x9f\x9f\x0f\x0f\x0f\x53\xfa\xbc\xdd\xde\x4d\xbc\x63\xf4\xf0\x30\xb5\xba\x32\x29\x6e\xb7\xa3\x70\xfa\x0d\x1b\xc2\xc9\x09\x88\xb0\x57\x16\xdd\xd3\x70\xd5\xe2\x19\xc2\xb6\x23\x47\x62\xb1\xfe\xe2\xe9\x7c\x96\x72\xb1\x4e\x1c\x2a\xa1\x5c\x22\xb3\x31\x32\xfe\x41\x38\x24\x57\xf1\x86\x17\xc1\xc5\xeb\x48\x4d\x55\xc9\xec\x0b\x09\x11\x9c\x99\x4e\x9c\xbe\x47\x75\x0c\x2d\x7e\x1d\xf0\xba\xa7\xed\x45\xa5\x0a\x61\xec\x52\xe4\x49\xae\x53\x91\x77\x46\x6d\x61\x56\xcb\xd1\x0e\x96\x39\x38\xe0\xbc\x3a\x58\x8b\x91\x08\x15\x3a\x0a\x56\x9e\x8c\x52\x2a\x87\x46\xa1\x03\xe1\x88\xdd\xca\xe4\x03\xbc\x36\x6e\x4c\x92\x0a\x95\x62\x9e\x77\x3a\x11\x97\x3f\x4d\xe1\x95\x9f\xd3\xe4\xaf\x38\x2c\x1b\x89\x60\x2e\x64\x37\xf4\x56\x7e\x3c\x93\x59\x30\x0d\x45\x99\xa3\x43\xb0\x15\x6d\xe9\xbc\xca\xf3\xcd\x14\xae\x2a\x05\x9f\x1e\x07\x80\x9f\x38\x5e\xe1\x00\x9a\x4c\xb5\x93\x22\xcf\x37\x4d\xb4\xec\x03\xa3\xb1\x94\xfa\xe4\x5d\x62\x9d\x70\x55\x97\xf3\x7a\x72\x72\x72\xf2\xf2\xe5\xcb\x97\x87\x73\xfc\xd7\xbc\x14\x68\x02\x4d\x1c\x85\x95\x4b\x35\x98\x8d\x11\x51\x14\x4d\x06\xa1\xbe\xe3\x85\xd3\xaf\x64\x4f\xdf\xeb\xf6\xda\xf1\x48\x7a\xf7\xfb\xb6\xed\x41\xf7\xee\xf8\x68\x7c\x43\xf2\xdb\x41\xf9\x04\x09\x86\xd2\x4b\xc2\x39\x35\x76\x1e\xc8\xe8\x26\xc2\x25\xe4\xfe\x75\x20\x7d\x78\x98\xa6\x45\xb6\xdd\x86\x4c\xdc\xc3\xc3\x94\x16\xba\x4d\x89\xdb\x2d\x9b\x4a\x5a\xbb\xdd\xde\x4d\xa7\xbd\xb8\xd9\x67\xdf\x24\x51\x9f\x07\xca\x7a\x0f\x0f\x14\x41\x04\x04\x44\xe4\x76\x7b\x07\x4b\x61\x61\x86\xa8\x76\x18\xae\x4f\xc8\x78\xec\xdd\x75\xc0\xd7\x71\x1c\x0e\x12\x30\x9d\xf6\x64\x50\x03\x8a\x26\x19\xfe\xf5\x58\x6c\x60\x8e\x61\x32\xce\xee\x66\xf3\xb6\x99\x71\x90\xd1\x5e\x3e\x33\x2c\x51\x65\xa8\xd2\x63\xc4\xd9\x2c\x7a\x3a\x9e\xe6\x88\x74\xca\xf4\xf5\x41\x34\x5f\xa2\x38\x87\xa9\x20\xc3\x50\x99\x2e\x37\xf1\xf5\x4e\x0a\xfc\x30\xeb\xff\x87\x77\x44\xe4\xe7\x38\x3d\xf9\xb2\x1d\x7c\x6c\xe6\xbe\xce\x1e\x8e\x3c\x19\x5d\x94\xf4\xef\xe3\xed\x5e\x31\xe3\x29\x3b\xd9\x47\x55\x48\x58\x3c\xf5\xce\x61\x8a\xfc\x0d\x50\x27\x44\xfa\x68\x81\xac\x32\xb4\x93\x31\xe5\xda\xba\x11\xff\x38\x7d\x8b\x3c\xce\x75\xa5\xb2\x24\xd0\x1b\x2c\x55\xa7\x02\x84\x24\xff\x41\x0b\x19\x2a\x09\xdc\x0f\x41\x74\xb5\xea\x08\xb1\xd6\xbf\x9f\x53\xe6\x4b\xca\x7f\x26\x08\xc2\x32\x2f\x5c\xad\x1f\xeb\x16\x84\x14\x5f\x12\xaa\x58\x5d\x85\x40\x3f\xca\xb1\x0d\xb4\xd2\x8f\x06\x39\xad\x92\x4d\xb8\x2c\xdc\xb8\x5b\xf5\xb6\x11\x1d\xa6\x5e\x11\x90\x80\x68\x55\x4b\xda\x45\x56\xdf\xcb\x10\xb4\xdf\xf8\x32\xe0\x50\xe3\xc7\xf9\xd5\xd5\xe5\xd5\x75\x07\xdd\x2f\xf7\xff\x81\x9f\x0e\x8f\x06\x5e\xbe\xec\xb9\x7e\x8c\xd9\x3d\x68\xf7\x4a\xaf\x55\x42\x9e\xc2\xf0\x51\xa7\x59\x24\xaa\xb0\x6a\x0a\xad\x5c\x3d\x97\x40\x6c\x55\xfa\x8a\xc1\x73\xce\x72\x4f\xed\xc6\x3a\x2c\x60\x26\x55\x26\xd5\xc2\x82\x36\xb0\x90\x6e\x59\xcd\xa6\xa9\x2e\xea\x6a\x63\xff\x7d\x69\x4c\xbc\x33\x53\x83\xc2\x75\x91\xc9\x7d\x4e\xc0\x53\x76\xd4\x92\xbb\x5d\xb8\x41\x2a\xb6\x86\x9c\xd2\x20\x1a\xb3\xdd\x72\x99\xc2\x8f\xa5\x3a\xf3\x03\xf4\x61\x20\x9a\x69\x91\xe4\xcf\x4a\x2f\x49\xd9\xa3\x93\xf2\x07\x91\x34\x47\xa4\x70\x7a\xa5\xef\xbb\x08\x7a\xc3\x66\x8b\xcc\x85\x9f\xc6\x07\x92\x96\xc1\x7a\x89\xad\xc2\x9d\xf3\x6d\x4e\x61\xe8\x8f\xa1\xf6\x1e\x37\x75\x4a\x87\xfc\x5d\xe1\xb4\xe9\x4b\x57\xd5\x73\x38\xfb\xf1\x21\x0a\xf3\x8e\xf4\x31\xc0\x19\xc4\x19\x33\xbb\x89\xd2\xce\x1b\xbb\x0e\x84\xef\xda\x29\x60\xb6\xd5\x3c\x9b\xe2\x5d\xce\xc1\xb6\x3d\xea\x21\xa4\xec\xbd\x17\xd2\x16\xc2\xa5\x5d\xee\x3b\x31\x58\xab\x07\x2d\xc8\x18\x45\x16\xed\xa9\x54\xfb\xb5\x06\x3f\x1e\x68\xe0\x76\x29\x26\x93\x91\xf0\xb6\xb2\x79\xa3\x49\x45\x0b\xc8\x4e\x6a\xdb\x8f\x46\x36\xfa\x99\x08\xf1\x3f\xa9\x97\xc8\x65\x97\xd8\x2e\xfc\x28\xf7\x78\xf9\x2d\xa9\xb3\xc8\x84\x2b\x7c\x26\x5a\x0e\x36\x88\x71\xed\x94\x68\x17\xbe\x6e\x48\x6b\xfc\xc7\x31\x72\x8e\x24\x0e\x88\xfa\xea\x18\x82\xf6\xe4\xca\x47\xc1\x53\xf4\xcc\x82\xcf\xf2\x78\x51\xe2\x67\x87\xca\x46\xa2\xf1\x33\xdf\x61\xc4\xce\x97\xb0\x62\x93\x05\x76\xe5\x53\x9b\xa3\xbc\x40\xdf\xd6\x12\x6c\x6f\x93\xb9\x7f\x54\xa0\xa5\xfb\x4d\xa6\xad\xe3\x3b\x5a\xa6\x9e\xf4\xc4\x73\xcc\xa7\xa7\xc6\xd6\x41\xdf\x0e\xc3\xec\x17\x92\x18\x1b\x29\x0b\xb5\xa9\x75\x83\x8c\x48\x6b\xdb\x07\xe5\x1a\x72\xba\x35\x09\x83\x6c\x54\x26\x3f\x5e\x73\x7d\x62\x2b\x84\xd0\xb7\x57\x6f\x7d\xc6\xd1\xe4\xe1\x28\x7d\xd8\x89\xb1\xef\x7c\xaf\xd2\x18\x42\x0a\x91\xcf\xb5\x29\x3a\x25\xf7\x2e\x8e\xf7\x51\x30\x85\x1b\xb3\x01\xb1\x10\x52\x0d\x85\xf4\xc6\x24\xff\xb2\x5a\xd5\xc6\x36\x2d\xb2\x9e\x42\x32\xd7\x1a\xa4\x2a\x2b\x07\x99\x70\x02\xde\x05\x69\x3c\x4b\x8b\xec\x19\x99\xde\x7e\x4c\xa2\x94\x4d\x41\xc0\x2b\x8d\x36\x89\xc5\x7f\x57\xa8\x3a\x33\xf6\xbe\xbd\xf6\xf9\x75\x98\xb5\x7b\x58\x5a\xf6\xdd\xeb\xf3\x5e\xef\xc8\xd9\xfb\x0b\xbf\xa0\x94\x34\x3b\x15\xca\xbb\x22\x33\xf4\xce\x40\xbb\xdf\xad\x51\xb2\xe7\x91\xa4\x03\x30\xa7\xf0\x3e\x47\x61\x11\xaa\x32\x13\x6e\xaf\x59\xc5\x5f\x9e\x69\x5e\x65\xfb\x74\x0a\x0b\x02\xd6\x38\xdb\xc7\x30\xb8\x3b\x41\x4e\xfd\x0a\x7a\x76\xc0\x8e\x90\x68\xc2\xaa\x29\x5c\x38\x1f\x7d\x69\xb7\xe4\xbb\x78\xb7\x05\xa3\x3e\x78\x13\x2f\x1d\xad\x30\x54\x81\x0b\x82\x82\x9f\x4b\x4c\xc7\x9c\xa4\x40\x6b\xdc\xe2\x68\x1f\xc8\x30\x26\x84\xf5\x0b\xa9\x67\xc2\x1b\x23\x41\x60\x75\xe5\xda\xc6\x62\x0a\xbf\x36\x46\x38\x9a\x0a\x5a\x36\xa9\xcd\x89\xb4\x8d\xb3\x30\x70\xad\x05\x76\xa2\x98\x12\x8a\x56\x1c\x26\x99\x34\xa3\x8c\xdc\x41\xb6\x88\x8f\x5a\xee\xa5\x96\xca\xbb\x54\x3e\x44\x73\xd8\x6a\x72\x6e\x8e\xf3\x84\x42\xc0\xc8\x15\x37\x19\xef\x59\xb8\x7e\x36\x52\x41\x01\xbb\x58\x61\x92\xe9\xf4\x1e\xbb\x9e\x02\xbc\x12\x8a\xa1\x8a\x15\xc2\x6b\x9e\x08\xb2\x60\x07\x7c\xc0\xb1\x94\x39\x26\x22\x37\x28\xb2\x4d\x82\x9f\xa5\xed\x6c\xb5\x78\x43\x27\x24\xcc\x04\x3f\x73\x00\x76\x16\x5b\x05\x9b\xa8\x44\xa2\xf5\x0a\x65\xc9\x73\xca\xc5\x0c\xbb\x8a\x23\x97\x0a\x81\xf4\x30\xc7\xfd\xb0\xbf\xf9\x33\x6e\x89\x5b\x6b\xa8\x91\x71\xd1\xc4\xcb\x9a\x66\xc7\xbf\xbc\x61\x5d\x4a\x0b\xf7\x52\x65\x74\x40\x82\x2e\x86\x1a\xe9\xa3\x8b\x67\xcf\x52\x90\x7d\x69\x11\xc2\xa4\x1f\x20\x27\x3c\x08\x78\x64\x57\x58\x59\xb8\xbe\x4f\xbe\x5b\x24\x0a\x62\x58\x83\xcc\x83\xc5\x52\x18\xfa\x83\xa1\xfb\x7e\xb3\x0e\xde\xc6\x29\x7f\x38\x64\x09\xb1\x7c\xac\x9e\x2b\xed\x25\x65\xd1\x1d\x87\xec\x58\x5b\x11\x90\xb5\xce\xfb\x00\xbe\x68\x7d\x93\xa5\x58\x91\xa5\x62\x5d\xf2\x89\x74\x1b\x88\xe9\x7a\xac\xd2\xbe\x86\x22\x98\x60\xaf\xa2\x6a\xc7\x1e\x09\xb2\xf9\x2a\x1a\x23\x1f\xe8\xb3\x2b\x46\xfb\x17\xa2\xdb\x69\x7c\x3d\x12\x5a\x7c\x3d\x3c\xcb\x17\x15\x29\x13\x3f\x71\xe0\x05\xec\xb1\x4b\x05\x22\xea\x74\x84\x30\x70\xf8\xb5\x9a\xe7\x32\x25\x2b\x93\x84\xc0\x8d\x38\x34\xda\xda\x98\x09\xe9\x3a\xae\xad\xf3\x13\x43\x3e\x62\x3a\x7c\x0e\x3c\x47\x5e\xd9\xf9\x2d\xaa\xdc\xc9\x32\xf7\x51\xa3\x3f\x3c\xf4\x29\x78\x24\x1e\x39\x9b\xaf\x78\xf7\xee\xa5\x41\x5c\xbb\xa8\x3c\x01\xe9\xfc\x89\x2a\xb5\xb5\x72\xe6\x4f\x01\x0b\x24\x32\xe2\xb1\x36\xe2\x99\x91\x5f\x52\x6b\x3a\x13\xf1\xe8\x10\x06\x4e\x18\xcd\xa3\xa0\xe7\x08\x61\x9a\x2a\xc7\x27\x48\x92\x96\x85\xe8\x22\xc7\x43\x32\x6c\xe8\x8f\xf6\x7e\xcf\x91\xf0\x6f\x50\x6a\x11\xec\x6e\xc9\xd4\x3f\x3d\xfa\x1a\x42\x66\x06\x0f\x49\x58\x58\xab\x53\xc9\xa0\x0f\x53\xfc\x3c\x12\xb7\x2f\x7c\x66\xfe\x49\x92\x17\xa6\x69\xf1\xe0\x62\x76\x67\x6b\x7b\x28\x90\x41\x2e\x15\x82\x30\x8b\x8a\x83\x62\x12\xa1\x59\x6c\xb7\x6d\x7f\x91\xe1\x4c\xa0\xf4\x24\xc6\x57\x1f\x24\x0f\x1e\x39\x82\xa2\x7b\xdc\x7c\x35\xaa\xee\x71\xf3\x9c\x61\x41\x29\xa4\x79\x44\xde\xee\x30\xdb\x77\xfc\x2c\x8a\x92\x9c\xdd\x1a\xdc\x3d\x6e\x46\xf1\x10\x1c\xac\xe1\x4e\xa4\x2e\x06\xbe\x8b\x28\xbf\x67\x1b\x1c\xe0\xf9\x36\x25\x7f\x71\xd5\xa9\x90\x89\x4f\x48\xb6\xc2\xcb\xa8\x1c\xf5\x7b\x1b\xf0\xab\x39\xc8\x68\x40\x0c\xe5\x1e\xf0\xdf\x95\x34\x9c\xdb\x2a\x2b\x67\x47\x69\xc9\x55\x58\xe3\x43\x19\x7f\x5a\x76\xb4\xc2\x02\xae\x50\x81\x98\x3b\x34\x20\xca\x32\xe7\xfa\x09\x37\x36\x94\xda\xc3\x09\xb5\x54\x54\xab\x29\xac\x84\x91\x62\x96\x63\xa3\xf0\x16\x5d\x0d\x71\x77\x4a\x3c\xc0\x3e\x8a\x6a\xda\xb8\x0e\xbd\xb6\xf1\x2f\x9b\x4c\x78\x7f\xc4\x9b\x3d\xd7\x79\xae\xd7\x9e\x1a\xa2\x9d\xe5\xe9\x3f\x6e\xb7\xc3\xd1\xd7\xc2\x37\xa8\x24\x14\xf4\x70\xc5\x78\x28\xb0\x68\x35\xb5\xd0\x9a\x26\xc1\x25\x4a\x49\x5f\xc4\x1c\xd3\x01\x77\x9d\xa7\xd6\x1d\x6b\xf1\x01\xc1\xbe\x97\x14\x42\x0e\x83\x84\x74\x15\x10\xd4\x99\xe2\x3d\x18\x43\x67\xb2\x15\x5f\xae\x71\xd6\x7f\x93\x77\x45\x1d\x44\x5d\x3b\x54\x1b\x15\x44\xc6\x17\x31\xcd\xb2\xe1\x60\x69\x8f\xd8\x81\x30\xb8\xcf\xf1\x68\x48\x8e\x03\x47\x13\x3d\x3a\x1e\x8d\x41\x5d\x29\x8c\x45\xd3\xfb\xb8\xb8\xc9\x42\x19\x74\x46\x22\x5f\x2a\x21\xf9\x54\x5b\x81\x7e\x6c\xcd\x2e\xc6\x83\xee\x7b\xdd\x63\x47\x56\x9f\xee\xde\x2a\x11\xee\x33\x8b\x69\x65\xbc\x03\xde\x6c\xd0\x7f\xc0\x41\x0d\x38\xa3\x28\x48\xd4\x03\x21\x8d\xdc\xb6\x6e\xde\xfc\xd2\x20\x7f\xea\x4e\x8f\xfe\x7a\x76\xf5\xf3\xc5\xcf\x3f\x8c\x2f\xd9\xc4\x05\xc7\x15\x6d\xd6\xc2\xa8\xba\x2f\x84\x24\xdd\x95\xbd\xb9\xa2\x31\xda\xf2\x0f\xb1\x21\xe4\x2e\x98\x38\xde\xc5\x53\x9f\x45\xa3\x5d\xb9\xeb\xd3\x82\x80\x8f\xdb\xe4\x8e\xce\x9b\xb5\xdb\xfb\x5b\x79\x72\xc8\xd0\x0d\xe7\x18\x18\x33\x5d\xb6\x19\x96\x06\x53\x52\xe2\xc4\x60\x99\x8b\xb4\x33\x08\xbf\x59\x7a\x3c\x3a\xcf\xc2\x56\x72\x7b\xa4\x8f\xb1\x76\x1b\x61\xf8\xcd\xb2\xd5\x5a\xd1\x19\x69\x30\xd4\x57\x70\x65\xbd\x0a\x71\x29\x13\xd7\x3b\xe0\xac\x43\x31\x92\xf6\x20\x89\xa7\x14\x33\xec\x52\x57\x79\x46\xe4\x51\x48\x05\xb7\xd6\x57\xf5\x7d\xc9\xf1\x80\x5a\xf6\xe7\xfe\x6a\x8a\x78\xfe\xc0\x56\x12\x5d\x1e\x03\xdd\x42\x8f\x8b\x2c\x64\x82\xbc\xf9\x3f\x02\x25\x67\x51\xc4\xaa\x77\xf3\x86\x90\xf2\xfa\xb8\xa1\xb1\x7c\x1c\x1f\x71\xb6\x5f\x6f\x0e\x13\x96\xcb\x42\xba\x44\x2e\x94\x36\x9d\x24\x45\x95\x0e\x51\x1d\x2f\xf1\x59\x02\xfa\xb4\x5f\x48\xa1\x5b\xd1\x83\x1b\x8b\x3d\x5d\x0a\xb5\x40\x32\x5c\xfd\xd7\xd6\xdb\x1a\x71\x5d\xc0\xb1\x91\xfd\x7c\xe3\x1b\x08\x6a\x50\x53\xb8\x20\x2a\xa4\x5a\x8c\x51\x09\x26\xc4\x26\xb9\x5e\x24\x56\xfe\x3e\x40\x07\x4f\x3e\x85\x5c\x2f\xae\xe5\xef\xa4\xba\x7c\xc3\xe8\xca\x59\x99\xc5\x94\x87\xd7\x4f\x43\xd4\xd0\x8e\x7c\x78\x31\x81\xbf\xbe\xb8\x83\x77\xff\xa8\xdd\xa5\x15\x1a\xf2\x00\xb9\x0c\x5e\xfa\x77\xcc\xa6\x71\x02\xf8\xf9\xbe\xf7\x67\xc7\x12\x5f\x60\xa1\xcd\x66\x3c\xfd\x7e\xfe\x78\x16\xfe\xfa\xb7\xbf\x4f\xe0\x6f\x2f\xfe\xdf\xdf\xff\x58\x36\xe8\xae\xd4\x55\xe7\x1b\xea\x1d\x16\xc2\xdc\x91\xf4\xbf\x78\x31\x81\xff\xff\x82\xfe\xdd\x41\x21\xf3\x5c\x5a\x4c\xb5\x6a\xc5\xcb\x5f\x8f\x17\x2e\xf6\x27\xa5\xd1\x25\x1a\x27\x3b\x83\xe4\x68\xa9\x5b\x76\xd5\xb7\x88\x78\xd7\x21\x34\x89\xf8\xce\x81\x06\x58\x6c\x26\x39\x6c\xbb\xa3\xe9\xce\x34\x9f\x08\xb2\xe0\xd2\xd5\xa2\xd1\x73\xb8\x31\x62\x25\x2d\xcc\x2a\x99\x67\xfd\x9d\x06\xcc\x8a\x37\x5b\x2c\xc6\x51\x26\xab\x3e\x9e\x3b\x86\x4b\xed\x5d\x3c\xc1\xac\x73\xff\x04\x45\xf3\xfe\xdb\xf8\x04\xfc\xe1\x61\x5a\x48\x15\xaa\xe9\xf4\x87\x48\x07\x6a\x73\x4c\x6a\xf4\xd3\xbc\x15\xe8\x32\x67\xb1\xde\x19\x66\x91\xb3\xb4\x57\xfa\x3c\x50\x1e\xe9\xac\x6e\x3e\xa9\xa4\xc9\xd4\x86\x86\x09\x4e\xc1\xf5\xe6\x90\x1f\xd5\xc2\x77\x6c\xe0\x5e\x72\xb9\x89\xc6\x72\x7e\x98\xaa\xb4\x5b\x86\xdc\xcf\x30\x49\x31\xa7\x33\xd8\x0e\x70\xf3\x28\x5b\xdb\x76\x6c\xc2\xeb\x1d\xcc\x40\xe9\x71\x3d\x2d\x8c\xbd\xd5\x4e\xc6\x42\x19\x43\xc4\xc1\x66\xab\x70\x33\xee\x47\x95\xeb\x50\x73\xf5\x9d\x0b\x87\x72\xce\x23\x24\xd4\x7a\x83\x97\xe8\x15\x1a\x23\xb3\x0c\xbb\xe2\x2d\xa2\xb0\xfd\x24\xaf\x69\x07\x6c\x96\x46\x9f\xa6\xdd\xed\x35\x76\xa3\x12\x69\x93\xb2\x9a\xe5\xb2\xeb\x67\x0f\xfc\xae\xf0\xdc\x58\x39\xf4\xaf\x0e\x29\x56\xe5\x85\x8f\xb2\x52\x13\x32\x17\x6c\x5b\x66\x08\x2b\xe9\x13\x64\x74\x0e\x29\x9c\x9a\x61\x78\xe7\x81\x19\xcc\x36\x20\xd4\x46\xab\x9e\x57\x7c\x4c\x6b\x4c\x74\xe3\x2c\xbc\xad\x1e\x70\x37\x1e\xe7\xb9\xb9\x84\xc7\x51\x8c\xca\xe8\xff\x93\xf0\x0c\x7a\xbf\x86\x47\x07\x81\x7f\xc7\x06\x67\x13\xef\x84\x84\xbf\xc2\x82\x9e\xc0\xcb\x53\xfa\x67\x8a\xa5\xe1\x95\x56\x2b\x32\xf8\x21\x78\x69\x90\x38\x3d\x3e\xea\x3e\xc8\xd7\x9f\x24\xec\xde\xe7\xb0\x8d\xaa\xe6\x71\x54\x90\x5e\x73\x19\xb3\x7b\x06\x6d\xa9\x95\xc5\xbe\x36\xbe\x3d\xb2\x39\xaf\xbb\x9f\xbf\x09\xe3\x31\x53\xd3\xca\xfc\xc4\x1c\x5c\x9d\x3b\x5e\x3a\x57\xfa\xdf\xbb\xf2\xa8\xf9\x6e\x9b\xc2\x2b\xba\x65\xb8\xef\xa7\xfd\xbd\xbf\xd8\xf9\xda\x09\x5f\x07\xa6\x19\x0a\xdd\x29\x0d\x65\x43\x5a\x1b\x77\x16\xd5\x4a\x1a\xad\xd8\x7e\xc6\xd4\x5b\x57\x47\x45\x88\x4c\xcf\x9b\x25\xf0\x4b\x58\x32\x26\xca\x7f\x7d\xfe\x8f\xdb\x1f\x46\x87\xf8\x3c\xfb\xb8\xf8\x3e\x9b\x2d\x12\x8b\xc2\xa4\x4b\xe2\x2c\x1a\xdd\xba\x50\xdc\xa9\xb8\x61\x45\x6d\x74\x77\x4b\xcb\x71\xfb\xa2\x7c\xbd\x73\x32\x10\x1f\x10\x29\xfb\x37\xd3\xd7\xbe\x95\x9e\x78\x23\x11\x69\xf5\x95\xed\x5b\x95\x7b\x7e\x7e\xe8\xf5\x81\x7e\xb9\x20\x91\x53\x78\xc3\x14\x34\xbf\x76\xc3\x65\x13\x02\x76\x2c\x01\xfd\xef\xb5\x8f\xa7\xa1\xdd\x0d\x1d\xbb\xf7\x8f\x7b\x83\xbb\xf7\xa6\xb1\xef\x29\x29\x4d\x7e\xf4\x90\xf1\xf8\xd7\xb2\x21\x76\xa8\xdb\xaf\xbf\x3a\x11\x13\x76\xeb\x9f\x3d\x3c\x4c\xb3\xaa\x28\x36\x3c\x6b\xbb\x7d\x46\xe6\xa7\x1d\xfb\x68\xd5\xaf\x3f\xe1\xbd\x78\xf2\xbb\x2c\x13\xfc\xcc\x2d\x3c\xbe\xb5\xa1\xe7\x5d\xd5\x39\xcf\xa3\x33\xf6\x5e\xb8\xe5\x69\x7b\x07\xc7\xa2\x12\x59\x16\x1f\x72\xf5\x61\x3a\xe3\x69\x3b\x07\xd7\x69\xf8\x1f\x59\xc2\x9b\xa1\x83\xd1\xc6\x16\x7a\x93\x62\xab\x5e\x0f\xc2\x37\xa1\xd9\xf2\xda\x3b\xfa\x4f\xe6\xef\x00\xc6\x24\x43\xeb\xa4\x62\x54\x5f\x42\x02\x7b\x40\xaf\x1b\x58\xad\x19\x2d\x0c\x23\x69\x8d\x97\x65\xa4\x17\x55\x77\x1e\x35\x26\x53\xe0\x22\xb4\x7a\x9d\xd3\x64\x52\x38\xe9\x5a\x85\x10\xa6\x24\xc0\xe3\xd2\x6c\x9c\xce\xb0\xd9\x35\x40\xc9\x01\x09\xdf\x99\x1f\x3c\x9f\x77\x40\x06\xd9\x7f\x9e\xb4\xd9\xeb\x3f\xea\x91\x8f\xd8\xe2\xce\xc2\xef\xa9\xe8\xbd\x8a\xad\xf0\x24\xe1\xa8\x47\x47\xef\x70\x2e\xad\x4b\xf4\x9c\x11\xd9\x84\xdb\x60\xf9\x8e\x12\xce\xa1\xe9\x3a\xd8\xde\xb4\x71\x73\x79\x5d\xcc\xf2\x3f\x14\xe6\x9b\x08\x02\x94\xb8\xef\xdc\x35\xf4\xde\xfb\x22\x0c\xb6\x57\x0e\xc1\xc1\xde\xfd\xf9\x82\xae\x43\xb5\xfb\x1b\x07\x74\x13\x76\xb6\x97\x70\xa0\xd2\xf6\x06\x82\x1b\x47\x6c\x5c\x9d\xff\xf7\xed\xc5\xd5\x79\xf2\xeb\x8f\x17\xd7\x3f\x25\x67\xb7\x37\x3f\xb6\xaa\x08\x91\xda\x6f\xee\xbe\xf9\xdf\x00\x00\x00\xff\xff\x40\xdb\x8b\x92\xa5\x55\x00\x00") func wski18nResourcesEn_usAllJsonBytes() ([]byte, error) { return bindataRead( @@ -112,7 +112,7 @@ func wski18nResourcesEn_usAllJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "wski18n/resources/en_US.all.json", size: 21565, mode: os.FileMode(420), modTime: time.Unix(1579715078, 0)} + info := bindataFileInfo{name: "wski18n/resources/en_US.all.json", size: 21925, mode: os.FileMode(420), modTime: time.Unix(1580408211, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/wski18n/resources/en_US.all.json b/wski18n/resources/en_US.all.json index bc0aba499..27dab3ab5 100644 --- a/wski18n/resources/en_US.all.json +++ b/wski18n/resources/en_US.all.json @@ -387,6 +387,14 @@ "id": "msg_err_api_gateway_base_path_invalid", "translation": "API Gateway base path [{{.apibasepath}}] is invalid. It has path parameters which is not supported, only relative path supports path parameters." }, + { + "id": "msg_err_api_missing_web_action", + "translation": "Action [{{.action}}] is not a web action, API [{{.api}}] can only be created using a web action.\n" + }, + { + "id": "msg_err_api_missing_web_sequence", + "translation": "Sequence [{{.sequence}}] is not a web sequence, API [{{.api}}] can only be created using a web sequence.\n" + }, { "id": "msg_err_runtime_parser_error", "translation": "Failed to retrieve runtimes {{.err}}." @@ -481,11 +489,11 @@ }, { "id": "msg_warn_api_missing_web_action", - "translation": "Action [{{.action}}] is not a web action, API [{{.api}}] can only be created using web action. Converting [{{.action}}] to a web action.\n" + "translation": "Action [{{.action}}] is not a web action, API [{{.api}}] can only be created using a web action. Converting [{{.action}}] to a web action.\n" }, { "id": "msg_warn_api_missing_web_sequence", - "translation": "Sequence [{{.sequence}}] is not a web sequence, API [{{.api}}] can only be created using web sequence. Converting [{{.sequence}}] to a web sequence.\n" + "translation": "Sequence [{{.sequence}}] is not a web sequence, API [{{.api}}] can only be created using a web sequence. Converting [{{.sequence}}] to a web sequence.\n" }, { "id": "msg_warn_api_invalid_response_type", From 4c3104723feaec026615d2493f34e4ba09106419 Mon Sep 17 00:00:00 2001 From: Matt Rutkowski Date: Thu, 30 Jan 2020 20:42:58 -0600 Subject: [PATCH 3/8] Fix non-working code that attempted to add web annotations --- deployers/manifestreader.go | 2 +- parsers/manifest_parser.go | 60 +++++++++---------- ...b_annotation_on_api_action_or_sequence.yml | 9 ++- utils/misc.go | 12 ++++ webaction/webaction.go | 15 ++--- 5 files changed, 54 insertions(+), 44 deletions(-) diff --git a/deployers/manifestreader.go b/deployers/manifestreader.go index b039a3002..63fe49d2a 100644 --- a/deployers/manifestreader.go +++ b/deployers/manifestreader.go @@ -108,7 +108,7 @@ func (reader *ManifestReader) HandleYaml(manifestParser *parsers.YAMLParser, man return wskderrors.NewYAMLFileFormatError(manifestName, err) } - apis, responses, err := manifestParser.ComposeApiRecordsFromAllPackages(reader.serviceDeployer.ClientConfig, manifest) + apis, responses, err := manifestParser.ComposeApiRecordsFromAllPackages(reader.serviceDeployer.ClientConfig, manifest, actions, sequences) if err != nil { return wskderrors.NewYAMLFileFormatError(manifestName, err) } diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go index c4cc1dec2..a61ab18e3 100644 --- a/parsers/manifest_parser.go +++ b/parsers/manifest_parser.go @@ -21,6 +21,7 @@ import ( "encoding/base64" "encoding/json" "errors" + "fmt" "io/ioutil" "os" "path" @@ -1131,7 +1132,9 @@ func (dm *YAMLParser) ComposeRules(pkg Package, packageName string, managedAnnot return rules, nil } -func (dm *YAMLParser) ComposeApiRecordsFromAllPackages(client *whisk.Config, manifest *YAML) ([]*whisk.ApiCreateRequest, map[string]*whisk.ApiCreateRequestOptions, error) { +func (dm *YAMLParser) ComposeApiRecordsFromAllPackages(client *whisk.Config, manifest *YAML, + actionrecords []utils.ActionRecord, + sequencerecords []utils.ActionRecord) ([]*whisk.ApiCreateRequest, map[string]*whisk.ApiCreateRequestOptions, error) { var requests = make([]*whisk.ApiCreateRequest, 0) var responses = make(map[string]*whisk.ApiCreateRequestOptions, 0) manifestPackages := make(map[string]Package) @@ -1143,7 +1146,8 @@ func (dm *YAMLParser) ComposeApiRecordsFromAllPackages(client *whisk.Config, man } for packageName, p := range manifestPackages { - r, response, err := dm.ComposeApiRecords(client, packageName, p, manifest.Filepath) + r, response, err := dm.ComposeApiRecords(client, packageName, p, manifest.Filepath, + actionrecords, sequencerecords) if err == nil { requests = append(requests, r...) for k, v := range response { @@ -1181,7 +1185,8 @@ func (dm *YAMLParser) ComposeApiRecordsFromAllPackages(client *whisk.Config, man * } * } */ -func (dm *YAMLParser) ComposeApiRecords(client *whisk.Config, packageName string, pkg Package, manifestPath string) ([]*whisk.ApiCreateRequest, map[string]*whisk.ApiCreateRequestOptions, error) { +func (dm *YAMLParser) ComposeApiRecords(client *whisk.Config, packageName string, pkg Package, manifestPath string, + actionrecords []utils.ActionRecord, sequencerecords []utils.ActionRecord) ([]*whisk.ApiCreateRequest, map[string]*whisk.ApiCreateRequestOptions, error) { var requests = make([]*whisk.ApiCreateRequest, 0) // supply a dummy API GW token as it is optional @@ -1217,45 +1222,40 @@ func (dm *YAMLParser) ComposeApiRecords(client *whisk.Config, packageName string gatewayRelPath = PATH_SEPARATOR + gatewayRelPath } for actionName, gatewayMethodResponse := range gatewayRelPathMap { - // verify that the action is defined under actions sections + // verify that the action is defined under action records if _, ok := pkg.Actions[actionName]; ok { - // verify that the action is defined as web action - // web or web-export set to any of [true, yes, raw] - a := pkg.Actions[actionName] - if !webaction.IsWebAction(a.GetWeb()) { + // verify that the action is defined as web action; + // web or web-export set to any of [true, yes, raw]; if not, + // we will try to add it (if no strict" flag) and warn user that we did so + a1 := utils.GetActionFromActionRecords(actionrecords,packageName,actionName) + + if a1.Annotations.FindKeyValue(webaction.WEB_EXPORT_ANNOT) == -1 { if !utils.Flags.Strict { webaction.WarnWebAnnotationMissingFromActionOrSequence(apiName,actionName,false) - if a.Annotations == nil { - a.Annotations = make(map[string]interface{}, 0) - } - a.Annotations[webaction.WEB_EXPORT_ANNOT] = true - //a.Annotations = webaction.CreateWebAnnotationsAsMap() - pkg.Actions[actionName] = a + a1.Annotations = webaction.AddWebAnnotations(a1.Annotations) + fmt.Printf("%v\n",a1.Annotations) } else { err := wskderrors.NewInvalidWebActionError(apiName,actionName,false) return requests, requestOptions, err } } - // verify that the sequence is defined under sequences sections + // verify that the sequence action is defined under sequences records } else if _, ok := pkg.Sequences[actionName]; ok { - // verify that the sequence is defined as web sequence - // web set to any of [true, yes, raw] - a := pkg.Sequences[actionName] - if !webaction.IsWebSequence(a.Web) { - if !utils.Flags.Strict { - webaction.WarnWebAnnotationMissingFromActionOrSequence(apiName,actionName,true) - if a.Annotations == nil { - a.Annotations = make(map[string]interface{}, 0) - } - a.Annotations[webaction.WEB_EXPORT_ANNOT] = true - //a.Annotations = webaction.CreateWebAnnotationsAsMap() - pkg.Sequences[actionName] = a + // verify that the sequence action is defined as web sequence + // web or web-export set to any of [true, yes, raw]; if not, + // we will try to add it (if no strict" flag) and warn user that we did so + a1 := utils.GetActionFromActionRecords(sequencerecords,packageName,actionName) + + if a1.Annotations.FindKeyValue(webaction.WEB_EXPORT_ANNOT) == -1 { + if !utils.Flags.Strict { + webaction.WarnWebAnnotationMissingFromActionOrSequence(apiName,actionName,false) + a1.Annotations = webaction.AddWebAnnotations(a1.Annotations) + fmt.Printf("%v\n",a1.Annotations) } else { - err := wskderrors.NewInvalidWebActionError(apiName,actionName,true) + err := wskderrors.NewInvalidWebActionError(apiName,actionName,false) return requests, requestOptions, err } } - // return failure since action or sequence are not defined in the manifest } else { return nil, nil, wskderrors.NewYAMLFileFormatError(manifestPath, wski18n.T(wski18n.ID_ERR_API_MISSING_ACTION_OR_SEQUENCE_X_action_or_sequence_X_api_X, @@ -1279,7 +1279,7 @@ func (dm *YAMLParser) ComposeApiRecords(client *whisk.Config, packageName string gatewayMethodResponse.Response = utils.HTTP_FILE_EXTENSION } - // Chekc if API verb is valid, it must be one of (GET, PUT, POST, DELETE) + // Check if API verb is valid, it must be one of (GET, PUT, POST, DELETE) if _, ok := whisk.ApiVerbs[strings.ToUpper(gatewayMethodResponse.Method)]; !ok { return nil, nil, wskderrors.NewInvalidAPIGatewayMethodError(manifestPath, gatewayBasePath+gatewayRelPath, diff --git a/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml b/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml index d61d0e2a8..9838dcaf2 100644 --- a/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml +++ b/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml @@ -15,7 +15,10 @@ # limitations under the License. # -# TODO: test for value conflicts when both `web` and `web-export` supplied +# Test automatically adding web annotations on Actions that do not have them declared +# when these actions are used to create APIs. +# 1) Test an Action missing web-export, but with existing Annotations declared +# 2) Test a Sequence missing web-export with NO existing Annotations (to verify we allocate) packages: TestMissingWebAnnotation: actions: @@ -23,6 +26,8 @@ packages: function: src/hello_http_validate.js hello: function: src/hello.js + annotations: + foo: bar hello_wrap: function: src/hello_http_wrap.js sequences: @@ -31,7 +36,7 @@ packages: apis: actionmissingweb: helloworlds: - hellosequence: + helloweb: hello: method: GET response: http diff --git a/utils/misc.go b/utils/misc.go index 65193fcf3..f0f7eb066 100644 --- a/utils/misc.go +++ b/utils/misc.go @@ -187,3 +187,15 @@ func getKeyValueFormattedJSON(data map[string]interface{}) whisk.KeyValueArr { } return keyValueArr } + +func GetActionFromActionRecords( records []ActionRecord, packageName string, actionName string ) *whisk.Action { + + for _, record := range records { + if record.Packagename == packageName { + if record.Action.Name == actionName { + return record.Action + } + } + } + return nil +} diff --git a/webaction/webaction.go b/webaction/webaction.go index a6c25ddd4..778d12d6c 100644 --- a/webaction/webaction.go +++ b/webaction/webaction.go @@ -92,6 +92,10 @@ func webActionAnnotations( fetchAnnotations bool, annotations whisk.KeyValueArr, return annotations, nil } +func AddWebAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr { + return addWebAnnotations(annotations) +} + func addWebAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr { annotations = deleteWebAnnotationKeys(annotations) annotations = addKeyValue(WEB_EXPORT_ANNOT, true, annotations) @@ -153,17 +157,6 @@ func HasAnnotation(annotations *whisk.KeyValueArr, key string) bool { return (annotations.FindKeyValue(key) >= 0) } -func CreateWebAnnotationsAsMap(annotations map[string]interface{}) map[string]interface{} { - if annotations == nil { - annotations = make(map[string]interface{}, 3) - } - - annotations[WEB_EXPORT_ANNOT] = true - annotations[RAW_HTTP_ANNOT] = false - annotations[FINAL_ANNOT] = true - return annotations -} - func WarnWebAnnotationMissingFromActionOrSequence(apiName string, actionName string, isSequence bool){ nameKey := wski18n.KEY_ACTION i18nWarningID := wski18n.ID_WARN_API_MISSING_WEB_ACTION_X_action_X_api_X From b757480d4bcfe28e38e476bf518a424b0c7d7f5e Mon Sep 17 00:00:00 2001 From: Matt Rutkowski Date: Fri, 31 Jan 2020 08:57:29 -0600 Subject: [PATCH 4/8] refine procedural logic for reuse --- parsers/manifest_parser.go | 29 ++++--------------- ...b_annotation_on_api_action_or_sequence.yml | 1 + utils/misc.go | 1 - webaction/webaction.go | 28 +++++++++--------- 4 files changed, 20 insertions(+), 39 deletions(-) diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go index a61ab18e3..5591e9359 100644 --- a/parsers/manifest_parser.go +++ b/parsers/manifest_parser.go @@ -21,7 +21,6 @@ import ( "encoding/base64" "encoding/json" "errors" - "fmt" "io/ioutil" "os" "path" @@ -1227,34 +1226,18 @@ func (dm *YAMLParser) ComposeApiRecords(client *whisk.Config, packageName string // verify that the action is defined as web action; // web or web-export set to any of [true, yes, raw]; if not, // we will try to add it (if no strict" flag) and warn user that we did so - a1 := utils.GetActionFromActionRecords(actionrecords,packageName,actionName) - - if a1.Annotations.FindKeyValue(webaction.WEB_EXPORT_ANNOT) == -1 { - if !utils.Flags.Strict { - webaction.WarnWebAnnotationMissingFromActionOrSequence(apiName,actionName,false) - a1.Annotations = webaction.AddWebAnnotations(a1.Annotations) - fmt.Printf("%v\n",a1.Annotations) - } else { - err := wskderrors.NewInvalidWebActionError(apiName,actionName,false) - return requests, requestOptions, err - } + if err := webaction.TryUpdateAPIsActionToWebAction(actionrecords, packageName, + apiName, actionName, false); err!=nil { + return requests, requestOptions, err } // verify that the sequence action is defined under sequences records } else if _, ok := pkg.Sequences[actionName]; ok { // verify that the sequence action is defined as web sequence // web or web-export set to any of [true, yes, raw]; if not, // we will try to add it (if no strict" flag) and warn user that we did so - a1 := utils.GetActionFromActionRecords(sequencerecords,packageName,actionName) - - if a1.Annotations.FindKeyValue(webaction.WEB_EXPORT_ANNOT) == -1 { - if !utils.Flags.Strict { - webaction.WarnWebAnnotationMissingFromActionOrSequence(apiName,actionName,false) - a1.Annotations = webaction.AddWebAnnotations(a1.Annotations) - fmt.Printf("%v\n",a1.Annotations) - } else { - err := wskderrors.NewInvalidWebActionError(apiName,actionName,false) - return requests, requestOptions, err - } + if err := webaction.TryUpdateAPIsActionToWebAction(sequencerecords, packageName, + apiName, actionName, true); err!=nil { + return requests, requestOptions, err } } else { return nil, nil, wskderrors.NewYAMLFileFormatError(manifestPath, diff --git a/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml b/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml index 9838dcaf2..ffa8e8e60 100644 --- a/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml +++ b/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml @@ -15,6 +15,7 @@ # limitations under the License. # +# TODO automate this testcase # Test automatically adding web annotations on Actions that do not have them declared # when these actions are used to create APIs. # 1) Test an Action missing web-export, but with existing Annotations declared diff --git a/utils/misc.go b/utils/misc.go index f0f7eb066..30fb601c5 100644 --- a/utils/misc.go +++ b/utils/misc.go @@ -189,7 +189,6 @@ func getKeyValueFormattedJSON(data map[string]interface{}) whisk.KeyValueArr { } func GetActionFromActionRecords( records []ActionRecord, packageName string, actionName string ) *whisk.Action { - for _, record := range records { if record.Packagename == packageName { if record.Action.Name == actionName { diff --git a/webaction/webaction.go b/webaction/webaction.go index 778d12d6c..c8642a812 100644 --- a/webaction/webaction.go +++ b/webaction/webaction.go @@ -92,10 +92,6 @@ func webActionAnnotations( fetchAnnotations bool, annotations whisk.KeyValueArr, return annotations, nil } -func AddWebAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr { - return addWebAnnotations(annotations) -} - func addWebAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr { annotations = deleteWebAnnotationKeys(annotations) annotations = addKeyValue(WEB_EXPORT_ANNOT, true, annotations) @@ -157,7 +153,7 @@ func HasAnnotation(annotations *whisk.KeyValueArr, key string) bool { return (annotations.FindKeyValue(key) >= 0) } -func WarnWebAnnotationMissingFromActionOrSequence(apiName string, actionName string, isSequence bool){ +func warnWebAnnotationMissingFromActionOrSequence(apiName string, actionName string, isSequence bool){ nameKey := wski18n.KEY_ACTION i18nWarningID := wski18n.ID_WARN_API_MISSING_WEB_ACTION_X_action_X_api_X @@ -173,18 +169,20 @@ func WarnWebAnnotationMissingFromActionOrSequence(apiName string, actionName str wskprint.PrintOpenWhiskWarning(warningString) } -func ErrorWebAnnotationMissingFromActionOrSequence(apiName string, actionName string, isSequence bool){ - i18nErrorID := wski18n.ID_ERR_API_MISSING_WEB_ACTION_X_action_X_api_X +func TryUpdateAPIsActionToWebAction(records []utils.ActionRecord, pkgName string, apiName string, actionName string, isSequence bool) error { + a1 := utils.GetActionFromActionRecords(records,pkgName,actionName) - if isSequence { - i18nErrorID = wski18n.ID_ERR_API_MISSING_WEB_SEQUENCE_X_sequence_X_api_X - } + if a1.Annotations.FindKeyValue(WEB_EXPORT_ANNOT) == -1 { + if !utils.Flags.Strict { + warnWebAnnotationMissingFromActionOrSequence(apiName,actionName,isSequence) + a1.Annotations = addWebAnnotations(a1.Annotations) + fmt.Printf("Web Annotations to Action; result: %v\n",a1.Annotations) + } else { + return wskderrors.NewInvalidWebActionError(apiName,actionName,isSequence) + } + } // NOOP - errString := wski18n.T(i18nErrorID, - map[string]interface{}{ - wski18n.KEY_SEQUENCE: actionName, - wski18n.KEY_API: apiName}) - wskprint.PrintOpenWhiskWarning(errString) + return nil } func ValidateRequireWhiskAuthAnnotationValue(actionName string, value interface{}) (string, error) { From 1c65aa55c9d8b3a3fbb96209383980e10531e3f4 Mon Sep 17 00:00:00 2001 From: Matt Rutkowski Date: Fri, 31 Jan 2020 09:39:26 -0600 Subject: [PATCH 5/8] refine procedural logic for reuse --- parsers/manifest_parser.go | 2 +- ...issing_web_annotation_on_api_action_or_sequence.yml | 7 +++++++ webaction/webaction.go | 10 ++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go index 5591e9359..d29e496e7 100644 --- a/parsers/manifest_parser.go +++ b/parsers/manifest_parser.go @@ -1230,7 +1230,7 @@ func (dm *YAMLParser) ComposeApiRecords(client *whisk.Config, packageName string apiName, actionName, false); err!=nil { return requests, requestOptions, err } - // verify that the sequence action is defined under sequences records + // verify that the sequence action is defined under sequence records } else if _, ok := pkg.Sequences[actionName]; ok { // verify that the sequence action is defined as web sequence // web or web-export set to any of [true, yes, raw]; if not, diff --git a/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml b/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml index ffa8e8e60..ebb78683f 100644 --- a/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml +++ b/tests/src/integration/webaction/manifest_missing_web_annotation_on_api_action_or_sequence.yml @@ -20,6 +20,7 @@ # when these actions are used to create APIs. # 1) Test an Action missing web-export, but with existing Annotations declared # 2) Test a Sequence missing web-export with NO existing Annotations (to verify we allocate) +# 3) Test if web-action is set to false that we error out as we cannot override explicit value packages: TestMissingWebAnnotation: actions: @@ -31,6 +32,9 @@ packages: foo: bar hello_wrap: function: src/hello_http_wrap.js + helloWebFalse: + function: src/hello.js + web: false sequences: hellosequence: actions: hello_validate, hello, hello_wrap @@ -44,3 +48,6 @@ packages: hellosequence: method: GET response: http + helloWebFalse: + method: GET + response: http diff --git a/webaction/webaction.go b/webaction/webaction.go index c8642a812..e72b4cc66 100644 --- a/webaction/webaction.go +++ b/webaction/webaction.go @@ -172,7 +172,8 @@ func warnWebAnnotationMissingFromActionOrSequence(apiName string, actionName str func TryUpdateAPIsActionToWebAction(records []utils.ActionRecord, pkgName string, apiName string, actionName string, isSequence bool) error { a1 := utils.GetActionFromActionRecords(records,pkgName,actionName) - if a1.Annotations.FindKeyValue(WEB_EXPORT_ANNOT) == -1 { + //if a1.Annotations.FindKeyValue(WEB_EXPORT_ANNOT) == -1 { + if !HasAnnotation(&a1.Annotations,WEB_EXPORT_ANNOT) { if !utils.Flags.Strict { warnWebAnnotationMissingFromActionOrSequence(apiName,actionName,isSequence) a1.Annotations = addWebAnnotations(a1.Annotations) @@ -180,7 +181,12 @@ func TryUpdateAPIsActionToWebAction(records []utils.ActionRecord, pkgName string } else { return wskderrors.NewInvalidWebActionError(apiName,actionName,isSequence) } - } // NOOP + } else { + // verify its web-export annotation value is "true", else error + if !a1.WebAction() { + return wskderrors.NewInvalidWebActionError(apiName,actionName,isSequence) + } + } return nil } From 7883d9bda87fe3bda593673b5aafe97abb396cc0 Mon Sep 17 00:00:00 2001 From: Matt Rutkowski Date: Fri, 31 Jan 2020 10:39:36 -0600 Subject: [PATCH 6/8] refine procedural logic for reuse --- parsers/manifest_parser_test.go | 2 +- webaction/webaction.go | 38 +++++++++++++++++---------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/parsers/manifest_parser_test.go b/parsers/manifest_parser_test.go index 5fcb5ef18..ea5ef556e 100644 --- a/parsers/manifest_parser_test.go +++ b/parsers/manifest_parser_test.go @@ -1576,7 +1576,7 @@ func TestComposeApiRecords(t *testing.T) { ApigwAccessToken: "token", } - apiList, apiRequestOptions, err := p.ComposeApiRecordsFromAllPackages(&config, m) + apiList, apiRequestOptions, err := p.ComposeApiRecordsFromAllPackages(&config, m, nil, nil) if err != nil { assert.Fail(t, "Failed to compose api records: "+err.Error()) } diff --git a/webaction/webaction.go b/webaction/webaction.go index e72b4cc66..e69c07350 100644 --- a/webaction/webaction.go +++ b/webaction/webaction.go @@ -145,10 +145,6 @@ func IsWebAction(webexport string) bool { return false } -func IsWebSequence(webexport string) bool { - return IsWebAction(webexport) -} - func HasAnnotation(annotations *whisk.KeyValueArr, key string) bool { return (annotations.FindKeyValue(key) >= 0) } @@ -170,21 +166,27 @@ func warnWebAnnotationMissingFromActionOrSequence(apiName string, actionName str } func TryUpdateAPIsActionToWebAction(records []utils.ActionRecord, pkgName string, apiName string, actionName string, isSequence bool) error { - a1 := utils.GetActionFromActionRecords(records,pkgName,actionName) - - //if a1.Annotations.FindKeyValue(WEB_EXPORT_ANNOT) == -1 { - if !HasAnnotation(&a1.Annotations,WEB_EXPORT_ANNOT) { - if !utils.Flags.Strict { - warnWebAnnotationMissingFromActionOrSequence(apiName,actionName,isSequence) - a1.Annotations = addWebAnnotations(a1.Annotations) - fmt.Printf("Web Annotations to Action; result: %v\n",a1.Annotations) + + // if records are nil; it may be that the Action already exists at target provider OR + // this is a unit test. If the former case, we pass through and allow provider to validate + // and return an error. + if records!=nil { + action := utils.GetActionFromActionRecords(records,pkgName,actionName) + + if !HasAnnotation(&action.Annotations,WEB_EXPORT_ANNOT) { + if !utils.Flags.Strict { + warnWebAnnotationMissingFromActionOrSequence(apiName,actionName,isSequence) + action.Annotations = addWebAnnotations(action.Annotations) + wskprint.PrintOpenWhiskVerbose(utils.Flags.Verbose, + fmt.Sprintf("Web Annotations to Action; result: %v\n",action.Annotations)) + } else { + return wskderrors.NewInvalidWebActionError(apiName,actionName,isSequence) + } } else { - return wskderrors.NewInvalidWebActionError(apiName,actionName,isSequence) - } - } else { - // verify its web-export annotation value is "true", else error - if !a1.WebAction() { - return wskderrors.NewInvalidWebActionError(apiName,actionName,isSequence) + // verify its web-export annotation value is "true", else error + if !action.WebAction() { + return wskderrors.NewInvalidWebActionError(apiName,actionName,isSequence) + } } } From 153a006a0952020d8e3fc95d91f37320027adc71 Mon Sep 17 00:00:00 2001 From: Matt Rutkowski Date: Fri, 31 Jan 2020 11:30:56 -0600 Subject: [PATCH 7/8] refine procedural logic for reuse --- specification/html/spec_sequences.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/specification/html/spec_sequences.md b/specification/html/spec_sequences.md index 146ec83c8..0529d89fc 100644 --- a/specification/html/spec_sequences.md +++ b/specification/html/spec_sequences.md @@ -67,6 +67,12 @@ sequences: : actions: + web: | yes | no | raw + annotations: + + web-export: | yes | no | raw # optional + web-custom-options: # optional, only valid when `web-export` enabled + require-whisk-auth: | | # optional, only valid when `web-export` enabled ... ``` @@ -75,6 +81,7 @@ sequences: sequences: newbot: actions: newbot-create, newbot-select-persona, newbot-greeting + web: true ```