def view_func(path): if _is_binary_cloud_event(request): # Support CloudEvents in binary content mode, with data being the # whole request body and context attributes retrieved from request # headers. data = request.get_data() context = Context( eventId=request.headers.get("ce-eventId"), timestamp=request.headers.get("ce-timestamp"), eventType=request.headers.get("ce-eventType"), resource=request.headers.get("ce-resource"), ) function(data, context) else: # This is a regular CloudEvent event_data = request.get_json() if not event_data: flask.abort(400) if _is_pub_sub_message(request): event_object = _PubSubPushEvent(**event_data) else: event_object = _Event(**event_data) data = event_object.data context = Context(**event_object.context) function(data, context) return "OK"
def view_func(path): if event_conversion.is_convertable_cloud_event(request): # Convert this CloudEvent to the equivalent background event data and context. data, context = event_conversion.cloud_event_to_background_event( request) function(data, context) elif is_binary(request.headers): # Support CloudEvents in binary content mode, with data being the # whole request body and context attributes retrieved from request # headers. data = request.get_data() context = Context( eventId=request.headers.get("ce-eventId"), timestamp=request.headers.get("ce-timestamp"), eventType=request.headers.get("ce-eventType"), resource=request.headers.get("ce-resource"), ) function(data, context) else: # This is a regular CloudEvent event_data = event_conversion.marshal_background_event_data( request) if not event_data: flask.abort(400) event_object = BackgroundEvent(**event_data) data = event_object.data context = Context(**event_object.context) function(data, context) return "OK"
def test_split_resource(background_resource): context = Context(eventType="google.storage.object.finalize", resource=background_resource) service, resource, subject = event_conversion._split_resource(context) assert service == "storage.googleapis.com" assert resource == "projects/_/buckets/some-bucket" assert subject == "objects/folder/Test.cs"
def _run_legacy_event(function, request): event_data = request.get_json() if not event_data: flask.abort(400) event_object = _Event(**event_data) data = event_object.data context = Context(**event_object.context) function(data, context)
def cloud_event_to_background_event(request) -> Tuple[Any, Context]: """Converts a background event represented by the given HTTP request into a CloudEvent.""" try: event = from_http(request.headers, request.get_data()) data = event.data service, name = _split_ce_source(event["source"]) if event["type"] not in _CE_TO_BACKGROUND_TYPE: raise EventConversionException( f'Unable to find background event equivalent type for "{event["type"]}"' ) if service == _PUBSUB_CE_SERVICE: resource = { "service": service, "name": name, "type": _PUBSUB_MESSAGE_TYPE } if "message" in data: data = data["message"] if "messageId" in data: del data["messageId"] if "publishTime" in data: del data["publishTime"] elif service == _FIREBASE_AUTH_CE_SERVICE: resource = name if "metadata" in data: for old, new in _FIREBASE_AUTH_METADATA_FIELDS_CE_TO_BACKGROUND.items( ): if old in data["metadata"]: data["metadata"][new] = data["metadata"][old] del data["metadata"][old] elif service == _STORAGE_CE_SERVICE: resource = { "name": f"{name}/{event['subject']}", "service": service, "type": data["kind"], } elif service == _FIREBASE_DB_CE_SERVICE: name = re.sub("/locations/[^/]+", "", name) resource = f"{name}/{event['subject']}" else: resource = f"{name}/{event['subject']}" context = Context( eventId=event["id"], timestamp=event["time"], eventType=_CE_TO_BACKGROUND_TYPE[event["type"]], resource=resource, ) return (data, context) except (AttributeError, KeyError, TypeError, MissingRequiredFields): raise EventConversionException( "Failed to convert CloudEvent to BackgroundEvent.")
def fixture_to_pubsub(fixture): with open('test/fixtures/%s.json' % fixture, 'r') as file: data = json.loads(file.read()) event = { 'data': data[0]['message']['data'], 'attributes': data[0]['message']['attributes'] } context = Context(eventId=data[0]['message']['messageId'], timestamp=data[0]['message']['publishTime']) return event, context
def test_split_resource_no_resource_regex_match(): background_resource = { "service": "storage.googleapis.com", # This name will not match the regex associated with the service. "name": "foo/bar/baz", "type": "storage#object", } context = Context(eventType="google.storage.object.finalize", resource=background_resource) with pytest.raises(EventConversionException) as exc_info: event_conversion._split_resource(context) assert "Resource regex did not match" in exc_info.value.args[0]
def test_split_resource_without_service_unknown_event_type(): background_resource = { "name": "projects/_/buckets/some-bucket/objects/folder/Test.cs", "type": "storage#object", } # This event type cannot be mapped to an equivalent CloudEvent type. context = Context(eventType="not_a_known_event_type", resource=background_resource) with pytest.raises(EventConversionException) as exc_info: event_conversion._split_resource(context) assert "Unable to find CloudEvent equivalent service" in exc_info.value.args[ 0]
def test_split_resource_unknown_service_and_event_type(): # With both an unknown service and an unknown event type, we won't attempt any # event type mapping or resource/subject splitting. background_resource = { "service": "not_a_known_service", "name": "projects/_/my/stuff/at/test.txt", "type": "storage#object", } context = Context(eventType="not_a_known_event_type", resource=background_resource) service, resource, subject = event_conversion._split_resource(context) assert service == "not_a_known_service" assert resource == "projects/_/my/stuff/at/test.txt" assert subject == ""
def background_event_to_cloudevent(request) -> CloudEvent: """Converts a background event represented by the given HTTP request into a CloudEvent.""" event_data = marshal_background_event_data(request) if not event_data: raise EventConversionException("Failed to parse JSON") event_object = BackgroundEvent(**event_data) data = event_object.data context = Context(**event_object.context) if context.event_type not in _BACKGROUND_TO_CE_TYPE: raise EventConversionException( f'Unable to find CloudEvent equivalent type for "{context.event_type}"' ) new_type = _BACKGROUND_TO_CE_TYPE[context.event_type] service, resource, subject = _split_resource(context) # Handle Pub/Sub events. if service == _PUBSUB_CE_SERVICE: data = {"message": data} # Handle Firebase Auth events. if service == _FIREBASE_AUTH_CE_SERVICE: if "metadata" in data: for old, new in _FIREBASE_AUTH_METADATA_FIELDS_BACKGROUND_TO_CE.items( ): if old in data["metadata"]: data["metadata"][new] = data["metadata"][old] del data["metadata"][old] if "uid" in data: uid = data["uid"] subject = f"users/{uid}" metadata = { "id": context.event_id, "time": context.timestamp, "specversion": _CLOUDEVENT_SPEC_VERSION, "datacontenttype": "application/json", "type": new_type, "source": f"//{service}/{resource}", } if subject: metadata["subject"] = subject return CloudEvent(metadata, data)
def _run_legacy_event(function, request): event_data = request.get_json() if not event_data: flask.abort(400) ############### # Need to add this code to make pubsub events work. if 'message' in event_data: if 'data' not in event_data: message = event_data['message'] event_data['data'] = { 'data': message.get('data'), 'attributes': message.get('attributes') } ############### event_object = _Event(**event_data) data = event_object.data context = Context(**event_object.context) function(data, context)
def background_event_to_cloud_event(request) -> CloudEvent: """Converts a background event represented by the given HTTP request into a CloudEvent.""" event_data = marshal_background_event_data(request) if not event_data: raise EventConversionException("Failed to parse JSON") event_object = BackgroundEvent(**event_data) data = event_object.data context = Context(**event_object.context) if context.event_type not in _BACKGROUND_TO_CE_TYPE: raise EventConversionException( f'Unable to find CloudEvent equivalent type for "{context.event_type}"' ) new_type = _BACKGROUND_TO_CE_TYPE[context.event_type] service, resource, subject = _split_resource(context) source = f"//{service}/{resource}" # Handle Pub/Sub events. if service == _PUBSUB_CE_SERVICE: if "messageId" not in data: data["messageId"] = context.event_id if "publishTime" not in data: data["publishTime"] = context.timestamp data = {"message": data} # Handle Firebase Auth events. if service == _FIREBASE_AUTH_CE_SERVICE: if "metadata" in data: for old, new in _FIREBASE_AUTH_METADATA_FIELDS_BACKGROUND_TO_CE.items( ): if old in data["metadata"]: data["metadata"][new] = data["metadata"][old] del data["metadata"][old] if "uid" in data: uid = data["uid"] subject = f"users/{uid}" # Handle Firebase DB events. if service == _FIREBASE_DB_CE_SERVICE: # The CE source of firebasedatabase CloudEvents includes location information # that is inferred from the 'domain' field of legacy events. if "domain" not in event_data: raise EventConversionException( "Invalid FirebaseDB event payload: missing 'domain'") domain = event_data["domain"] location = "us-central1" if domain != "firebaseio.com": location = domain.split(".")[0] resource = f"projects/_/locations/{location}/{resource}" source = f"//{service}/{resource}" metadata = { "id": context.event_id, "time": context.timestamp, "specversion": _CLOUD_EVENT_SPEC_VERSION, "datacontenttype": "application/json", "type": new_type, "source": source, } if subject: metadata["subject"] = subject return CloudEvent(metadata, data)
logger.setLevel(logging.INFO) json_handler = logging.StreamHandler() formatter = jsonlogger.JsonFormatter() json_handler.setFormatter(formatter) logger.addHandler(json_handler) return logger if __name__ == '__main__': arg_parser = argparse.ArgumentParser( description='Pub/Sub to Inbox, turn Pub/Sub messages into emails') arg_parser.add_argument('--config', type=str, help='Configuration file') arg_parser.add_argument('message', type=str, help='JSON file containing the message(s)') args = arg_parser.parse_args() if args.config: config_file_name = args.config with open(args.message) as f: contents = f.read() messages = json.loads(contents) for message in messages: event = { 'data': message['message']['data'], 'attributes': message['message']['attributes'] if 'attributes' in message['message'] else {} } context = Context(eventId=message['message']['messageId'], timestamp=message['message']['publishTime']) process_pubsub(event, context)