Skip to content

Commit

Permalink
feat: proactive connect - lists (#395)
Browse files Browse the repository at this point in the history
* Creating file structure

* Implement GetLists

* Add missing response

* Implement CreateListRequestBuilder

* Implement CreateList

* Implement serialization test for CreateList

* Reuse same struct for a list response

* Implement GetList

* Implement DeleteList

* Implement ClearListRequest

* Implement ReplaceItemsRequest

* Implement UpdateListRequest

* Remove wrong property

* Exposes ProactiveConnectClient
  • Loading branch information
Tr00d authored Apr 25, 2023
1 parent 980bff4 commit 18490c2
Show file tree
Hide file tree
Showing 67 changed files with 3,206 additions and 85 deletions.
42 changes: 42 additions & 0 deletions Vonage.Common/HalLink.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Text.Json.Serialization;

namespace Vonage.Common;

/// <summary>
/// Represents a set of HAL Links.
/// </summary>
public struct HalLinks
{
/// <summary>
/// Represents the navigation link to the first element.
/// </summary>
public HalLink First { get; set; }

/// <summary>
/// Represents the navigation link to the last element.
/// </summary>
public HalLink Last { get; set; }

/// <summary>
/// Represents the navigation link to the next element.
/// </summary>
public HalLink Next { get; set; }

/// <summary>
/// Represents the navigation link to the previous element.
/// </summary>
[JsonPropertyName("prev")]
public HalLink Previous { get; set; }

/// <summary>
/// Represents the navigation link to the current element.
/// </summary>
public HalLink Self { get; set; }
}

/// <summary>
/// Represents a link to another page.
/// </summary>
/// <param name="Href">Hyperlink reference.</param>
public record HalLink(Uri Href);
5 changes: 2 additions & 3 deletions Vonage.Common/PhoneNumber.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public readonly struct PhoneNumber
private const int MinimumLength = 7;
private const string InternationalIndicator = "+";
private const string MustContainDigits = "Number can only contain digits.";
private const string NumberLengthIdentifier = "Number length";

private PhoneNumber(string number) => this.Number = number;

Expand Down Expand Up @@ -59,12 +58,12 @@ private static Result<PhoneNumber> VerifyDigitsOnly(
private static Result<PhoneNumber> VerifyLengthHigherThanMinimum(
PhoneNumber request) =>
InputValidation
.VerifyHigherOrEqualThan(request, request.Number.Length, MinimumLength, NumberLengthIdentifier);
.VerifyLengthHigherOrEqualThan(request, request.Number, MinimumLength, nameof(request.Number));

private static Result<PhoneNumber> VerifyLengthLowerThanMaximum(
PhoneNumber request) =>
InputValidation
.VerifyLowerOrEqualThan(request, request.Number.Length, MaximumLength, NumberLengthIdentifier);
.VerifyLengthLowerOrEqualThan(request, request.Number, MaximumLength, nameof(request.Number));

private static Result<PhoneNumber> VerifyNumberNotEmpty(PhoneNumber number) =>
InputValidation
Expand Down
59 changes: 55 additions & 4 deletions Vonage.Common/Validation/InputValidation.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Vonage.Common.Failures;
using Vonage.Common.Monads;

Expand All @@ -10,14 +11,32 @@ namespace Vonage.Common.Validation;
/// </summary>
public static class InputValidation
{
private const string CannotBeHigherThan = "cannot be higher than {value}.";
private const string CannotBeLowerThan = "cannot be lower than {value}.";
private const string CollectionCannotBeNull = "cannot be null.";
private const string GuidCannotBeNullOrWhitespace = "cannot be empty.";
private const string IntCannotBeHigherThan = "cannot be higher than {value}.";
private const string IntCannotBeLowerThan = "cannot be lower than {value}.";
private const string IntCannotBeNegative = "cannot be negative.";
private const string StringCannotBeNullOrWhitespace = "cannot be null or whitespace.";
private const string UnexpectedLength = "length should be {value}.";

/// <summary>
/// Verifies if count lower or equal than specified threshold.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="value">The value.</param>
/// <param name="maximumCount">The threshold.</param>
/// <param name="name">The display name.</param>
/// <typeparam name="T">The request type.</typeparam>
/// <typeparam name="TItem">The collection item type.</typeparam>
/// <returns>Success or Failure.</returns>
public static Result<T> VerifyCountLowerOrEqualThan<T, TItem>(T request, IEnumerable<TItem> value, int maximumCount,
string name) =>
value.Count() > maximumCount
? Result<T>.FromFailure(
ResultFailure.FromErrorMessage(
$"{name} count {CannotBeHigherThan.Replace("{value}", maximumCount.ToString())}"))
: request;

/// <summary>
/// Verifies if higher or equal than specified threshold.
/// </summary>
Expand All @@ -31,7 +50,7 @@ public static Result<T> VerifyHigherOrEqualThan<T>(T request, int value, int min
value < minValue
? Result<T>.FromFailure(
ResultFailure.FromErrorMessage(
$"{name} {IntCannotBeLowerThan.Replace("{value}", minValue.ToString())}"))
$"{name} {CannotBeLowerThan.Replace("{value}", minValue.ToString())}"))
: request;

/// <summary>
Expand All @@ -50,6 +69,38 @@ public static Result<T> VerifyLength<T>(T request, string value, int expectedLen
$"{name} {UnexpectedLength.Replace("{value}", expectedLength.ToString())}"))
: request;

/// <summary>
/// Verifies if length higher or equal than specified threshold.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="value">The value.</param>
/// <param name="minimumLength">The threshold.</param>
/// <param name="name">The display name.</param>
/// <typeparam name="T">The request type.</typeparam>
/// <returns>Success or Failure.</returns>
public static Result<T> VerifyLengthHigherOrEqualThan<T>(T request, string value, int minimumLength, string name) =>
value.Length < minimumLength
? Result<T>.FromFailure(
ResultFailure.FromErrorMessage(
$"{name} length {CannotBeLowerThan.Replace("{value}", minimumLength.ToString())}"))
: request;

/// <summary>
/// Verifies if length lower or equal than specified threshold.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="value">The value.</param>
/// <param name="maximumLength">The threshold.</param>
/// <param name="name">The display name.</param>
/// <typeparam name="T">The request type.</typeparam>
/// <returns>Success or Failure.</returns>
public static Result<T> VerifyLengthLowerOrEqualThan<T>(T request, string value, int maximumLength, string name) =>
value?.Length > maximumLength
? Result<T>.FromFailure(
ResultFailure.FromErrorMessage(
$"{name} length {CannotBeHigherThan.Replace("{value}", maximumLength.ToString())}"))
: request;

/// <summary>
/// Verifies if lower or equal than specified threshold.
/// </summary>
Expand All @@ -63,7 +114,7 @@ public static Result<T> VerifyLowerOrEqualThan<T>(T request, int value, int maxV
value > maxValue
? Result<T>.FromFailure(
ResultFailure.FromErrorMessage(
$"{name} {IntCannotBeHigherThan.Replace("{value}", maxValue.ToString())}"))
$"{name} {CannotBeHigherThan.Replace("{value}", maxValue.ToString())}"))
: request;

