Skip to content

Commit 3151b6c

Browse files
committed
feat: add support for SipHeaders in Answer webhook
1 parent c1a7322 commit 3151b6c

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

Vonage.Test/Voice/Data/Answer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22
"from": "442079460000",
33
"to": "447700900000",
44
"uuid": "aaaaaaaa-bbbb-cccc-dddd-0123456789ab",
5-
"conversation_uuid": "CON-aaaaaaaa-bbbb-cccc-dddd-0123456789ab"
5+
"conversation_uuid": "CON-aaaaaaaa-bbbb-cccc-dddd-0123456789ab",
6+
"SipHeader_X-Test": "test",
7+
"SipHeader_X-Value": "value"
68
}

Vonage.Test/Voice/WebhookStructsTest.cs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#region
22
using System;
3+
using System.Collections.Generic;
34
using System.Globalization;
45
using System.IO;
56
using FluentAssertions;
@@ -31,15 +32,6 @@ public class WebhookStructsTest
3132
"yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
3233
CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
3334

34-
private static Answer ExpectedAnswer =>
35-
new Answer
36-
{
37-
From = From,
38-
To = To,
39-
Uuid = Uuid,
40-
ConversationUuid = ConversationId,
41-
};
42-
4335
private static Answered ExpectedAnswered =>
4436
new Answered
4537
{
@@ -230,11 +222,27 @@ public class WebhookStructsTest
230222
SipCode = 404,
231223
};
232224

225+
private static void VerifyAnswer(Answer input)
226+
{
227+
input.From.Should().Be(From);
228+
input.To.Should().Be(To);
229+
input.Uuid.Should().Be(Uuid);
230+
input.ConversationUuid.Should().Be(ConversationId);
231+
input.SipHeaders.Should().BeEquivalentTo(new Dictionary<string, string>
232+
{
233+
{"SipHeader_X-Test", "test"},
234+
{"SipHeader_X-Value", "value"},
235+
});
236+
}
237+
233238
[Theory]
234239
[InlineData(JsonSerializerType.Newtonsoft)]
235240
[InlineData(JsonSerializerType.SystemTextJson)]
236-
public void DeserializeAnswer(JsonSerializerType serializer) =>
237-
Deserialize<Answer>(ReadJson("Voice/Data/Answer.json"), serializer).Should().BeEquivalentTo(ExpectedAnswer);
241+
public void DeserializeAnswer(JsonSerializerType serializer)
242+
{
243+
var answer = Deserialize<Answer>(ReadJson("Voice/Data/Answer.json"), serializer);
244+
VerifyAnswer(answer);
245+
}
238246

239247
[Theory]
240248
[InlineData(JsonSerializerType.Newtonsoft)]

Vonage/Voice/AnswerWebhooks/Answer.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#region
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text.Json;
25
using System.Text.Json.Serialization;
36
using Newtonsoft.Json;
7+
using Newtonsoft.Json.Linq;
48
using Vonage.Voice.EventWebhooks;
59
#endregion
610

@@ -35,4 +39,34 @@ public class Answer : EventBase
3539
[JsonProperty("conversation_uuid")]
3640
[JsonPropertyName("conversation_uuid")]
3741
public string ConversationUuid { get; set; }
42+
43+
/// <summary>
44+
/// Captures all additional unmapped properties from the JSON payload.
45+
/// This is used internally to store extension data including SIP headers.
46+
/// </summary>
47+
[System.Text.Json.Serialization.JsonExtensionData]
48+
[Newtonsoft.Json.JsonExtensionData]
49+
50+
// ReSharper disable once CollectionNeverUpdated.Global
51+
public Dictionary<string, object> ExtensionData { get; set; } = new Dictionary<string, object>();
52+
53+
/// <summary>
54+
/// Gets all SIP header properties (properties starting with "SipHeader_X-") from the webhook payload.
55+
/// The dictionary key is the full property name (e.g., "SipHeader_X-Custom") and the value is the header value.
56+
/// </summary>
57+
[System.Text.Json.Serialization.JsonIgnore]
58+
[Newtonsoft.Json.JsonIgnore]
59+
public Dictionary<string, string> SipHeaders =>
60+
this.ExtensionData
61+
.Where(kvp => kvp.Key.StartsWith("SipHeader_X-"))
62+
.ToDictionary(kvp => kvp.Key, kvp => GetStringValue(kvp.Value));
63+
64+
private static string GetStringValue(object value) =>
65+
value switch
66+
{
67+
JsonElement jsonElement => jsonElement.GetString(),
68+
JToken jToken => jToken.Value<string>(),
69+
string str => str,
70+
_ => value?.ToString(),
71+
};
3872
}

0 commit comments

Comments
 (0)