def test_creating_exception_from_call_error(): call_error = CallError(unique_id="1337", error_code="ProtocolError", error_description="Something went wrong", error_details="Some details about the error") assert call_error.to_exception() == ProtocolError( description="Something went wrong", details="Some details about the error")
def unpack(msg): """ Unpacks a message into either a Call, CallError or CallResult. """ try: msg = json.loads(msg) except json.JSONDecodeError as e: raise FormatViolationError(f'Message is not valid JSON: {e}') if not isinstance(msg, list): raise ProtocolError("OCPP message hasn't the correct format. It " f"should be a list, but got {type(msg)} instead") for cls in [Call, CallResult, CallError]: try: if msg[0] == cls.message_type_id: return cls(*msg[1:]) except IndexError: raise ProtocolError("Message doesn\'t contain MessageTypeID") raise PropertyConstraintViolationError(f"MessageTypeId '{msg[0]}' isn't " "valid")
def test_exception_with_error_details(): exception = ProtocolError("Some error", {'key': 'value'}) assert exception.description == "Some error" assert exception.details == {'key': 'value'}
def test_exception_without_error_details(): exception = ProtocolError() assert exception.description == "Payload for Action is incomplete" assert exception.details == {}
def validate_payload(message, ocpp_version): """ Validate the payload of the message using JSON schemas. """ if type(message) not in [Call, CallResult]: raise ValidationError("Payload can't be validated because message " f"type. It's '{type(message)}', but it should " "be either 'Call' or 'CallResult'.") try: # 3 OCPP 1.6 schedules have fields of type floats. The JSON schema # defines a certain precision for these fields of 1 decimal. A value of # 21.4 is valid, whereas a value if 4.11 is not. # # The problem is that Python's internal representation of 21.4 might # have more than 1 decimal. It might be 21.399999999999995. This would # make the validation fail, although the payload is correct. This is a # known issue with jsonschemas, see: # https://github.com/Julian/jsonschema/issues/247 # # This issue can be fixed by using a different parser for floats than # the default one that is used. # # Both the schema and the payload must be parsed using the different # parser for floats. if ocpp_version == '1.6' and ( (type(message) == Call and message.action in ['SetChargingProfile', 'RemoteStartTransaction']) # noqa or (type(message) == CallResult and message.action == 'GetCompositeSchedule') ): validator = get_validator( message.message_type_id, message.action, ocpp_version, parse_float=decimal.Decimal ) message.payload = json.loads( json.dumps(message.payload), parse_float=decimal.Decimal ) else: validator = get_validator( message.message_type_id, message.action, ocpp_version ) except (OSError, json.JSONDecodeError) as e: raise ValidationError("Failed to load validation schema for action " f"'{message.action}': {e}") try: validator.validate(message.payload) except SchemaValidationError as e: if (e.validator == SchemaValidators.type.__name__): raise TypeConstraintViolationError(details={"cause": e.message}) elif (e.validator == SchemaValidators.additionalProperties.__name__): raise FormatViolationError(details={"cause": e.message}) elif (e.validator == SchemaValidators.required.__name__): raise ProtocolError(details={"cause": e.message}) elif e.validator == "maxLength": raise TypeConstraintViolationError( details={"cause": e.message}) from e else: raise ValidationError(f"Payload '{message.payload} for action " f"'{message.action}' is not valid: {e}")