/// <summary>
Expand Down
4 changes: 0 additions & 4 deletions Vonage.Server.Test/Vonage.Server.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,4 @@
</Content>
</ItemGroup>

<ItemGroup>
<Folder Include="Video\Sessions\Common\"/>
</ItemGroup>

</Project>
4 changes: 2 additions & 2 deletions Vonage.Test.Unit/Meetings/CreateRoom/RequestBuilderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void Build_ShouldReturnFailure_GivenDisplayNameExceeds200Length() =>
.WithDisplayName(StringHelper.GenerateString(201))
.Create()
.Should()
.BeFailure(ResultFailure.FromErrorMessage("DisplayName cannot be higher than 200."));
.BeFailure(ResultFailure.FromErrorMessage("DisplayName length cannot be higher than 200."));

[Theory]
[InlineData("")]
Expand All @@ -89,7 +89,7 @@ public void Build_ShouldReturnFailure_GivenMetadataExceeds500Length() =>
.WithMetadata(StringHelper.GenerateString(501))
.Create()
.Should()
.BeFailure(ResultFailure.FromErrorMessage("Metadata cannot be higher than 500."));
.BeFailure(ResultFailure.FromErrorMessage("Metadata length cannot be higher than 500."));

[Fact]
public void Build_ShouldReturnSuccess_() =>
Expand Down
31 changes: 31 additions & 0 deletions Vonage.Test.Unit/ProactiveConnect/Lists/ClearList/RequestTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using Vonage.Common.Failures;
using Vonage.Common.Test.Extensions;
using Vonage.ProactiveConnect.Lists.ClearList;
using Xunit;

