Add "ZonePlans" extensions
This commit is contained in:
@@ -230,7 +230,8 @@ namespace AMWD.Net.Api.Cloudflare
|
||||
var errorResponse = JsonConvert.DeserializeObject<CloudflareResponse<object>>(content, _jsonSerializerSettings)
|
||||
?? throw new CloudflareException("Response is not a valid Cloudflare API response.");
|
||||
|
||||
throw new AuthenticationException(string.Join(Environment.NewLine, errorResponse.Errors.Select(e => $"{e.Code}: {e.Message}")));
|
||||
string[] errors = errorResponse.Errors?.Select(e => $"{e.Code}: {e.Message}").ToArray() ?? [];
|
||||
throw new AuthenticationException(string.Join(Environment.NewLine, errors));
|
||||
|
||||
default:
|
||||
try
|
||||
|
||||
69
Extensions/Cloudflare.Zones/Models/AvailableRatePlan.cs
Normal file
69
Extensions/Cloudflare.Zones/Models/AvailableRatePlan.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
namespace AMWD.Net.Api.Cloudflare.Zones
|
||||
{
|
||||
/// <summary>
|
||||
/// A Cloudflare available plan.
|
||||
/// <see href="https://github.com/cloudflare/cloudflare-typescript/blob/v4.4.1/src/resources/zones/plans.ts#L60">Source</see>
|
||||
/// </summary>
|
||||
public class AvailableRatePlan
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifier.
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether you can subscribe to this plan.
|
||||
/// </summary>
|
||||
[JsonProperty("can_subscribe")]
|
||||
public bool? CanSubscribe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The monetary unit in which pricing information is displayed.
|
||||
/// </summary>
|
||||
[JsonProperty("currency")]
|
||||
public string? Currency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this plan is managed externally.
|
||||
/// </summary>
|
||||
[JsonProperty("externally_managed")]
|
||||
public bool? ExternallyManaged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The frequency at which you will be billed for this plan.
|
||||
/// </summary>
|
||||
[JsonProperty("frequency")]
|
||||
public RenewFrequency? Frequency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether you are currently subscribed to this plan.
|
||||
/// </summary>
|
||||
[JsonProperty("is_subscribed")]
|
||||
public bool? IsSubscribed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this plan has a legacy discount applied.
|
||||
/// </summary>
|
||||
[JsonProperty("legacy_discount")]
|
||||
public bool? LegacyDiscount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The legacy identifier for this rate plan, if any.
|
||||
/// </summary>
|
||||
[JsonProperty("legacy_id")]
|
||||
public string? LegacyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The plan name.
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount you will be billed for this plan.
|
||||
/// </summary>
|
||||
[JsonProperty("price")]
|
||||
public decimal? Price { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,16 @@ This package contains the feature set of the _Domain/Zone Management_ section of
|
||||
- [Get Zone Hold](https://developers.cloudflare.com/api/resources/zones/subresources/holds/methods/get/)
|
||||
|
||||
|
||||
##### [Plans]
|
||||
|
||||
- [Available Plan Details](https://developers.cloudflare.com/api/resources/zones/subresources/plans/methods/get/)
|
||||
- [List Available Plans](https://developers.cloudflare.com/api/resources/zones/subresources/plans/methods/list/)
|
||||
|
||||
|
||||
##### [Rate Plans]
|
||||
|
||||
- [List Available Rate Plans](https://developers.cloudflare.com/api/resources/zones/subresources/rate_plans/methods/get/)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -49,3 +59,5 @@ Published under MIT License (see [choose a license])
|
||||
[Zones]: https://developers.cloudflare.com/api/resources/zones/
|
||||
[Activation Check]: https://developers.cloudflare.com/api/resources/zones/subresources/activation_check/
|
||||
[Holds]: https://developers.cloudflare.com/api/resources/zones/subresources/holds/
|
||||
[Plans]: https://developers.cloudflare.com/api/resources/zones/subresources/plans/
|
||||
[Rate Plans]: https://developers.cloudflare.com/api/resources/zones/subresources/rate_plans/
|
||||
|
||||
103
Extensions/Cloudflare.Zones/Responses/RatePlanGetResponse.cs
Normal file
103
Extensions/Cloudflare.Zones/Responses/RatePlanGetResponse.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace AMWD.Net.Api.Cloudflare.Zones
|
||||
{
|
||||
/// <summary>
|
||||
/// <see href="https://github.com/cloudflare/cloudflare-typescript/blob/v4.4.1/src/resources/zones/rate-plans.ts#L36">Source</see>
|
||||
/// </summary>
|
||||
public class RatePlanGetResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Plan identifier tag.
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Array of available components values for the plan.
|
||||
/// </summary>
|
||||
[JsonProperty("components")]
|
||||
public IReadOnlyCollection<Component>? Components { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The monetary unit in which pricing information is displayed.
|
||||
/// </summary>
|
||||
[JsonProperty("currency")]
|
||||
public string? Currency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The duration of the plan subscription.
|
||||
/// </summary>
|
||||
[JsonProperty("duration")]
|
||||
public int? Duration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The frequency at which you will be billed for this plan.
|
||||
/// </summary>
|
||||
[JsonProperty("frequency")]
|
||||
public RenewFrequency? Frequency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The plan name.
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rate plan component.
|
||||
/// </summary>
|
||||
public class Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The default amount allocated.
|
||||
/// </summary>
|
||||
[JsonProperty("default")]
|
||||
public decimal? Default { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique component.
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
public ComponentName? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unit price of the addon.
|
||||
/// </summary>
|
||||
[JsonProperty("unit_price")]
|
||||
public decimal? UnitPrice { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rate plan component name.
|
||||
/// <see href="https://github.com/cloudflare/cloudflare-typescript/blob/v4.4.1/src/resources/zones/rate-plans.ts#L78">Source</see>
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ComponentName
|
||||
{
|
||||
/// <summary>
|
||||
/// Zones
|
||||
/// </summary>
|
||||
[EnumMember(Value = "zones")]
|
||||
Zones = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Page rules
|
||||
/// </summary>
|
||||
[EnumMember(Value = "page_rules")]
|
||||
PageRules = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Dedicated certificates
|
||||
/// </summary>
|
||||
[EnumMember(Value = "dedicated_certificates")]
|
||||
DedicatedCertificatese = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Custom dedicated certificates
|
||||
/// </summary>
|
||||
[EnumMember(Value = "dedicated_certificates_custom")]
|
||||
DedicatedCertificatesCustom = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Extensions/Cloudflare.Zones/ZonePlansExtensions.cs
Normal file
52
Extensions/Cloudflare.Zones/ZonePlansExtensions.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AMWD.Net.Api.Cloudflare.Zones
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for <see href="https://developers.cloudflare.com/api/resources/zones/subresources/plans/">Zone Plans</see>.
|
||||
/// </summary>
|
||||
public static class ZonePlansExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Details of the available plan that the zone can subscribe to.
|
||||
/// </summary>
|
||||
/// <param name="client">The <see cref="ICloudflareClient"/> instance.</param>
|
||||
/// <param name="zoneId">The zone identifier.</param>
|
||||
/// <param name="planId">The plan identifier.</param>
|
||||
/// <param name="cancellationToken">A cancellation token used to propagate notification that this operation should be canceled.</param>
|
||||
public static Task<CloudflareResponse<AvailableRatePlan>> AvailablePlanDetails(this ICloudflareClient client, string zoneId, string planId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
zoneId.ValidateCloudflareId();
|
||||
planId.ValidateCloudflareId();
|
||||
|
||||
return client.GetAsync<AvailableRatePlan>($"/zones/{zoneId}/available_plans/{planId}", cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists available plans the zone can subscribe to.
|
||||
/// </summary>
|
||||
/// <param name="client">The <see cref="ICloudflareClient"/> instance.</param>
|
||||
/// <param name="zoneId">The zone identifier.</param>
|
||||
/// <param name="cancellationToken">A cancellation token used to propagate notification that this operation should be canceled.</param>
|
||||
public static Task<CloudflareResponse<IReadOnlyCollection<AvailableRatePlan>>> ListAvailablePlans(this ICloudflareClient client, string zoneId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
zoneId.ValidateCloudflareId();
|
||||
|
||||
return client.GetAsync<IReadOnlyCollection<AvailableRatePlan>>($"/zones/{zoneId}/available_plans", cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists all rate plans the zone can subscribe to.
|
||||
/// </summary>
|
||||
/// <param name="client">The <see cref="ICloudflareClient"/> instance.</param>
|
||||
/// <param name="zoneId">The zone identifier.</param>
|
||||
/// <param name="cancellationToken">A cancellation token used to propagate notification that this operation should be canceled.</param>
|
||||
public static Task<CloudflareResponse<RatePlanGetResponse>> ListAvailableRatePlans(this ICloudflareClient client, string zoneId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
zoneId.ValidateCloudflareId();
|
||||
|
||||
return client.GetAsync<RatePlanGetResponse>($"/zones/{zoneId}/available_rate_plans", cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AMWD.Net.Api.Cloudflare;
|
||||
using AMWD.Net.Api.Cloudflare.Zones;
|
||||
using Moq;
|
||||
|
||||
namespace Cloudflare.Zones.Tests.ZonePlansExtensions
|
||||
{
|
||||
[TestClass]
|
||||
public class AvailablePlanDetailsTest
|
||||
{
|
||||
private const string ZoneId = "023e105f4ecef8ad9ca31a8372d0c353";
|
||||
private const string PlanId = "023e105f4ecef8ad9ca31a8372d0c354";
|
||||
|
||||
private Mock<ICloudflareClient> _clientMock;
|
||||
|
||||
private CloudflareResponse<AvailableRatePlan> _response;
|
||||
|
||||
private List<(string RequestPath, IQueryParameterFilter QueryFilter)> _callbacks;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
_callbacks = [];
|
||||
|
||||
_response = new CloudflareResponse<AvailableRatePlan>
|
||||
{
|
||||
Success = true,
|
||||
Messages = [
|
||||
new ResponseInfo(1000, "Message 1")
|
||||
],
|
||||
Errors = [
|
||||
new ResponseInfo(1000, "Error 1")
|
||||
],
|
||||
Result = new AvailableRatePlan()
|
||||
};
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldReturnAvailablePlan()
|
||||
{
|
||||
// Arrange
|
||||
var client = GetClient();
|
||||
|
||||
// Act
|
||||
var response = await client.AvailablePlanDetails(ZoneId, PlanId);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(response);
|
||||
Assert.IsTrue(response.Success);
|
||||
Assert.AreEqual(_response.Result, response.Result);
|
||||
|
||||
Assert.AreEqual(1, _callbacks.Count);
|
||||
|
||||
var callback = _callbacks.First();
|
||||
Assert.AreEqual($"/zones/{ZoneId}/available_plans/{PlanId}", callback.RequestPath);
|
||||
Assert.IsNull(callback.QueryFilter);
|
||||
|
||||
_clientMock.Verify(m => m.GetAsync<AvailableRatePlan>($"/zones/{ZoneId}/available_plans/{PlanId}", null, It.IsAny<CancellationToken>()), Times.Once);
|
||||
_clientMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
private ICloudflareClient GetClient()
|
||||
{
|
||||
_clientMock = new Mock<ICloudflareClient>();
|
||||
_clientMock
|
||||
.Setup(m => m.GetAsync<AvailableRatePlan>(It.IsAny<string>(), It.IsAny<IQueryParameterFilter>(), It.IsAny<CancellationToken>()))
|
||||
.Callback<string, IQueryParameterFilter, CancellationToken>((requestPath, queryFilter, _) => _callbacks.Add((requestPath, queryFilter)))
|
||||
.ReturnsAsync(() => _response);
|
||||
|
||||
return _clientMock.Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AMWD.Net.Api.Cloudflare;
|
||||
using AMWD.Net.Api.Cloudflare.Zones;
|
||||
using Moq;
|
||||
|
||||
namespace Cloudflare.Zones.Tests.ZonePlansExtensions
|
||||
{
|
||||
[TestClass]
|
||||
public class ListAvailablePlansTest
|
||||
{
|
||||
private const string ZoneId = "023e105f4ecef8ad9ca31a8372d0c353";
|
||||
|
||||
private Mock<ICloudflareClient> _clientMock;
|
||||
|
||||
private CloudflareResponse<IReadOnlyCollection<AvailableRatePlan>> _response;
|
||||
|
||||
private List<(string RequestPath, IQueryParameterFilter QueryFilter)> _callbacks;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
_callbacks = [];
|
||||
|
||||
_response = new CloudflareResponse<IReadOnlyCollection<AvailableRatePlan>>
|
||||
{
|
||||
Success = true,
|
||||
Messages = [
|
||||
new ResponseInfo(1000, "Message 1")
|
||||
],
|
||||
Errors = [
|
||||
new ResponseInfo(1000, "Error 1")
|
||||
],
|
||||
Result = []
|
||||
};
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldReturnAvailablePlan()
|
||||
{
|
||||
// Arrange
|
||||
var client = GetClient();
|
||||
|
||||
// Act
|
||||
var response = await client.ListAvailablePlans(ZoneId);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(response);
|
||||
Assert.IsTrue(response.Success);
|
||||
Assert.AreEqual(_response.Result, response.Result);
|
||||
|
||||
Assert.AreEqual(1, _callbacks.Count);
|
||||
|
||||
var callback = _callbacks.First();
|
||||
Assert.AreEqual($"/zones/{ZoneId}/available_plans", callback.RequestPath);
|
||||
Assert.IsNull(callback.QueryFilter);
|
||||
|
||||
_clientMock.Verify(m => m.GetAsync<IReadOnlyCollection<AvailableRatePlan>>($"/zones/{ZoneId}/available_plans", null, It.IsAny<CancellationToken>()), Times.Once);
|
||||
_clientMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
private ICloudflareClient GetClient()
|
||||
{
|
||||
_clientMock = new Mock<ICloudflareClient>();
|
||||
_clientMock
|
||||
.Setup(m => m.GetAsync<IReadOnlyCollection<AvailableRatePlan>>(It.IsAny<string>(), It.IsAny<IQueryParameterFilter>(), It.IsAny<CancellationToken>()))
|
||||
.Callback<string, IQueryParameterFilter, CancellationToken>((requestPath, queryFilter, _) => _callbacks.Add((requestPath, queryFilter)))
|
||||
.ReturnsAsync(() => _response);
|
||||
|
||||
return _clientMock.Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AMWD.Net.Api.Cloudflare;
|
||||
using AMWD.Net.Api.Cloudflare.Zones;
|
||||
using Moq;
|
||||
|
||||
namespace Cloudflare.Zones.Tests.ZonePlansExtensions
|
||||
{
|
||||
[TestClass]
|
||||
public class ListAvailableRatePlansTest
|
||||
{
|
||||
private const string ZoneId = "023e105f4ecef8ad9ca31a8372d0c353";
|
||||
|
||||
private Mock<ICloudflareClient> _clientMock;
|
||||
|
||||
private CloudflareResponse<RatePlanGetResponse> _response;
|
||||
|
||||
private List<(string RequestPath, IQueryParameterFilter QueryFilter)> _callbacks;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
_callbacks = [];
|
||||
|
||||
_response = new CloudflareResponse<RatePlanGetResponse>
|
||||
{
|
||||
Success = true,
|
||||
Messages = [
|
||||
new ResponseInfo(1000, "Message 1")
|
||||
],
|
||||
Errors = [
|
||||
new ResponseInfo(1000, "Error 1")
|
||||
],
|
||||
Result = new()
|
||||
};
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldReturnAvailablePlans()
|
||||
{
|
||||
// Arrange
|
||||
var client = GetClient();
|
||||
|
||||
// Act
|
||||
var response = await client.ListAvailableRatePlans(ZoneId);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(response);
|
||||
Assert.IsTrue(response.Success);
|
||||
Assert.AreEqual(_response.Result, response.Result);
|
||||
|
||||
Assert.AreEqual(1, _callbacks.Count);
|
||||
|
||||
var callback = _callbacks.First();
|
||||
Assert.AreEqual($"/zones/{ZoneId}/available_rate_plans", callback.RequestPath);
|
||||
Assert.IsNull(callback.QueryFilter);
|
||||
|
||||
_clientMock.Verify(m => m.GetAsync<RatePlanGetResponse>($"/zones/{ZoneId}/available_rate_plans", null, It.IsAny<CancellationToken>()), Times.Once);
|
||||
_clientMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
private ICloudflareClient GetClient()
|
||||
{
|
||||
_clientMock = new Mock<ICloudflareClient>();
|
||||
_clientMock
|
||||
.Setup(m => m.GetAsync<RatePlanGetResponse>(It.IsAny<string>(), It.IsAny<IQueryParameterFilter>(), It.IsAny<CancellationToken>()))
|
||||
.Callback<string, IQueryParameterFilter, CancellationToken>((requestPath, queryFilter, _) => _callbacks.Add((requestPath, queryFilter)))
|
||||
.ReturnsAsync(() => _response);
|
||||
|
||||
return _clientMock.Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user