| 1 |  |  | # Licensed to the StackStorm, Inc ('StackStorm') under one or more | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  | # contributor license agreements.  See the NOTICE file distributed with | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | # this work for additional information regarding copyright ownership. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  | # The ASF licenses this file to You under the Apache License, Version 2.0 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | # (the "License"); you may not use this file except in compliance with | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  | # the License.  You may obtain a copy of the License at | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | # | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  | #     http://www.apache.org/licenses/LICENSE-2.0 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  | # | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  | # Unless required by applicable law or agreed to in writing, software | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  | # distributed under the License is distributed on an "AS IS" BASIS, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | # See the License for the specific language governing permissions and | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  | # limitations under the License. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  | try: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |     import simplejson as json | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  | except ImportError: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  |     import json | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  | import six | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  | import pecan | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  | import uuid | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  | from pecan import abort | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  | from pecan.rest import RestController | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  | from six.moves.urllib import parse as urlparse | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  | urljoin = urlparse.urljoin | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  | from st2common import log as logging | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  | from st2common.constants.triggers import WEBHOOK_TRIGGER_TYPES | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  | from st2common.models.api.base import jsexpose | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  | from st2common.models.api.trace import TraceContext | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 |  |  | import st2common.services.triggers as trigger_service | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 |  |  | from st2common.services.triggerwatcher import TriggerWatcher | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 |  |  | from st2common.transport.reactor import TriggerDispatcher | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  | from st2common.util.http import parse_content_type_header | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  | from st2common.rbac.types import PermissionType | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  | from st2common.rbac.decorators import request_user_has_webhook_permission | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  | http_client = six.moves.http_client | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  | LOG = logging.getLogger(__name__) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  | TRACE_TAG_HEADER = 'St2-Trace-Tag' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 |  |  | class WebhooksController(RestController): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 |  |  |     def __init__(self, *args, **kwargs): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  |         super(WebhooksController, self).__init__(*args, **kwargs) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 |  |  |         self._hooks = {} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  |         self._base_url = '/webhooks/' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  |         self._trigger_types = WEBHOOK_TRIGGER_TYPES.keys() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  |         self._trigger_dispatcher = TriggerDispatcher(LOG) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  |         queue_suffix = self.__class__.__name__ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 |  |  |         self._trigger_watcher = TriggerWatcher(create_handler=self._handle_create_trigger, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |                                                update_handler=self._handle_update_trigger, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  |                                                delete_handler=self._handle_delete_trigger, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  |                                                trigger_types=self._trigger_types, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  |                                                queue_suffix=queue_suffix, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  |                                                exclusive=True) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  |         self._trigger_watcher.start() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  |         self._register_webhook_trigger_types() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  |     @jsexpose() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  |     def get_all(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  |         # Return only the hooks known by this controller. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  |         return [trigger for trigger in six.itervalues(self._hooks)] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 |  |  |     @jsexpose() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 |  |  |     def get_one(self, name): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 |  |  |         hook = self._hooks.get(name, None) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 |  |  |         if not hook: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 |  |  |             abort(http_client.NOT_FOUND) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 76 |  |  |             return | 
            
                                                                                                            
                            
            
                                    
            
            
                | 77 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 78 |  |  |         return hook | 
            
                                                                                                            
                            
            
                                    
            
            
                | 79 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 |  |  |     @request_user_has_webhook_permission(permission_type=PermissionType.WEBHOOK_SEND) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  |     @jsexpose(arg_types=[str], status_code=http_client.ACCEPTED) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  |     def post(self, *args, **kwargs): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 |  |  |         hook = '/'.join(args)  # TODO: There must be a better way to do this. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 |  |  |         # Note: For backward compatibility reasons we default to application/json if content | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 |  |  |         # type is not explicitly provided | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  |         content_type = pecan.request.headers.get('Content-Type', 'application/json') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 |  |  |         content_type = parse_content_type_header(content_type=content_type)[0] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  |         body = pecan.request.body | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 |  |  |         try: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  |             body = self._parse_request_body(content_type=content_type, body=body) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  |         except Exception as e: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  |             self._log_request('Failed to parse request body: %s.' % (str(e)), pecan.request) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  |             msg = 'Failed to parse request body "%s": %s' % (body, str(e)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  |             return pecan.abort(http_client.BAD_REQUEST, msg) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  |         headers = self._get_headers_as_dict(pecan.request.headers) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 |  |  |         # If webhook contains a trace-tag use that else create create a unique trace-tag. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  |         trace_context = self._create_trace_context(trace_tag=headers.pop(TRACE_TAG_HEADER, None), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 |  |  |                                                    hook=hook) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 |  |  |         if hook == 'st2' or hook == 'st2/': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 |  |  |             return self._handle_st2_webhook(body, trace_context=trace_context) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 106 |  |  |         if not self._is_valid_hook(hook): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 107 |  |  |             self._log_request('Invalid hook.', pecan.request) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 108 |  |  |             msg = 'Webhook %s not registered with st2' % hook | 
            
                                                                                                            
                            
            
                                    
            
            
                | 109 |  |  |             return pecan.abort(http_client.NOT_FOUND, msg) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  |         trigger = self._get_trigger_for_hook(hook) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 |  |  |         payload = {} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 |  |  |         payload['headers'] = headers | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |         payload['body'] = body | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  |         self._trigger_dispatcher.dispatch(trigger, payload=payload, trace_context=trace_context) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 |  |  |         return body | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 |  |  |     def _parse_request_body(self, content_type, body): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 |  |  |         if content_type == 'application/json': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 |  |  |             self._log_request('Parsing request body as JSON', request=pecan.request) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 |  |  |             body = json.loads(body) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 |  |  |         elif content_type in ['application/x-www-form-urlencoded', 'multipart/form-data']: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  |             self._log_request('Parsing request body as form encoded data', request=pecan.request) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  |             body = urlparse.parse_qs(body) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 |  |  |         else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  |             raise ValueError('Unsupported Content-Type: "%s"' % (content_type)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 130 |  |  |         return body | 
            
                                                                                                            
                            
            
                                    
            
            
                | 131 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 132 |  |  |     def _handle_st2_webhook(self, body, trace_context): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 133 |  |  |         trigger = body.get('trigger', None) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 134 |  |  |         payload = body.get('payload', None) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 135 |  |  |         if not trigger: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 136 |  |  |             msg = 'Trigger not specified.' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 137 |  |  |             return pecan.abort(http_client.BAD_REQUEST, msg) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 138 |  |  |         self._trigger_dispatcher.dispatch(trigger, payload=payload, trace_context=trace_context) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 139 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 140 |  |  |         return body | 
            
                                                                                                            
                            
            
                                    
            
            
                | 141 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 142 |  |  |     def _is_valid_hook(self, hook): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 143 |  |  |         # TODO: Validate hook payload with payload_schema. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 144 |  |  |         return hook in self._hooks | 
            
                                                                                                            
                            
            
                                    
            
            
                | 145 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 146 |  |  |     def _get_trigger_for_hook(self, hook): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 147 |  |  |         return self._hooks[hook] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 148 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 149 |  |  |     def _register_webhook_trigger_types(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 |  |  |         for trigger_type in WEBHOOK_TRIGGER_TYPES.values(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 |  |  |             trigger_service.create_trigger_type_db(trigger_type) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 152 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 153 |  |  |     def _create_trace_context(self, trace_tag, hook): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 154 |  |  |         # if no trace_tag then create a unique one | 
            
                                                                                                            
                            
            
                                    
            
            
                | 155 |  |  |         if not trace_tag: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 156 |  |  |             trace_tag = 'webhook-%s-%s' % (hook, uuid.uuid4().hex) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 157 |  |  |         return TraceContext(trace_tag=trace_tag) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 158 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 159 |  |  |     def add_trigger(self, trigger): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 160 |  |  |         # Note: Permission checking for creating and deleting a webhook is done during rule | 
            
                                                                                                            
                            
            
                                    
            
            
                | 161 |  |  |         # creation | 
            
                                                                                                            
                            
            
                                    
            
            
                | 162 |  |  |         url = self._get_normalized_url(trigger) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 163 |  |  |         LOG.info('Listening to endpoint: %s', urljoin(self._base_url, url)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 164 |  |  |         self._hooks[url] = trigger | 
            
                                                                                                            
                            
            
                                    
            
            
                | 165 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 166 |  |  |     def update_trigger(self, trigger): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 167 |  |  |         pass | 
            
                                                                                                            
                            
            
                                    
            
            
                | 168 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 169 |  |  |     def remove_trigger(self, trigger): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 170 |  |  |         # Note: Permission checking for creating and deleting a webhook is done during rule | 
            
                                                                                                            
                            
            
                                    
            
            
                | 171 |  |  |         # creation | 
            
                                                                                                            
                            
            
                                    
            
            
                | 172 |  |  |         url = self._get_normalized_url(trigger) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 173 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 174 |  |  |         if url in self._hooks: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 175 |  |  |             LOG.info('Stop listening to endpoint: %s', urljoin(self._base_url, url)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 176 |  |  |             del self._hooks[url] | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 177 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 178 |  |  |     def _get_normalized_url(self, trigger): | 
            
                                                                        
                            
            
                                    
            
            
                | 179 |  |  |         """ | 
            
                                                                        
                            
            
                                    
            
            
                | 180 |  |  |         remove the trailing and leading / so that the hook url and those coming | 
            
                                                                        
                            
            
                                    
            
            
                | 181 |  |  |         from trigger parameters end up being the same. | 
            
                                                                        
                            
            
                                    
            
            
                | 182 |  |  |         """ | 
            
                                                                        
                            
            
                                    
            
            
                | 183 |  |  |         return trigger['parameters']['url'].strip('/') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 184 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 185 |  |  |     def _get_headers_as_dict(self, headers): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 186 |  |  |         headers_dict = {} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 187 |  |  |         for key, value in headers.items(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 188 |  |  |             headers_dict[key] = value | 
            
                                                                                                            
                            
            
                                    
            
            
                | 189 |  |  |         return headers_dict | 
            
                                                                                                            
                            
            
                                    
            
            
                | 190 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 191 |  |  |     def _log_request(self, msg, request, log_method=LOG.debug): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 192 |  |  |         headers = self._get_headers_as_dict(request.headers) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 193 |  |  |         body = str(request.body) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 194 |  |  |         log_method('%s\n\trequest.header: %s.\n\trequest.body: %s.', msg, headers, body) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 195 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 196 |  |  |     ############################################## | 
            
                                                                                                            
                            
            
                                    
            
            
                | 197 |  |  |     # Event handler methods for the trigger events | 
            
                                                                                                            
                            
            
                                    
            
            
                | 198 |  |  |     ############################################## | 
            
                                                                                                            
                            
            
                                    
            
            
                | 199 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 200 |  |  |     def _handle_create_trigger(self, trigger): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 201 |  |  |         LOG.debug('Calling "add_trigger" method (trigger.type=%s)' % (trigger.type)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 202 |  |  |         trigger = self._sanitize_trigger(trigger=trigger) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 203 |  |  |         self.add_trigger(trigger=trigger) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 204 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 205 |  |  |     def _handle_update_trigger(self, trigger): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 206 |  |  |         LOG.debug('Calling "update_trigger" method (trigger.type=%s)' % (trigger.type)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 207 |  |  |         trigger = self._sanitize_trigger(trigger=trigger) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 208 |  |  |         self.update_trigger(trigger=trigger) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 209 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 210 |  |  |     def _handle_delete_trigger(self, trigger): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 211 |  |  |         LOG.debug('Calling "remove_trigger" method (trigger.type=%s)' % (trigger.type)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 212 |  |  |         trigger = self._sanitize_trigger(trigger=trigger) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 213 |  |  |         self.remove_trigger(trigger=trigger) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 214 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 215 |  |  |     def _sanitize_trigger(self, trigger): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 216 |  |  |         sanitized = trigger._data | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 217 |  |  |         if 'id' in sanitized: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 218 |  |  |             # Friendly objectid rather than the MongoEngine representation. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 219 |  |  |             sanitized['id'] = str(sanitized['id']) | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 220 |  |  |         return sanitized | 
            
                                                        
            
                                    
            
            
                | 221 |  |  |  | 
            
                        
Prefixing a member variable
_is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class: