diff --git a/examples/ResponseWithHeaders.md b/examples/ResponseWithHeaders.md new file mode 100644 index 000000000..92631619b --- /dev/null +++ b/examples/ResponseWithHeaders.md @@ -0,0 +1,58 @@ +```csharp +using System; +using System.IO; +using Microsoft.Extensions.Logging; +using Serilog; +using Serilog.Events; +using Twilio; +using Twilio.Rest.Api.V2010.Account; +using Twilio.Types; +using Twilio.Http; +using Twilio.Credential; + +namespace TwilioTest { + class Program + { + static void Main(string[] args) + { + TwilioClient.Init(ACCOUNT_SID, AUTH_TOKEN); + } + + public static void createWithHeaders() + { + var record = MessageResource.CreateWithHeaders( + from: new PhoneNumber("fromNumber"), + body: "Body", + to: new PhoneNumber("toNumber") + ); + Console.WriteLine(record); + Console.WriteLine(record.Headers); + Console.WriteLine(record.Data.Sid); + } + + public static void fetchWithHeaders() + { + var record = MessageResource.FetchWithHeaders(pathSid: "SM123"); + Console.WriteLine(record); + Console.WriteLine(record.Headers); + Console.WriteLine(record.Data.Body); + } + + public static void updateWithHeaders() + { + var record = MessageResource.UpdateWithHeaders(pathSid: "SM123", body: ""); + Console.WriteLine(record); + Console.WriteLine(record.Headers); + Console.WriteLine(record.Data); + } + + public static void deleteWithHeaders() + { + var record = MessageResource.DeleteWithHeaders(pathSid: "SM123"); + Console.WriteLine(record); + Console.WriteLine(record.Headers); + Console.WriteLine(record.Data); + } + } +} +``` \ No newline at end of file diff --git a/src/Twilio/Base/ResourceSetResponse.cs b/src/Twilio/Base/ResourceSetResponse.cs new file mode 100644 index 000000000..878cc66b8 --- /dev/null +++ b/src/Twilio/Base/ResourceSetResponse.cs @@ -0,0 +1,66 @@ +using System.Collections; +using System.Collections.Generic; +using System.Net; + +#if NET35 +using Headers = System.Net.WebHeaderCollection; +#else +using Headers = System.Net.Http.Headers.HttpResponseHeaders; +#endif + +namespace Twilio.Base +{ + /// + /// Wrapper class that contains both the resource set and HTTP response headers. + /// Implements IEnumerable to allow direct iteration over records. + /// + /// The type of the resource + public class ResourceSetResponse : IEnumerable where T : Resource + { + /// + /// The resource set containing the records + /// + public ResourceSet Records { get; } + + /// + /// HTTP response headers from the initial request + /// + public Headers Headers { get; } + + /// + /// HTTP status code from the initial request + /// + public HttpStatusCode StatusCode { get; } + + /// + /// Create a new ResourceSetResponse + /// + /// The resource set containing records + /// HTTP response headers + /// HTTP status code + public ResourceSetResponse(ResourceSet records, Headers headers, HttpStatusCode statusCode) + { + Records = records; + Headers = headers; + StatusCode = statusCode; + } + + /// + /// Get enumerator for iterating over resources + /// + /// IEnumerator of resources + public IEnumerator GetEnumerator() + { + return Records.GetEnumerator(); + } + + /// + /// Get enumerator for iterating over resources + /// + /// IEnumerator of resources + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/Twilio/Base/TwilioResponse.cs b/src/Twilio/Base/TwilioResponse.cs new file mode 100644 index 000000000..b7227817c --- /dev/null +++ b/src/Twilio/Base/TwilioResponse.cs @@ -0,0 +1,45 @@ +using System.Net; + +#if NET35 +using Headers = System.Net.WebHeaderCollection; +#else +using Headers = System.Net.Http.Headers.HttpResponseHeaders; +#endif + +namespace Twilio.Base +{ + /// + /// Wrapper class that contains both the resource and HTTP response headers + /// + /// The type of the resource + public class TwilioResponse + { + /// + /// The resource data + /// + public T Data { get; } + + /// + /// HTTP response headers + /// + public Headers Headers { get; } + + /// + /// HTTP status code + /// + public HttpStatusCode StatusCode { get; } + + /// + /// Create a new TwilioResponse + /// + /// The resource data + /// HTTP response headers + /// HTTP status code + public TwilioResponse(T data, Headers headers, HttpStatusCode statusCode) + { + Data = data; + Headers = headers; + StatusCode = statusCode; + } + } +} diff --git a/test/Twilio.Test/Base/ResourceSetResponseTest.cs b/test/Twilio.Test/Base/ResourceSetResponseTest.cs new file mode 100644 index 000000000..f7394daf0 --- /dev/null +++ b/test/Twilio.Test/Base/ResourceSetResponseTest.cs @@ -0,0 +1,176 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net; +using NUnit.Framework; +using Twilio.Base; + +#if !NET35 +using System.Net.Http; +#endif + +namespace Twilio.Tests.Base +{ + [TestFixture] + public class ResourceSetResponseTest + { + // Mock resource class for testing + private class MockResource : Resource + { + public string Id { get; set; } + public string Name { get; set; } + } + + // Mock ReadOptions for testing + private class MockReadOptions : ReadOptions + { + } + +#if !NET35 + private ResourceSet CreateMockResourceSet(List records) + { + // Create a JSON string that Page.FromJson can parse + var json = CreatePageJson(records); + var page = Page.FromJson("records", json); + var options = new MockReadOptions(); + return new ResourceSet(page, options, null); + } + + private string CreatePageJson(List records) + { + var recordsJson = string.Join(",", records.Select(r => + $"{{\"Id\":\"{r.Id}\",\"Name\":\"{r.Name}\"}}")); + return $"{{\"records\":[{recordsJson}],\"page_size\":{records.Count},\"uri\":\"/test\"}}"; + } + + [Test] + public void TestResourceSetResponseProperties() + { + var records = new List + { + new MockResource { Id = "1", Name = "First" }, + new MockResource { Id = "2", Name = "Second" } + }; + var resourceSet = CreateMockResourceSet(records); + var headers = new HttpResponseMessage().Headers; + var statusCode = HttpStatusCode.OK; + + var response = new ResourceSetResponse(resourceSet, headers, statusCode); + + Assert.AreEqual(resourceSet, response.Records); + Assert.AreEqual(headers, response.Headers); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + } + + [Test] + public void TestResourceSetResponseEnumeration() + { + var records = new List + { + new MockResource { Id = "1", Name = "First" }, + new MockResource { Id = "2", Name = "Second" }, + new MockResource { Id = "3", Name = "Third" } + }; + var resourceSet = CreateMockResourceSet(records); + resourceSet.AutoPaging = false; + var headers = new HttpResponseMessage().Headers; + var statusCode = HttpStatusCode.OK; + + var response = new ResourceSetResponse(resourceSet, headers, statusCode); + + // Test enumeration through the response + var enumeratedRecords = response.ToList(); + Assert.AreEqual(3, enumeratedRecords.Count); + Assert.AreEqual("1", enumeratedRecords[0].Id); + Assert.AreEqual("2", enumeratedRecords[1].Id); + Assert.AreEqual("3", enumeratedRecords[2].Id); + } + + [Test] + public void TestResourceSetResponseWithEmptySet() + { + var records = new List(); + var resourceSet = CreateMockResourceSet(records); + resourceSet.AutoPaging = false; + var headers = new HttpResponseMessage().Headers; + var statusCode = HttpStatusCode.OK; + + var response = new ResourceSetResponse(resourceSet, headers, statusCode); + + var enumeratedRecords = response.ToList(); + Assert.AreEqual(0, enumeratedRecords.Count); + } + + [Test] + public void TestResourceSetResponseWithDifferentStatusCodes() + { + var records = new List + { + new MockResource { Id = "1", Name = "Test" } + }; + var resourceSet = CreateMockResourceSet(records); + var headers = new HttpResponseMessage().Headers; + + var okResponse = new ResourceSetResponse(resourceSet, headers, HttpStatusCode.OK); + Assert.AreEqual(HttpStatusCode.OK, okResponse.StatusCode); + + var partialResponse = new ResourceSetResponse(resourceSet, headers, HttpStatusCode.PartialContent); + Assert.AreEqual(HttpStatusCode.PartialContent, partialResponse.StatusCode); + } + + [Test] + public void TestResourceSetResponseForeachEnumeration() + { + var records = new List + { + new MockResource { Id = "A", Name = "Alpha" }, + new MockResource { Id = "B", Name = "Beta" } + }; + var resourceSet = CreateMockResourceSet(records); + resourceSet.AutoPaging = false; + var headers = new HttpResponseMessage().Headers; + var statusCode = HttpStatusCode.OK; + + var response = new ResourceSetResponse(resourceSet, headers, statusCode); + + // Test foreach enumeration + var ids = new List(); + foreach (var record in response) + { + ids.Add(record.Id); + } + + Assert.AreEqual(2, ids.Count); + Assert.Contains("A", ids); + Assert.Contains("B", ids); + } + + [Test] + public void TestResourceSetResponseLinqOperations() + { + var records = new List + { + new MockResource { Id = "1", Name = "Apple" }, + new MockResource { Id = "2", Name = "Banana" }, + new MockResource { Id = "3", Name = "Cherry" } + }; + var resourceSet = CreateMockResourceSet(records); + resourceSet.AutoPaging = false; + var headers = new HttpResponseMessage().Headers; + var statusCode = HttpStatusCode.OK; + + var response = new ResourceSetResponse(resourceSet, headers, statusCode); + + // Test LINQ operations + var firstRecord = response.First(); + Assert.AreEqual("1", firstRecord.Id); + + var count = response.Count(); + Assert.AreEqual(3, count); + + var filtered = response.Where(r => r.Name.StartsWith("B")).ToList(); + Assert.AreEqual(1, filtered.Count); + Assert.AreEqual("Banana", filtered[0].Name); + } +#endif + } +} diff --git a/test/Twilio.Test/Base/TwilioResponseTest.cs b/test/Twilio.Test/Base/TwilioResponseTest.cs new file mode 100644 index 000000000..7b480f202 --- /dev/null +++ b/test/Twilio.Test/Base/TwilioResponseTest.cs @@ -0,0 +1,114 @@ +using System.Net; +using NUnit.Framework; +using Twilio.Base; + +#if !NET35 +using System.Net.Http; +#endif + +namespace Twilio.Tests.Base +{ + [TestFixture] + public class TwilioResponseTest + { + // Mock resource class for testing + private class MockResource : Resource + { + public string Id { get; set; } + public string Name { get; set; } + } + +#if !NET35 + [Test] + public void TestTwilioResponseWithResource() + { + var resource = new MockResource { Id = "123", Name = "Test" }; + var headers = new HttpResponseMessage().Headers; + var statusCode = HttpStatusCode.OK; + + var response = new TwilioResponse(resource, headers, statusCode); + + Assert.AreEqual(resource, response.Data); + Assert.AreEqual(headers, response.Headers); + Assert.AreEqual(statusCode, response.StatusCode); + } + + [Test] + public void TestTwilioResponseWithBool() + { + var data = true; + var headers = new HttpResponseMessage().Headers; + var statusCode = HttpStatusCode.NoContent; + + var response = new TwilioResponse(data, headers, statusCode); + + Assert.AreEqual(true, response.Data); + Assert.AreEqual(headers, response.Headers); + Assert.AreEqual(HttpStatusCode.NoContent, response.StatusCode); + } + + [Test] + public void TestTwilioResponseWithString() + { + var data = "test string"; + var headers = new HttpResponseMessage().Headers; + var statusCode = HttpStatusCode.OK; + + var response = new TwilioResponse(data, headers, statusCode); + + Assert.AreEqual("test string", response.Data); + Assert.AreEqual(headers, response.Headers); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + } + + [Test] + public void TestTwilioResponseWithNullData() + { + MockResource data = null; + var headers = new HttpResponseMessage().Headers; + var statusCode = HttpStatusCode.NotFound; + + var response = new TwilioResponse(data, headers, statusCode); + + Assert.IsNull(response.Data); + Assert.AreEqual(headers, response.Headers); + Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); + } + + [Test] + public void TestTwilioResponseWithDifferentStatusCodes() + { + var resource = new MockResource { Id = "456", Name = "Created" }; + var headers = new HttpResponseMessage().Headers; + + var createdResponse = new TwilioResponse(resource, headers, HttpStatusCode.Created); + Assert.AreEqual(HttpStatusCode.Created, createdResponse.StatusCode); + + var acceptedResponse = new TwilioResponse(resource, headers, HttpStatusCode.Accepted); + Assert.AreEqual(HttpStatusCode.Accepted, acceptedResponse.StatusCode); + + var badRequestResponse = new TwilioResponse(resource, headers, HttpStatusCode.BadRequest); + Assert.AreEqual(HttpStatusCode.BadRequest, badRequestResponse.StatusCode); + } + + [Test] + public void TestTwilioResponseDataIsReadOnly() + { + var resource = new MockResource { Id = "789", Name = "ReadOnly" }; + var headers = new HttpResponseMessage().Headers; + var statusCode = HttpStatusCode.OK; + + var response = new TwilioResponse(resource, headers, statusCode); + + // Verify that Data property returns the same instance + Assert.AreSame(resource, response.Data); + + // Modify the original resource + resource.Name = "Modified"; + + // The response should reflect the change since it holds a reference + Assert.AreEqual("Modified", response.Data.Name); + } +#endif + } +}