Exemplo n.º 1
0
    def UnmarshalJSON(
        self,
        b: typing.Union[str, bytes],
        data_unmarshaller: types.UnmarshallerType,
    ):
        raw_ce = json.loads(b)

        missing_fields = self._ce_required_fields - raw_ce.keys()
        if len(missing_fields) > 0:
            raise cloud_exceptions.MissingRequiredFields(
                f"Missing required attributes: {missing_fields}")

        for name, value in raw_ce.items():
            decoder = lambda x: x
            if name == "data":
                # Use the user-provided serializer, which may have customized
                # JSON decoding
                decoder = lambda v: data_unmarshaller(json.dumps(v))
            if name == "data_base64":
                decoder = lambda v: data_unmarshaller(base64.b64decode(v))
                name = "data"

            try:
                set_value = decoder(value)
            except Exception as e:
                raise cloud_exceptions.DataUnmarshallerError(
                    "Failed to unmarshall data with error: "
                    f"{type(e).__name__}('{e}')")
            self.Set(name, set_value)
Exemplo n.º 2
0
    def UnmarshalBinary(
        self,
        headers: dict,
        body: typing.Union[bytes, str],
        data_unmarshaller: types.UnmarshallerType,
    ):
        required_binary_fields = {
            f"ce-{field}"
            for field in self._ce_required_fields
        }
        missing_fields = required_binary_fields - headers.keys()

        if len(missing_fields) > 0:
            raise cloud_exceptions.MissingRequiredFields(
                f"Missing required attributes: {missing_fields}")

        for header, value in headers.items():
            header = header.lower()
            if header == "content-type":
                self.SetContentType(value)
            elif header.startswith("ce-"):
                self.Set(header[3:], value)

        try:
            raw_ce = data_unmarshaller(body)
        except Exception as e:
            raise cloud_exceptions.DataUnmarshallerError(
                f"Failed to unmarshall data with error: {type(e).__name__}('{e}')"
            )
        self.Set("data", raw_ce)
Exemplo n.º 3
0
    def __init__(
        self, attributes: typing.Dict[str, str], data: typing.Any = None
    ):
        """
        Event Constructor
        :param attributes: a dict with cloudevent attributes. Minimally
            expects the attributes 'type' and 'source'. If not given the
            attributes 'specversion', 'id' or 'time', this will create
            those attributes with default values.
            e.g. {
                "content-type": "application/cloudevents+json",
                "id": "16fb5f0b-211e-1102-3dfe-ea6e2806f124",
                "source": "<event-source>",
                "type": "cloudevent.event.type",
                "specversion": "0.2"
            }
        :type attributes: typing.Dict[str, str]
        :param data: The payload of the event, as a python object
        :type data: typing.Any
        """
        self._attributes = {k.lower(): v for k, v in attributes.items()}
        self.data = data
        if "specversion" not in self._attributes:
            self._attributes["specversion"] = "1.0"
        if "id" not in self._attributes:
            self._attributes["id"] = str(uuid.uuid4())
        if "time" not in self._attributes:
            self._attributes["time"] = datetime.datetime.now(
                datetime.timezone.utc
            ).isoformat()

        if self._attributes["specversion"] not in _required_by_version:
            raise cloud_exceptions.MissingRequiredFields(
                f"Invalid specversion: {self._attributes['specversion']}"
            )
        # There is no good way to default 'source' and 'type', so this
        # checks for those (or any new required attributes).
        required_set = _required_by_version[self._attributes["specversion"]]
        if not required_set <= self._attributes.keys():
            raise cloud_exceptions.MissingRequiredFields(
                f"Missing required keys: {required_set - self._attributes.keys()}"
            )
Exemplo n.º 4
0
def from_http(
    headers: typing.Dict[str, str],
    data: typing.Union[str, bytes, None],
    data_unmarshaller: types.UnmarshallerType = None,
):
    """
    Unwrap a CloudEvent (binary or structured) from an HTTP request.
    :param headers: the HTTP headers
    :type headers: typing.Dict[str, str]
    :param data: the HTTP request body. If set to None, "" or b'', the returned
        event's data field will be set to None
    :type data: typing.IO
    :param data_unmarshaller: Callable function to map data to a python object
        e.g. lambda x: x or lambda x: json.loads(x)
    :type data_unmarshaller: types.UnmarshallerType
    """
    if data is None or data == b"":
        # Empty string will cause data to be marshalled into None
        data = ""

    if not isinstance(data, (str, bytes, bytearray)):
        raise cloud_exceptions.InvalidStructuredJSON(
            "Expected json of type (str, bytes, bytearray), "
            f"but instead found type {type(data)}")

    headers = {key.lower(): value for key, value in headers.items()}
    if data_unmarshaller is None:
        data_unmarshaller = _json_or_string

    marshall = marshaller.NewDefaultHTTPMarshaller()

    if is_binary(headers):
        specversion = headers.get("ce-specversion", None)
    else:
        try:
            raw_ce = json.loads(data)
        except json.decoder.JSONDecodeError:
            raise cloud_exceptions.MissingRequiredFields(
                "Failed to read specversion from both headers and data. "
                f"The following can not be parsed as json: {data}")
        if hasattr(raw_ce, "get"):
            specversion = raw_ce.get("specversion", None)
        else:
            raise cloud_exceptions.MissingRequiredFields(
                "Failed to read specversion from both headers and data. "
                f"The following deserialized data has no 'get' method: {raw_ce}"
            )

    if specversion is None:
        raise cloud_exceptions.MissingRequiredFields(
            "Failed to find specversion in HTTP request")

    event_handler = _obj_by_version.get(specversion, None)

    if event_handler is None:
        raise cloud_exceptions.InvalidRequiredFields(
            f"Found invalid specversion {specversion}")

    event = marshall.FromRequest(event_handler(),
                                 headers,
                                 data,
                                 data_unmarshaller=data_unmarshaller)
    attrs = event.Properties()
    attrs.pop("data", None)
    attrs.pop("extensions", None)
    attrs.update(**event.extensions)

    if event.data == "" or event.data == b"":
        # TODO: Check binary unmarshallers to debug why setting data to ""
        # returns an event with data set to None, but structured will return ""
        data = None
    else:
        data = event.data
    return CloudEvent(attrs, data)