namespace Vonage.Test.Unit.ProactiveConnect.Lists.ClearList
{
public class RequestTest
{
[Fact]
public void GetEndpointPath_ShouldReturnApiEndpoint() =>
ClearListRequest.Parse(new Guid("de51fd37-551c-45f1-8eaf-0fcd75c0bbc8"))
.Map(request => request.GetEndpointPath())
.Should()
.BeSuccess("/v.01/bulk/lists/de51fd37-551c-45f1-8eaf-0fcd75c0bbc8/clear");

[Fact]
public void Parse_ShouldReturnFailure_GivenIdIsEmpty() =>
ClearListRequest.Parse(Guid.Empty)
.Should()
.BeFailure(ResultFailure.FromErrorMessage("Id cannot be empty."));

[Fact]
public void Parse_ShouldReturnSuccess() =>
ClearListRequest.Parse(new Guid("de51fd37-551c-45f1-8eaf-0fcd75c0bbc8"))
.Map(request => request.Id)
.Should()
.BeSuccess(new Guid("de51fd37-551c-45f1-8eaf-0fcd75c0bbc8"));
}
}
60 changes: 60 additions & 0 deletions Vonage.Test.Unit/ProactiveConnect/Lists/ClearList/UseCaseTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using AutoFixture;
using AutoFixture.Kernel;
using FsCheck;
using FsCheck.Xunit;
using Vonage.Common.Client;
using Vonage.Common.Monads;
using Vonage.Common.Test;
using Vonage.ProactiveConnect;
using Vonage.ProactiveConnect.Lists.ClearList;
using Xunit;

namespace Vonage.Test.Unit.ProactiveConnect.Lists.ClearList
{
public class UseCaseTest : BaseUseCase
{
private Func<VonageHttpClientConfiguration, Task<Result<Common.Monads.Unit>>> Operation =>
configuration => new ProactiveConnectClient(configuration).ClearListAsync(this.request);

private readonly Result<ClearListRequest> request;

public UseCaseTest() => this.request = BuildRequest(this.helper.Fixture);

[Property]
public Property ShouldReturnFailure_GivenApiErrorCannotBeParsed() =>
this.helper.VerifyReturnsFailureGivenErrorCannotBeParsed(this.BuildExpectedRequest(), this.Operation);

[Property]
public Property ShouldReturnFailure_GivenApiResponseIsError() =>
this.helper.VerifyReturnsFailureGivenApiResponseIsError(this.BuildExpectedRequest(), this.Operation);

[Fact]
public async Task ShouldReturnFailure_GivenRequestIsFailure() =>
await this.helper
.VerifyReturnsFailureGivenRequestIsFailure<ClearListRequest, Common.Monads.Unit>(
(configuration, failureRequest) =>
new ProactiveConnectClient(configuration).ClearListAsync(failureRequest));

[Fact]
public async Task ShouldReturnFailure_GivenTokenGenerationFailed() =>
await this.helper.VerifyReturnsFailureGivenTokenGenerationFails(this.Operation);

[Fact]
public async Task ShouldReturnSuccess_GivenApiResponseIsSuccess() =>
await this.helper.VerifyReturnsExpectedValueGivenApiResponseIsSuccess(this.BuildExpectedRequest(),
this.Operation);

private ExpectedRequest BuildExpectedRequest() =>
new ExpectedRequest
{
Method = HttpMethod.Post,
RequestUri = new Uri(UseCaseHelper.GetPathFromRequest(this.request), UriKind.Relative),
};

private static Result<ClearListRequest> BuildRequest(ISpecimenBuilder fixture) =>
ClearListRequest.Parse(fixture.Create<Guid>());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "my name",
"description": "my description",
"tags": [
"vip",
"sport"
],
"attributes": [
{
"name": "phone_number",
"alias": "phone",
"key": false
}
],
"datasource": {
"type": "salesforce",
"integration_id": "salesforce_credentials",
"soql": "some sql"
},
"id": "29192c4a-4058-49da-86c2-3e349d1065b7",
"created_at": "2022-06-19T17:59:28.085Z",
"updated_at": "2022-06-19T17:59:28.085Z",
"sync_status": {
"value": "configured",
"details": "Not found",
"metadata_modified": false,
"data_modified": true,
"dirty": true
},
"items_count": 500
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "my name",
"tags": [],
"attributes": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "my name",
"description": "my description",
"tags": [
"vip",
"sport"
],
"attributes": [
{
"name": "phone_number",
"alias": "phone",
"key": false
}
],
"datasource": {
"type": "manual"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "my name",
"description": "my description",
"tags": [
"vip",
"sport"
],
"attributes": [
{
"name": "phone_number",
"alias": "phone",
"key": false
}
],
"datasource": {
"type": "salesforce",
"integration_id": "123456789",
"soql": "some sql"
}
}
Loading

0 comments on commit 18490c2

Please sign in to comment.