Skip to content

Commit

Permalink
feat: implement deal list [#74]
Browse files Browse the repository at this point in the history
* Implement Deal List
* Added missing interface for ListAsync, fixed doc
* Deal amount should be decimal not int
  • Loading branch information
LeeStevens318 authored May 12, 2021
1 parent c33954b commit 3afb06e
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/Company/HubSpotCompanyClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public HubSpotCompanyClient(string apiKey)
/// <returns></returns>
public async Task<T> ListAsync<T>(CompanyListRequestOptions opts = null) where T : IHubSpotEntity, new()
{
Logger.LogDebug("Contact ListAsync");
Logger.LogDebug("Company ListAsync");
if (opts == null)
{
opts = new CompanyListRequestOptions();
Expand Down
45 changes: 45 additions & 0 deletions src/Deal/DealListRequestOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;

namespace Skarp.HubSpotClient.Deal
{
public class DealListRequestOptions
{
private int _numberOfDealsToReturn = 100;

/// <summary>
/// Gets or sets the number of deals to return.
/// </summary>
/// <remarks>
/// Defaults to 20 which is also the hubspot api default. Max value is 100
/// </remarks>
/// <value>
/// The number of deals to return.
/// </value>
public int NumberOfDealsToReturn
{
get => _numberOfDealsToReturn;
set
{
if (value < 1 || value > 250)
{
throw new ArgumentException(
$"Number of deals to return must be a positive integer greater than 0 and less than 251 - you provided {value}");
}
_numberOfDealsToReturn = value;
}
}

/// <summary>
/// Get or set the continuation offset when calling list many times to enumerate all your deals
/// </summary>
/// <remarks>
/// The return DTO from List contains the current "offset" that you can inject into your next list call
/// to continue the listing process
/// </remarks>
public long? DealOffset { get; set; } = null;

public List<string> PropertiesToInclude { get; set; } = new List<string>();

}
}
2 changes: 1 addition & 1 deletion src/Deal/Dto/DealHubSpotEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public DealHubSpotEntity()
[DataMember(Name = "closedate")]
public string CloseDate { get; set; }
[DataMember(Name = "amount")]
public int Amount { get; set; }
public decimal Amount { get; set; }
[DataMember(Name = "dealtype")]
public string DealType { get; set; }
[IgnoreDataMember]
Expand Down
62 changes: 62 additions & 0 deletions src/Deal/Dto/DealListHubSpotEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
using Skarp.HubSpotClient.Deal.Interfaces;

namespace Skarp.HubSpotClient.Deal.Dto
{
[DataContract]
public class DealListHubSpotEntity<T> : IDealListHubSpotEntity<T> where T : IDealHubSpotEntity
{
/// <inheritdoc />
/// <summary>
/// Gets or sets the deals.
/// </summary>
/// <value>
/// The deals.
/// </value>
[DataMember(Name = "deals")]
public IList<T> Deals { get; set; } = new List<T>();

/// <inheritdoc />
/// <summary>
/// Gets or sets a value indicating whether more results are available.
/// </summary>
/// <value>
/// <c>true</c> if [more results available]; otherwise, <c>false</c>.
/// </value>
/// <remarks>
/// This is a mapping of the "hasMore" prop in the JSON return data from HubSpot
/// </remarks>
[DataMember(Name = "hasMore")]
public bool MoreResultsAvailable { get; set; }

/// <inheritdoc />
/// <summary>
/// Gets or sets the continuation offset.
/// </summary>
/// <value>
/// The continuation offset.
/// </value>
/// <remarks>
/// This is a mapping of the "offset" prop in the JSON return data from HubSpot
/// </remarks>
[DataMember(Name = "offset")]
public long ContinuationOffset { get; set; }

public string RouteBasePath => "/deals/v1";

public bool IsNameValue => false;

public List<string> PropertiesToInclude { get; set; } = new List<string>();

public virtual void ToHubSpotDataEntity(ref dynamic converted)
{

}

public virtual void FromHubSpotDataEntity(dynamic hubspotData)
{

}
}
}
32 changes: 30 additions & 2 deletions src/Deal/HubSpotDealClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
using Skarp.HubSpotClient.Deal.Dto;
using Skarp.HubSpotClient.Deal.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Flurl;

namespace Skarp.HubSpotClient.Deal
{
Expand Down Expand Up @@ -80,6 +80,32 @@ public HubSpotDealClient(string apiKey)
return data;
}

/// <summary>
/// List Deals
/// </summary>
/// <param name="opts">Request options - use for pagination</param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public async Task<T> ListAsync<T>(DealListRequestOptions opts = null) where T : IHubSpotEntity, new()
{
Logger.LogDebug("Deal ListAsync");
if (opts == null)
{
opts = new DealListRequestOptions();
}
var path = PathResolver(new DealHubSpotEntity(), HubSpotAction.List)
.SetQueryParam("limit", opts.NumberOfDealsToReturn);
if (opts.DealOffset.HasValue)
{
path = path.SetQueryParam("offset", opts.DealOffset);
}
if (opts.PropertiesToInclude.Any())
path.SetQueryParam("properties", opts.PropertiesToInclude);

var data = await ListAsync<T>(path);
return data;
}

public async Task<T> UpdateAsync<T>(IDealHubSpotEntity entity) where T : IHubSpotEntity, new()
{
Logger.LogDebug("Deal update w. id: {0}", entity.Id);
Expand Down Expand Up @@ -119,6 +145,8 @@ public string PathResolver(IDealHubSpotEntity entity, HubSpotAction action)
return $"{entity.RouteBasePath}/deal";
case HubSpotAction.Get:
return $"{entity.RouteBasePath}/deal/:dealId:";
case HubSpotAction.List:
return $"{entity.RouteBasePath}/deal/paged";
case HubSpotAction.Update:
return $"{entity.RouteBasePath}/deal/:dealId:";
case HubSpotAction.Delete:
Expand Down
2 changes: 1 addition & 1 deletion src/Deal/Interfaces/IDealHubSpotEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface IDealHubSpotEntity : IHubSpotEntity
string Pipeline { get; set; }
string OwnerId { get; set; }
string CloseDate { get; set; }
int Amount { get; set; }
decimal Amount { get; set; }
string DealType { get; set; }
string RouteBasePath { get; }
}
Expand Down
42 changes: 42 additions & 0 deletions src/Deal/Interfaces/IDealListHubSpotEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Collections.Generic;
using Skarp.HubSpotClient.Core.Interfaces;

namespace Skarp.HubSpotClient.Deal.Interfaces
{
public interface IDealListHubSpotEntity<T> : IHubSpotEntity where T : IDealHubSpotEntity
{
/// <summary>
/// Gets or sets the deals.
/// </summary>
/// <value>
/// The deals.
/// </value>
IList<T> Deals { get; set; }

/// <summary>
/// Gets or sets a value indicating whether more results are available.
/// </summary>
/// <remarks>
/// This is a mapping of the "hasMore" prop in the JSON return data from HubSpot
/// </remarks>
/// <value>
/// <c>true</c> if [more results available]; otherwise, <c>false</c>.
/// </value>
bool MoreResultsAvailable { get; set; }

/// <summary>
/// Gets or sets the continuation offset.
/// </summary>
/// <remarks>
/// This is a mapping of the "vid-offset" prop in the JSON reeturn data from HubSpot
/// </remarks>
/// <value>
/// The continuation offset.
/// </value>
long ContinuationOffset { get; set; }

string RouteBasePath { get; }

List<string> PropertiesToInclude { get; set; }
}
}
7 changes: 7 additions & 0 deletions src/Deal/Interfaces/IHubSpotDealClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ public interface IHubSpotDealClient
/// <returns></returns>
Task<T> GetByIdAsync<T>(long dealId) where T : IHubSpotEntity, new();
/// <summary>
/// List Deals
/// </summary>
/// <param name="opts">Request options - use for pagination</param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
Task<T> ListAsync<T>(DealListRequestOptions opts = null) where T : IHubSpotEntity, new();
/// <summary>
/// Update an existing deal in hubspot
/// </summary>
/// <typeparam name="T"></typeparam>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestPlatform.Utilities;
using RapidCore.Network;
using Skarp.HubSpotClient.Contact;
using Skarp.HubSpotClient.Contact.Dto;
Expand Down
50 changes: 50 additions & 0 deletions test/integration/Deal/HubSpotDealClientIntegrationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using RapidCore.Network;
using Skarp.HubSpotClient.Core.Requests;
using Skarp.HubSpotClient.Deal;
using Skarp.HubSpotClient.Deal.Dto;
using Xunit;
using Xunit.Abstractions;

namespace integration.Deal
{
public class HubSpotDealClientIntegrationTest : IntegrationTestBase<HubSpotDealClient>
{
private readonly HubSpotDealClient _client;
private readonly string _apiKey;
private readonly bool _isAppVeyorEnv;

public HubSpotDealClientIntegrationTest(ITestOutputHelper output) : base(output)
{
_apiKey = Environment.GetEnvironmentVariable("HUBSPOT_API_KEY") ?? "demo";
_isAppVeyorEnv = (Environment.GetEnvironmentVariable("APPVEYOR") ?? "false").Equals("true", StringComparison.InvariantCultureIgnoreCase);
; _client = new HubSpotDealClient(
new RealRapidHttpClient(new HttpClient()),
base.Logger,
new RequestSerializer(new RequestDataConverter(LoggerFactory.CreateLogger<RequestDataConverter>())),
"https://api.hubapi.com",
_apiKey
);
}

[Fact]
public async Task List()
{
var deals =
await _client.ListAsync<DealListHubSpotEntity<DealHubSpotEntity>>(new DealListRequestOptions
{
PropertiesToInclude = new List<string> { "dealname", "amount" },
NumberOfDealsToReturn = 2
});

Assert.NotNull(deals);
Assert.NotNull(deals.Deals);
Assert.NotEmpty(deals.Deals);
Assert.True(deals.Deals.Count > 1, "contacts.Deals.Count > 1");
}
}
}
19 changes: 16 additions & 3 deletions test/unit/Deal/HubSpotDealClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
using Skarp.HubSpotClient.Core;
using Skarp.HubSpotClient.Core.Interfaces;
using Skarp.HubSpotClient.Core.Requests;
using Xunit;
using Xunit.Abstractions;
using Skarp.HubSpotClient.Deal;
using Skarp.HubSpotClient.Deal.Dto;
using Xunit;
using Xunit.Abstractions;

namespace Skarp.HubSpotClient.UnitTest.Contact
namespace Skarp.HubSpotClient.UnitTest.Deal
{
public class HubSpotDealClientTest : UnitTestBase<HubSpotDealClient>
{
Expand All @@ -33,6 +33,9 @@ public HubSpotDealClientTest(ITestOutputHelper output) : base(output)
A.CallTo(() => _mockSerializer.DeserializeEntity<DealHubSpotEntity>(A<string>.Ignored))
.Returns(new DealHubSpotEntity());

A.CallTo(() => _mockSerializer.DeserializeListEntity<DealListHubSpotEntity<DealHubSpotEntity>>(A<string>.Ignored))
.Returns(new DealListHubSpotEntity<DealHubSpotEntity>());

_client = new HubSpotDealClient(
_mockHttpClient,
Logger,
Expand All @@ -56,6 +59,7 @@ private HttpResponseMessage CreateNewEmptyOkResponse()
[InlineData(HubSpotAction.Get, "/deals/v1/deal/:dealId:")]
[InlineData(HubSpotAction.Update, "/deals/v1/deal/:dealId:")]
[InlineData(HubSpotAction.Delete, "/deals/v1/deal/:dealId:")]
[InlineData(HubSpotAction.List, "/deals/v1/deal/paged")]
public void DealClient_path_resolver_works(HubSpotAction action, string expetedPath)
{
var resvoledPath = _client.PathResolver(new DealHubSpotEntity(), action);
Expand All @@ -78,5 +82,14 @@ public async Task DealClient_create_contact_work()
A.CallTo(() => _mockSerializer.DeserializeEntity<DealHubSpotEntity>("{}")).MustHaveHappened();
}

[Fact]
public async Task DealClient_list_work()
{
var response = await _client.ListAsync<DealListHubSpotEntity<DealHubSpotEntity>>();

A.CallTo(() => _mockHttpClient.SendAsync(A<HttpRequestMessage>.Ignored)).MustHaveHappened();
//A.CallTo(() => _mockSerializer.SerializeEntity(A<IHubSpotEntity>.Ignored)).MustHaveHappened();
A.CallTo(() => _mockSerializer.DeserializeListEntity<DealListHubSpotEntity<DealHubSpotEntity>>("{}")).MustHaveHappened();
}
}
}

0 comments on commit 3afb06e

Please sign in to comment.