Vonage / vonage-dotnet-sdk

Vonage REST API client for .NET, written in C#. API support for SMS, Voice, Text-to-Speech, Numbers, Verify (2FA) and more.
https://developer.vonage.com/
Apache License 2.0
105 stars 82 forks source link

StartTime is not nullable in class Answered (EventWebHook) #567

Closed MarcusKohnert closed 5 months ago

MarcusKohnert commented 7 months ago

Describe the bug I'm using the EventBase.ParseEvent method to parse the received webhook events.

Expected behaviour: The method is able to parse an Answered event send by Vonage.

{
    "start_time": null,
    "headers": {
        "[redacted]": "[redacted]"
    },
    "rate": null,
    "from": "[redacted]",
    "to": "[redacted]",
    "uuid": "c8061992c89a65e3790bfa0aaca6f069",
    "conversation_uuid": "CON-5a0cdebb-0f35-45a4-9b38-a99f9d12d6d7",
    "status": "answered",
    "direction": "inbound",
    "network": null,
    "timestamp": "2024-01-25T12:16:32.319Z"
}

Actual behaviour: EventBase.ParseEvent throws an exception because start_time is null and StartTime is of type DateTime (which is not nullable).

StackTrace

Newtonsoft.Json.JsonSerializationException
  HResult=0x80131500
  Message=Error converting value {null} to type 'System.DateTime'. Path 'start_time', line 1, position 18.
  Source=Newtonsoft.Json
  StackTrace:
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
   at Vonage.Voice.EventWebhooks.EventBase.DeserializeStatus(String json, JProperty statusProperty)
   at Vonage.Voice.EventWebhooks.EventBase.ParseEvent(String json)

  This exception was originally thrown at this call stack:
    System.Convert.ChangeType(object, System.Type, System.IFormatProvider)
    Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(Newtonsoft.Json.JsonReader, object, System.Globalization.CultureInfo, Newtonsoft.Json.Serialization.JsonContract, System.Type)

Inner Exception 1:
InvalidCastException: Null object cannot be converted to a value type.

Environment:

Tr00d commented 7 months ago

Hi @MarcusKohnert,

Thanks for raising that one. I'll have a look asap

Tr00d commented 7 months ago

Hi @MarcusKohnert,

FYI the issue is fixed - the PR is linked. Unfortunately, this change can be considered a breaking change as I'm altering the signature of a public object. Normally, I should wait for the next major version (v7.0.0), but I was expecting that later this year. I'm considering pushing this one through anyway, given the current class is unstable. I'll let you know the outcome

MarcusKohnert commented 7 months ago

Unblocked myself with the following workaround:

EventBase eventBase;
try
{
    eventBase = EventBase.ParseEvent(json);
}
catch (JsonSerializationException) // workaround https://github.com/Vonage/vonage-dotnet-sdk/issues/567
{
    var callStatusEvent = JsonConvert.DeserializeObject<CallStatusEvent>(json, VonageSerialization.SerializerSettings)!;
    eventBase = new Answered
    {
        From             = callStatusEvent.From,
        Uuid             = callStatusEvent.Uuid,
        ConversationUuid = callStatusEvent.ConversationUuid,
        To               = callStatusEvent.To,
        Direction        = callStatusEvent.Direction,
        Status           = callStatusEvent.Status,
        DetailString     = callStatusEvent.DetailString,
        StartTime        = DateTime.Now,
        TimeStamp        = callStatusEvent.TimeStamp
    };
}
Tr00d commented 7 months ago

I'm sorry you had to go down that route :sweat_smile:

Tr00d commented 6 months ago

Hi @MarcusKohnert,

I wanted to share some feedback. This breaking change is available in the latest version v7.0.0-alpha. Being an alpha, the package is still subject to potential problems. I do expect a final version very soon.

Tr00d commented 5 months ago

Hi,

v7.0.0 is finally out, and this problem has been addressed.

Don't hesitate to share some feedback :smile: