diff --git a/README.md b/README.md index 70cbdf8..3832c32 100644 --- a/README.md +++ b/README.md @@ -104,8 +104,6 @@ namespace Example } } } -``` - ## Documentation for API Endpoints @@ -374,6 +372,11 @@ Class | Method | HTTP request | Description - [Model.WebhookSubscriptionsListBody](docs/WebhookSubscriptionsListBody.md) + +## Documentation for BXML Verbs + + - [Bxml.Verbs.Refer](docs/Refer.md) + ## Documentation for Authorization diff --git a/docs/Refer.md b/docs/Refer.md new file mode 100644 index 0000000..508c6ad --- /dev/null +++ b/docs/Refer.md @@ -0,0 +1,40 @@ +# Bandwidth.Standard.Model.Bxml.Verbs.Refer + +The `` verb is used to hand off a call to a SIP endpoint via a SIP REFER. The call is transferred to the specified SIP URI, and an optional callback is sent when the transfer completes. + +For more details, see the [Bandwidth BXML Refer documentation](https://dev.bandwidth.com/docs/voice/bxml/refer.html). + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ReferCompleteUrl** | **string** | URL to receive the `referComplete` callback when the REFER is finished. | [optional] +**ReferCompleteMethod** | **string** | HTTP method to use for the `referComplete` callback. Must be `GET` or `POST`. | [optional] [default to `POST`] +**Tag** | **string** | Optional custom string to include in callbacks. Max 4096 characters. | [optional] +**SipUriElement** | [**Refer.SipUri**](#sipuri-nested-class) | The SIP URI destination for the REFER. Must start with `sip:`. | + +## SipUri Nested Class + +The `` element specifies the destination SIP URI for the REFER. + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Uri** | **string** | The SIP URI to refer the call to. Must start with `sip:`. | + +## Methods + +Name | Description +------------ | ------------- +`WithSipUri(string sipUri)` | Sets the SIP URI destination from a string. Returns the `Refer` instance for chaining. +`WithSipUri(SipUri sipUri)` | Sets the SIP URI destination from a `SipUri` object. Returns the `Refer` instance for chaining. +`WithReferCompleteUrl(string referCompleteUrl)` | Sets the `referCompleteUrl` attribute. Returns the `Refer` instance for chaining. +`WithReferCompleteMethod(string referCompleteMethod)` | Sets the `referCompleteMethod` attribute (`GET` or `POST`). Returns the `Refer` instance for chaining. +`WithTag(string tag)` | Sets the `tag` attribute. Returns the `Refer` instance for chaining. + +## Validation + +- `SipUri.Uri` must start with `sip:` (case-insensitive). An `ArgumentException` is thrown if the value does not match. +- `ReferCompleteMethod` must be either `GET` or `POST`. An `ArgumentException` is thrown for any other value. + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/src/Bandwidth.Standard.Test/Unit/Model/Bxml/TestRefer.cs b/src/Bandwidth.Standard.Test/Unit/Model/Bxml/TestRefer.cs new file mode 100644 index 0000000..9064e42 --- /dev/null +++ b/src/Bandwidth.Standard.Test/Unit/Model/Bxml/TestRefer.cs @@ -0,0 +1,70 @@ +using System; +using System.IO; +using System.Xml.Serialization; +using Bandwidth.Standard.Model.Bxml; +using Bandwidth.Standard.Model.Bxml.Verbs; +using Xunit; + +namespace Bandwidth.Standard.Test.Unit.Model.Bxml +{ + public class TestRefer + { + [Fact] + public void ReferRoundTripTest() + { + var expected = " sip:alice@atlanta.example.com "; + + var refer = new Refer() + .WithSipUri("sip:alice@atlanta.example.com") + .WithReferCompleteUrl("https://example.com/handleRefer") + .WithReferCompleteMethod("POST") + .WithTag("refer-tag"); + + var actual = new Response(refer).ToBXML(); + Assert.Equal(expected, actual.Replace("\n", "").Replace("\r", "")); + + const string referOnlyXml = "sip:alice@atlanta.example.com"; + var serializer = new XmlSerializer(typeof(Refer), ""); + Refer deserializedRefer; + using (var reader = new StringReader(referOnlyXml)) + { + deserializedRefer = (Refer)serializer.Deserialize(reader); + } + + Assert.Equal("sip:alice@atlanta.example.com", deserializedRefer.SipUriElement.Uri); + var roundTrip = new Response(deserializedRefer).ToBXML(); + Assert.Equal(expected, roundTrip.Replace("\n", "").Replace("\r", "")); + } + + [Fact] + public void ReferSipUriMustStartWithSipScheme() + { + var refer = new Refer(); + Assert.Throws(() => refer.WithSipUri("tel:+15551234567")); + } + + [Fact] + public void ReferInvalidMethodThrows() + { + var refer = new Refer(); + Assert.Throws(() => refer.WithReferCompleteMethod("DELETE")); + } + + [Fact] + public void ReferWithSipUriObjectOverload() + { + var sipUri = new Refer.SipUri { Uri = "sip:alice@atlanta.example.com" }; + var refer = new Refer().WithSipUri(sipUri); + Assert.Equal("sip:alice@atlanta.example.com", refer.SipUriElement.Uri); + } + + [Fact] + public void ReferMinimalOnlySipUri() + { + var refer = new Refer().WithSipUri("sip:bob@biloxi.example.com"); + var bxml = new Response(refer).ToBXML(); + Assert.Contains("sip:bob@biloxi.example.com", bxml); + Assert.Contains("referCompleteMethod=\"POST\"", bxml); + } + } +} \ No newline at end of file diff --git a/src/Bandwidth.Standard/Model/Bxml/Verbs/Refer.cs b/src/Bandwidth.Standard/Model/Bxml/Verbs/Refer.cs new file mode 100644 index 0000000..a712af8 --- /dev/null +++ b/src/Bandwidth.Standard/Model/Bxml/Verbs/Refer.cs @@ -0,0 +1,128 @@ +using Bandwidth.Standard.Model.Bxml; +using System; +using System.Xml.Serialization; + +namespace Bandwidth.Standard.Model.Bxml.Verbs +{ + /// + /// The Refer verb is used to hand off a call to a SIP endpoint. + /// + /// + public class Refer : IVerb + { + private string referCompleteMethod; + + /// + /// URL to receive the refer complete callback. + /// + [XmlAttribute("referCompleteUrl")] + public string ReferCompleteUrl { get; set; } + + /// + /// HTTP method to send the refer complete callback. GET or POST. Default value is POST. + /// + [XmlAttribute("referCompleteMethod")] + public string ReferCompleteMethod + { + get { return referCompleteMethod; } + set + { + if (value != null && value != "GET" && value != "POST") + { + throw new ArgumentException("ReferCompleteMethod must be either 'GET' or 'POST'."); + } + referCompleteMethod = value; + } + } + + /// + /// Optional custom string to include in callbacks. + /// + [XmlAttribute("tag")] + public string Tag { get; set; } + + /// + /// SIP URI destination for the REFER. + /// + [XmlElement("SipUri")] + public SipUri SipUriElement { get; set; } + + /// + /// Initializes a new instance of the Refer class. + /// + public Refer() + { + ReferCompleteMethod = "POST"; + } + + /// + /// Sets the SIP URI destination from a string. + /// + public Refer WithSipUri(string sipUri) + { + SipUriElement = new SipUri { Uri = sipUri }; + return this; + } + + /// + /// Sets the SIP URI destination from a SipUri object. + /// + public Refer WithSipUri(SipUri sipUri) + { + SipUriElement = sipUri; + return this; + } + + /// + /// Sets referCompleteUrl. + /// + public Refer WithReferCompleteUrl(string referCompleteUrl) + { + ReferCompleteUrl = referCompleteUrl; + return this; + } + + /// + /// Sets referCompleteMethod. + /// + public Refer WithReferCompleteMethod(string referCompleteMethod) + { + ReferCompleteMethod = referCompleteMethod; + return this; + } + + /// + /// Sets tag. + /// + public Refer WithTag(string tag) + { + Tag = tag; + return this; + } + + /// + /// BXML tag to represent a SIP URI for the refer verb. + /// + public class SipUri : IVerb + { + private string _uri; + + /// + /// SIP URI to refer the call to (must start with sip:). + /// + [XmlText] + public string Uri + { + get { return _uri; } + set + { + if (value != null && !value.StartsWith("sip:", StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException("SipUri must start with 'sip:'."); + } + _uri = value; + } + } + } + } +} \ No newline at end of file