Skip to content

Commit c9b8750

Browse files
committed
Made XmlTransport public and fixed some bugs.
By making XmlTransport public, one can now use the second GatewayClient constructor to specify a custom endpoint or transport. Also improved documentation, removed an unused constant, and added some unit tests.
1 parent 951ebae commit c9b8750

9 files changed

Lines changed: 268 additions & 24 deletions

File tree

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using LinkMobility.PSWin.Client.Extensions;
2+
using NUnit.Framework;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
7+
namespace LinkMobility.PSWin.Client.Tests
8+
{
9+
[TestFixture]
10+
public class EnumerableBatchExtensionTests
11+
{
12+
[Test]
13+
public void Test_zero_batch_size()
14+
{
15+
Assert.Throws<ArgumentOutOfRangeException>(() => new int[2].Batch(0).ToArray());
16+
}
17+
18+
[Test]
19+
public void Test_MultipleEnumerationChecker()
20+
{
21+
var checker = new MultipleEnumerationChecker();
22+
var items = checker.GetItems(10);
23+
Assume.That(items.Count(), Is.EqualTo(items.Count()));
24+
Assert.That(checker.Enumerations, Is.EqualTo(2), "Detected enumerations");
25+
}
26+
27+
[Test]
28+
public void Test_no_multiple_enumeration()
29+
{
30+
var checker = new MultipleEnumerationChecker();
31+
var items = checker.GetItems(30);
32+
33+
var iteratedItems = 0;
34+
foreach (var batch in items.Batch(10))
35+
foreach (var item in batch)
36+
iteratedItems++;
37+
38+
Assume.That(iteratedItems, Is.GreaterThan(0));
39+
Assert.That(checker.Enumerations, Is.EqualTo(1), "Enumerations");
40+
}
41+
42+
[Test]
43+
public void Test_zero_batches()
44+
{
45+
var items = new int[0];
46+
var batch = items.Batch(10);
47+
Assert.That(batch, Is.Empty, "Number of batches");
48+
}
49+
50+
[Test]
51+
[TestCase(1)]
52+
[TestCase(9)]
53+
[TestCase(10)]
54+
public void Test_one_batch(int itemCount)
55+
{
56+
var items = Enumerable.Range(0, itemCount).ToArray();
57+
var batches = items.Batch(10);
58+
var enumerator = batches.GetEnumerator();
59+
Assert.That(enumerator.MoveNext(), Is.True, "At least one batch");
60+
Assert.That(enumerator.Current, Is.EqualTo(Enumerable.Range(0, itemCount)), "Batch content");
61+
Assert.That(enumerator.MoveNext(), Is.False, "No second batch");
62+
}
63+
64+
[Test]
65+
[TestCase(11)]
66+
[TestCase(19)]
67+
[TestCase(20)]
68+
public void Test_two_batches(int itemCount)
69+
{
70+
var items = Enumerable.Range(0, itemCount).ToArray();
71+
var batches = items.Batch(10);
72+
var enumerator = batches.GetEnumerator();
73+
Assert.That(enumerator.MoveNext(), Is.True, "At least one batch");
74+
Assert.That(enumerator.Current, Is.EqualTo(Enumerable.Range(0, 10)), "First batch content");
75+
Assert.That(enumerator.MoveNext(), Is.True, "At least two batches");
76+
Assert.That(enumerator.Current, Is.EqualTo(Enumerable.Range(10, itemCount - 10)), "Second batch content");
77+
Assert.That(enumerator.MoveNext(), Is.False, "No third batch");
78+
}
79+
80+
public class MultipleEnumerationChecker
81+
{
82+
public int Enumerations { get; private set; } = 0;
83+
84+
public IEnumerable<int> GetItems(int count)
85+
{
86+
Enumerations++;
87+
for (var i = 0; i < count; i++)
88+
yield return i;
89+
}
90+
}
91+
}
92+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
7+
<IsPackable>false</IsPackable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
12+
<PackageReference Include="NUnit" Version="3.13.2" />
13+
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
14+
<PackageReference Include="coverlet.collector" Version="3.1.0" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="..\PSWin.Client\LinkMobility.PSWin.Client.csproj" />
19+
</ItemGroup>
20+
21+
</Project>

LinkMobility.PsWin.Client.sln

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example.Client", "Example.C
99
EndProject
1010
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinkMobility.PSWin.Receiver", "PSWin.Receiver\LinkMobility.PSWin.Receiver.csproj", "{3F783F0E-D2E1-4442-BBB1-80C67B3E0846}"
1111
EndProject
12-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Receiver", "Example.Receiver\Example.Receiver.csproj", "{38C3F27E-2C39-4F82-9FA5-8038C359A89E}"
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example.Receiver", "Example.Receiver\Example.Receiver.csproj", "{38C3F27E-2C39-4F82-9FA5-8038C359A89E}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinkMobility.PSWin.Client.Tests", "LinkMobility.PSWin.Client.Tests\LinkMobility.PSWin.Client.Tests.csproj", "{ADB9FBA1-3DC0-4C5F-AB53-11E1732E74E2}"
1315
EndProject
1416
Global
1517
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -33,6 +35,10 @@ Global
3335
{38C3F27E-2C39-4F82-9FA5-8038C359A89E}.Debug|Any CPU.Build.0 = Debug|Any CPU
3436
{38C3F27E-2C39-4F82-9FA5-8038C359A89E}.Release|Any CPU.ActiveCfg = Release|Any CPU
3537
{38C3F27E-2C39-4F82-9FA5-8038C359A89E}.Release|Any CPU.Build.0 = Release|Any CPU
38+
{ADB9FBA1-3DC0-4C5F-AB53-11E1732E74E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39+
{ADB9FBA1-3DC0-4C5F-AB53-11E1732E74E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
40+
{ADB9FBA1-3DC0-4C5F-AB53-11E1732E74E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{ADB9FBA1-3DC0-4C5F-AB53-11E1732E74E2}.Release|Any CPU.Build.0 = Release|Any CPU
3642
EndGlobalSection
3743
GlobalSection(SolutionProperties) = preSolution
3844
HideSolutionNode = FALSE

PSWin.Client/GatewayClient.cs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,66 @@
77

88
namespace LinkMobility.PSWin.Client
99
{
10+
/// <summary>
11+
///
12+
/// </summary>
1013
public class GatewayClient
1114
{
12-
public const uint BatchSize = 150;
13-
1415
private readonly ITransport transport;
1516

17+
/// <summary>
18+
/// Initializes a client that sends messages using the default endpoint and transport.
19+
/// </summary>
20+
/// <param name="username">The username assigned to you by Link Mobility.</param>
21+
/// <param name="password">The password assigned to you by Link Mobility.</param>
1622
public GatewayClient(string username, string password) : this(new XmlTransport(username, password))
1723
{
1824
}
1925

26+
/// <summary>
27+
/// Initialize a client using the given transport.
28+
/// </summary>
29+
/// <param name="transport">The transport to use.</param>
2030
public GatewayClient(ITransport transport)
2131
{
2232
this.transport = transport;
2333
}
2434

35+
/// <summary>
36+
/// Send a single message.
37+
/// Using <see cref="SendAsync(IEnumerable{Sms}, string)"/> is more efficient if you need to send multiple messages.
38+
/// </summary>
39+
/// <seealso cref="SendAsync(IEnumerable{Sms}, string)"/>
40+
/// <param name="message">The message to send.</param>
41+
/// <param name="sessionData">See <see cref="SendAsync(IEnumerable{Sms}, string)"/>.</param>
42+
/// <returns>See <see cref="SendAsync(IEnumerable{Sms}, string)"/>.</returns>
2543
public async Task<MessageResult> SendAsync(Sms message, string sessionData = null)
2644
{
2745
return (await transport.SendAsync(new Sms[] { message }, sessionData)).Single();
2846
}
2947

48+
/// <summary>
49+
/// Send multiple Sms messages to the endpoint and retreive the results.
50+
/// </summary>
51+
/// <seealso cref="SendAsync(Sms, string)"/>
52+
/// <param name="messages">
53+
/// The messages to send.
54+
/// Messages are automatically split into batches to avoid hitting the POST body size limit.
55+
/// It is however not recommended that you send large message bulks with this client.
56+
/// If you need to do so, please contact support instead.
57+
/// </param>
58+
/// <param name="sessionData">
59+
/// A free text field that can be used to tag the session with customer specific data such as the application name, username, reference-id etc.
60+
/// The maximum length is 200 characters.
61+
/// Leave empty unless required.
62+
/// If you need aggregated reports based on Session Data values​​, please contact support to get this enabled.
63+
/// </param>
64+
/// <returns>
65+
/// The immediate response to the sent messages, in the same order as <paramref name="messages"/>.
66+
/// The response does not indicate whether the message was actually delivered.
67+
/// To get delivery status you need to have delivery reports enabled on your account,
68+
/// and receive them with LinkMobility.PSWin.Receiver.
69+
/// </returns>
3070
public async Task<IEnumerable<MessageResult>> SendAsync(IEnumerable<Sms> messages, string sessionData = null)
3171
{
3272
return await transport.SendAsync(messages, sessionData);

PSWin.Client/Interfaces/ITransport.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,20 @@
44

55
namespace LinkMobility.PSWin.Client.Interfaces
66
{
7+
/// <summary>
8+
/// The interface for an implementation of a PSWin gateway API.
9+
/// </summary>
710
public interface ITransport
811
{
12+
/// <summary>
13+
/// Send text messages to the API that this transport implements.
14+
/// </summary>
15+
/// <param name="messageBatch">The messages to send.</param>
16+
/// <param name="sessionData">
17+
/// A free text field that can be used to tag the session/messages with customer specific data such as the application name, username, reference-id etc.
18+
/// The maximum length is 200 characters.
19+
/// </param>
20+
/// <returns>A <see cref="MessageResult"/> for each message in <paramref name="messageBatch"/>, in the same order.</returns>
921
Task<IEnumerable<MessageResult>> SendAsync(IEnumerable<Sms> messageBatch, string sessionData);
1022
}
1123
}

PSWin.Client/LinkMobility.PSWin.Client.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
<PackageId>LinkMobilityNE.PSWin.Client</PackageId>
1919
<GenerateDocumentationFile>True</GenerateDocumentationFile>
2020
</PropertyGroup>
21+
22+
<ItemGroup>
23+
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
24+
<_Parameter1>$(AssemblyName).Tests</_Parameter1>
25+
</AssemblyAttribute>
26+
</ItemGroup>
2127

2228
<ItemGroup>
2329
<None Include="..\linklogo128.png">

PSWin.Client/Model/MessageResult.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
namespace LinkMobility.PSWin.Client.Model
22
{
3+
/// <summary>
4+
/// The response from sending a message to the PSWin gateway.
5+
/// </summary>
36
public class MessageResult
47
{
8+
/// <summary>
9+
/// Initialize all the fields of the response.
10+
/// </summary>
511
public MessageResult(string gatewayReference, bool isStatusOk, string statusText, Sms message)
612
{
713
this.GatewayReference = gatewayReference;
@@ -10,9 +16,30 @@ public MessageResult(string gatewayReference, bool isStatusOk, string statusText
1016
this.Message = message;
1117
}
1218

19+
/// <summary>
20+
/// The unique ID assigned to the message by the gateway,
21+
/// if references are enabled on the account.
22+
/// Otherwise null.
23+
/// The same reference is included in delivery reports.
24+
/// </summary>
1325
public string GatewayReference { get; }
26+
27+
/// <summary>
28+
/// The message passed initial checks and was sent further down the pipeline.
29+
/// Does not indicate whether the message actually was or could be delivered.
30+
/// This information is available through delivery reports.
31+
/// </summary>
1432
public bool IsStatusOk { get; }
33+
34+
/// <summary>
35+
/// When <see cref="IsStatusOk"/> is false,
36+
/// this contains a short description of the problem.
37+
/// </summary>
1538
public string StatusText { get; }
39+
40+
/// <summary>
41+
/// The message that this result belongs to.
42+
/// </summary>
1643
public Sms Message { get; }
1744
}
1845
}

PSWin.Client/Model/Sms.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ namespace LinkMobility.PSWin.Client.Model
77
/// </summary>
88
public class Sms
99
{
10+
/// <summary>
11+
/// Initialize a text message and set the required properties.
12+
/// There are a number of additional optional fields that can be set after the message is created.
13+
/// </summary>
14+
/// <param name="senderNumber">Sets the <see cref="SenderNumber"/> property.</param>
15+
/// <param name="receiverNumber">Sets the <see cref="ReceiverNumber"/> property.</param>
16+
/// <param name="text">Sets the <see cref="Text"/> property.</param>
1017
public Sms(string senderNumber, string receiverNumber, string text)
1118
{
1219
SenderNumber = senderNumber;
@@ -27,12 +34,14 @@ public Sms(string senderNumber, string receiverNumber, string text)
2734
/// A string up to 11 characters long, containing only spaces and the following characters: A-Z, a-z, 0-9 and <![CDATA[!”#%&’()*+-./><;]]>
2835
/// </description></item>
2936
/// </list>
37+
/// May not be null.
3038
/// </summary>
3139
public string SenderNumber { get; set; }
3240

3341
/// <summary>
3442
/// The receiver of the message.
3543
/// Must be a valid MSISDN. No spaces or international call prefix.
44+
/// May not be null.
3645
/// </summary>
3746
public string ReceiverNumber { get; set; }
3847

@@ -43,30 +52,35 @@ public Sms(string senderNumber, string receiverNumber, string text)
4352
/// Thus, the maximum length is 16*134=2144 characters. This is done automatically by the SMS Gateway.
4453
/// Text messages of more than 2144 characters will be truncated.
4554
/// Please note that only characters defined in the GSM-7 basic character set is allowed for messages of <see cref="Type"/> Text.
55+
/// May not be null.
4656
/// </summary>
4757
public string Text { get; set; }
4858

4959
/// <summary>
5060
/// Send a message as a Flash (a.k.a. Class 0) message.
61+
/// Default is false.
5162
/// </summary>
5263
public bool IsFlashMessage { get; set; }
5364

5465
/// <summary>
5566
/// Indicates a set of messages that can replace each other.
5667
/// This parameter can be used to specify that the message should replace a previous message with the same set-number in the Inbox of the handset.
68+
/// Default is null.
5769
/// </summary>
5870
public Replace? Replace { get; set; }
5971

6072
/// <summary>
61-
/// The PSWinCom Gateway supports billing using mobile phones in Norway.
62-
/// As opposed to traditional CPA/Premium SMS which can only be used to bill mobile content, CPA GAS can only be used to bill goods and services.
73+
/// The PSWin gateway supports billing using mobile phones in Norway.
74+
/// As opposed to traditional CPA/Premium SMS which can only be used to bill mobile content, this can only be used to bill goods and services (CPA GAS).
75+
/// Default is null.
6376
/// </summary>
6477
public Payment Payment { get; set; }
6578

6679
/// <summary>
6780
/// Specifies the number of minutes this message will be valid.
6881
/// The time is counted from the moment the message has been received and stored on PSWinCom Gateway.
6982
/// After the time has elapsed, the message will not be sent to the operator.
83+
/// Default is null, which means the message is valid indefinitely.
7084
/// </summary>
7185
public TimeSpan? TimeToLive { get; set; }
7286

@@ -75,15 +89,20 @@ public Sms(string senderNumber, string receiverNumber, string text)
7589
/// If this parameter is present the message will be considered to be a deferred message that will be queued for future delivery instead of immediately being forwarded to operator.
7690
/// Maximum delay of message is currently one week (7 days).
7791
/// The Gateway account must be provisioned to use this feature.
92+
/// Default is null, which means the message is delivered as soon as possible.
7893
/// </summary>
7994
public DateTime? DeliveryTime { get; set; }
8095

8196
/// <summary>
8297
/// The type of message content.
98+
/// Default is null, which means the same as <see cref="MessageType.Text"/>.
8399
/// </summary>
84100
public MessageType? Type { get; set; }
85101
}
86102

103+
/// <summary>
104+
/// The type of message.
105+
/// </summary>
87106
public enum MessageType
88107
{
89108
/// <summary>
@@ -98,15 +117,20 @@ public enum MessageType
98117
Unicode = 9,
99118
}
100119

120+
/// <summary>
121+
/// The available replacement groups.
122+
/// </summary>
101123
public enum Replace
102124
{
125+
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
103126
Set1 = 1,
104127
Set2 = 2,
105128
Set3 = 3,
106129
Set4 = 4,
107130
Set5 = 5,
108131
Set6 = 6,
109132
Set7 = 7
133+
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
110134
}
111135
}
112136

0 commit comments

Comments
 (0)