Added DNS analytics methods
This commit is contained in:
@@ -46,7 +46,7 @@ default-test:
|
||||
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
|
||||
script:
|
||||
- dotnet test -c Debug --nologo /p:CoverletOutputFormat=Cobertura
|
||||
- /dotnet-tools/reportgenerator "-reports:${CI_PROJECT_DIR}/**/coverage.cobertura.xml" "-targetdir:/reports" -reportType:TextSummary
|
||||
- /dotnet-tools/reportgenerator "-reports:${CI_PROJECT_DIR}/**/coverage.cobertura.xml" "-targetdir:/reports" "-reportType:TextSummary"
|
||||
- cat /reports/Summary.txt
|
||||
artifacts:
|
||||
when: always
|
||||
@@ -105,7 +105,7 @@ core-test:
|
||||
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
|
||||
script:
|
||||
- dotnet test -c Release --nologo /p:CoverletOutputFormat=Cobertura test/Cloudflare.Tests/Cloudflare.Tests.csproj
|
||||
- /dotnet-tools/reportgenerator "-reports:${CI_PROJECT_DIR}/**/coverage.cobertura.xml" "-targetdir:/reports" -reportType:TextSummary
|
||||
- /dotnet-tools/reportgenerator "-reports:${CI_PROJECT_DIR}/**/coverage.cobertura.xml" "-targetdir:/reports" "-reportType:TextSummary"
|
||||
- cat /reports/Summary.txt
|
||||
artifacts:
|
||||
when: always
|
||||
|
||||
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- New automatic documentation generation using docfx.
|
||||
- Additional articles for the documentation.
|
||||
- `DateTime` extensions for ISO 8601 formatting.
|
||||
- DNS Analytics
|
||||
|
||||
|
||||
## [v0.1.0], [zones/v0.1.0], [dns/v0.1.0] - 2025-08-05
|
||||
|
||||
42
src/Extensions/Cloudflare.Dns/DnsAnalyticsExtensions.cs
Normal file
42
src/Extensions/Cloudflare.Dns/DnsAnalyticsExtensions.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AMWD.Net.Api.Cloudflare.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for <see href="https://developers.cloudflare.com/api/resources/dns/subresources/analytics/">DNS Analytics</see>.
|
||||
/// </summary>
|
||||
public static class DnsAnalyticsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves a list of summarised aggregate metrics over a given time period.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See <see href="https://developers.cloudflare.com/dns/reference/analytics-api-properties/">Analytics API properties</see> for detailed information about the available query parameters.
|
||||
/// </remarks>
|
||||
/// <param name="client">The <see cref="ICloudflareClient"/> instance.</param>
|
||||
/// <param name="zoneId">The zone identifier.</param>
|
||||
/// <param name="options">Filter options (optional).</param>
|
||||
/// <param name="cancellationToken">A cancellation token used to propagate notification that this operation should be canceled.</param>
|
||||
public static Task<CloudflareResponse<DnsAnalyticsReport>> GetDnsAnalyticsReport(this ICloudflareClient client, string zoneId, GetDnsAnalyticsReportFilter? options = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
zoneId.ValidateCloudflareId();
|
||||
|
||||
return client.GetAsync<DnsAnalyticsReport>($"/zones/{zoneId}/dns_analytics/report", options, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of aggregate metrics grouped by time interval.
|
||||
/// </summary>
|
||||
/// <param name="client">The <see cref="ICloudflareClient"/> instance.</param>
|
||||
/// <param name="zoneId">The zone identifier.</param>
|
||||
/// <param name="options">Filter options (optional).</param>
|
||||
/// <param name="cancellationToken">A cancellation token used to propagate notification that this operation should be canceled.</param>
|
||||
public static Task<CloudflareResponse<DnsAnalyticsByTime>> GetDnsAnalyticsByTime(this ICloudflareClient client, string zoneId, GetDnsAnalyticsByTimeFilter? options = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
zoneId.ValidateCloudflareId();
|
||||
|
||||
return client.GetAsync<DnsAnalyticsByTime>($"/zones/{zoneId}/dns_analytics/report/bytime", options, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
src/Extensions/Cloudflare.Dns/Enums/TimeDeltaUnit.cs
Normal file
73
src/Extensions/Cloudflare.Dns/Enums/TimeDeltaUnit.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace AMWD.Net.Api.Cloudflare.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// Time delta units.
|
||||
/// <see href="https://github.com/cloudflare/cloudflare-typescript/blob/v4.4.1/src/resources/dns/dns.ts#L103">Source</see>
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum TimeDeltaUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// All time.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "all")]
|
||||
All = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Auto.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "auto")]
|
||||
Auto = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Year.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "year")]
|
||||
Year = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Quarter (3 months).
|
||||
/// </summary>
|
||||
[EnumMember(Value = "quarter")]
|
||||
Quarter = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Month.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "month")]
|
||||
Month = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Week.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "week")]
|
||||
Week = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Day.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "day")]
|
||||
Day = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Hour.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "hour")]
|
||||
Hour = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Dekaminute (10 minutes).
|
||||
/// </summary>
|
||||
[EnumMember(Value = "dekaminute")]
|
||||
DekaMinute = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Minute.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "minute")]
|
||||
Minute = 10
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace AMWD.Net.Api.Cloudflare.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// Filter for DNS analytics report by time.
|
||||
/// </summary>
|
||||
public class GetDnsAnalyticsByTimeFilter : GetDnsAnalyticsReportFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Unit of time to group data by
|
||||
/// </summary>
|
||||
public TimeDeltaUnit? TimeDelta { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IReadOnlyDictionary<string, string> GetQueryParameters()
|
||||
{
|
||||
var dict = base.GetQueryParameters().ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
|
||||
if (TimeDelta.HasValue && Enum.IsDefined(typeof(TimeDeltaUnit), TimeDelta))
|
||||
dict.Add("time_delta", TimeDelta.Value.GetEnumMemberValue()!);
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
namespace AMWD.Net.Api.Cloudflare.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// Filter for DNS analytics report.
|
||||
/// </summary>
|
||||
public class GetDnsAnalyticsReportFilter : IQueryParameterFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// A (comma-separated) list of dimensions to group results by.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Further see: <see href="https://developers.cloudflare.com/dns/reference/analytics-api-properties/#dimensions">Cloudflare Docs</see>.
|
||||
/// </remarks>
|
||||
public IReadOnlyCollection<string>? Dimensions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Segmentation filter in 'attribute operator value' format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Further see: <see href="https://developers.cloudflare.com/dns/reference/analytics-api-properties/#filters">Cloudflare Docs</see>.
|
||||
/// </remarks>
|
||||
public string? Filters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Limit number of returned metrics. (Default: 100.000)
|
||||
/// </summary>
|
||||
public int? Limit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A (comma-separated) list of metrics to query.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string>? Metrics { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Start date and time of requesting data period in ISO 8601 format.
|
||||
/// </summary>
|
||||
public DateTime? Since { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A (comma-separated) list of dimensions to sort by, where each dimension may be prefixed by - (descending) or + (ascending)
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string>? Sort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// End date and time of requesting data period in ISO 8601 format.
|
||||
/// </summary>
|
||||
public DateTime? Until { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual IReadOnlyDictionary<string, string> GetQueryParameters()
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
|
||||
if (Dimensions?.Count > 0)
|
||||
dict.Add("dimensions", string.Join(",", Dimensions));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Filters))
|
||||
dict.Add("filters", Filters!.Trim());
|
||||
|
||||
if (Limit.HasValue && Limit > 0)
|
||||
dict.Add("limit", Limit.Value.ToString());
|
||||
|
||||
if (Metrics?.Count > 0)
|
||||
dict.Add("metrics", string.Join(",", Metrics));
|
||||
|
||||
if (Since.HasValue)
|
||||
dict.Add("since", Since.Value.ToIso8601Format());
|
||||
|
||||
if (Sort?.Count > 0)
|
||||
dict.Add("sort", string.Join(",", Sort));
|
||||
|
||||
if (Until.HasValue)
|
||||
dict.Add("until", Until.Value.ToIso8601Format());
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace AMWD.Net.Api.Cloudflare.Dns
|
||||
namespace AMWD.Net.Api.Cloudflare.Dns
|
||||
{
|
||||
internal class DNSAnalyticsQuery
|
||||
/// <summary>
|
||||
/// The DNS Analytics query.
|
||||
/// </summary>
|
||||
public class DnsAnalyticsQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of dimension names.
|
||||
@@ -35,6 +35,9 @@ namespace AMWD.Net.Api.Cloudflare.Dns
|
||||
[JsonProperty("time_delta")]
|
||||
public TimeDeltaUnit? TimeDelta { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// End date and time of requesting data period.
|
||||
/// </summary>
|
||||
[JsonProperty("until")]
|
||||
public DateTime? Until { get; set; }
|
||||
|
||||
@@ -51,72 +54,4 @@ namespace AMWD.Net.Api.Cloudflare.Dns
|
||||
[JsonProperty("sort")]
|
||||
public IReadOnlyCollection<string>? Sort { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Time delta units.
|
||||
/// <see href="https://github.com/cloudflare/cloudflare-typescript/blob/v4.4.1/src/resources/dns/dns.ts#L103">Source</see>
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum TimeDeltaUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// All time.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "all")]
|
||||
All = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Auto.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "auto")]
|
||||
Auto = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Year.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "year")]
|
||||
Year = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Quarter.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "quarter")]
|
||||
Quarter = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Month.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "month")]
|
||||
Month = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Week.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "week")]
|
||||
Week = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Day.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "day")]
|
||||
Day = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Hour.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "hour")]
|
||||
Hour = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Dekaminute.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "dekaminute")]
|
||||
DekaMinute = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Minute.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "minute")]
|
||||
Minute = 10
|
||||
}
|
||||
}
|
||||
|
||||
77
src/Extensions/Cloudflare.Dns/Models/DnsAnalyticsByTime.cs
Normal file
77
src/Extensions/Cloudflare.Dns/Models/DnsAnalyticsByTime.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
namespace AMWD.Net.Api.Cloudflare.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// Summarised aggregate metrics over a given time period as report.
|
||||
/// </summary>
|
||||
public class DnsAnalyticsByTime
|
||||
{
|
||||
/// <summary>
|
||||
/// Array with one row per combination of dimension values.
|
||||
/// </summary>
|
||||
[JsonProperty("data")]
|
||||
public IReadOnlyCollection<DnsAnalyticsByTimeData>? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of seconds between current time and last processed event, in another words how many seconds of data could be missing.
|
||||
/// </summary>
|
||||
[JsonProperty("data_lag")]
|
||||
public int? DataLag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum results for each metric (object mapping metric names to values).
|
||||
/// Currently always an empty object.
|
||||
/// </summary>
|
||||
[JsonProperty("max")]
|
||||
public object? Max { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum results for each metric (object mapping metric names to values).
|
||||
/// Currently always an empty object.
|
||||
/// </summary>
|
||||
[JsonProperty("min")]
|
||||
public object? Min { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The query information.
|
||||
/// </summary>
|
||||
[JsonProperty("query")]
|
||||
public DnsAnalyticsQuery? Query { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total number of rows in the result.
|
||||
/// </summary>
|
||||
[JsonProperty("rows")]
|
||||
public int? Rows { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Array of time intervals in the response data. Each interval is represented as an
|
||||
/// array containing two values: the start time, and the end time.
|
||||
/// </summary>
|
||||
[JsonProperty("time_intervals")]
|
||||
public IReadOnlyCollection<IReadOnlyCollection<string>>? TimeIntervals { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total results for metrics across all data (object mapping metric names to values).
|
||||
/// </summary>
|
||||
[JsonProperty("totals")]
|
||||
public object? Totals { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A combination of dimension values.
|
||||
/// </summary>
|
||||
public class DnsAnalyticsByTimeData
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of dimension values, representing the combination of dimension values corresponding to this row.
|
||||
/// </summary>
|
||||
[JsonProperty("dimensions")]
|
||||
public IReadOnlyCollection<string>? Dimensions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Array with one item per requested metric. Each item is an array of values, broken down by time interval.
|
||||
/// </summary>
|
||||
[JsonProperty("metrics")]
|
||||
public IReadOnlyCollection<IReadOnlyCollection<object>>? Metrics { get; set; }
|
||||
}
|
||||
}
|
||||
118
src/Extensions/Cloudflare.Dns/Models/DnsAnalyticsReport.cs
Normal file
118
src/Extensions/Cloudflare.Dns/Models/DnsAnalyticsReport.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
namespace AMWD.Net.Api.Cloudflare.Dns
|
||||
{
|
||||
/// <summary>
|
||||
/// Summarised aggregate metrics over a given time period as report.
|
||||
/// </summary>
|
||||
public class DnsAnalyticsReport
|
||||
{
|
||||
/// <summary>
|
||||
/// Array with one row per combination of dimension values.
|
||||
/// </summary>
|
||||
[JsonProperty("data")]
|
||||
public IReadOnlyCollection<DnsAnalyticsReportData>? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of seconds between current time and last processed event, in another words how many seconds of data could be missing.
|
||||
/// </summary>
|
||||
[JsonProperty("data_lag")]
|
||||
public int? DataLag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum results for each metric (object mapping metric names to values).
|
||||
/// Currently always an empty object.
|
||||
/// </summary>
|
||||
[JsonProperty("max")]
|
||||
public object? Max { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum results for each metric (object mapping metric names to values).
|
||||
/// Currently always an empty object.
|
||||
/// </summary>
|
||||
[JsonProperty("min")]
|
||||
public object? Min { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The query information.
|
||||
/// </summary>
|
||||
[JsonProperty("query")]
|
||||
public DnsAnalyticsReportQuery? Query { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total number of rows in the result.
|
||||
/// </summary>
|
||||
[JsonProperty("rows")]
|
||||
public int? Rows { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total results for metrics across all data (object mapping metric names to values).
|
||||
/// </summary>
|
||||
[JsonProperty("totals")]
|
||||
public object? Totals { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A combination of dimension values.
|
||||
/// </summary>
|
||||
public class DnsAnalyticsReportData
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of dimension values, representing the combination of dimension values corresponding to this row.
|
||||
/// </summary>
|
||||
[JsonProperty("dimensions")]
|
||||
public IReadOnlyCollection<string>? Dimensions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Array with one item per requested metric. Each item is a single value.
|
||||
/// </summary>
|
||||
[JsonProperty("metrics")]
|
||||
public IReadOnlyCollection<int>? Metrics { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The query information.
|
||||
/// </summary>
|
||||
public class DnsAnalyticsReportQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of dimension names.
|
||||
/// </summary>
|
||||
[JsonProperty("dimensions")]
|
||||
public IReadOnlyCollection<string>? Dimensions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Limit number of returned metrics.
|
||||
/// </summary>
|
||||
[JsonProperty("limit")]
|
||||
public int? Limit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Array of metric names.
|
||||
/// </summary>
|
||||
[JsonProperty("metrics")]
|
||||
public IReadOnlyCollection<string>? Metrics { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Start date and time of requesting data period in ISO 8601 format.
|
||||
/// </summary>
|
||||
[JsonProperty("since")]
|
||||
public DateTime? Since { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// End date and time of requesting data period in ISO 8601 format.
|
||||
/// </summary>
|
||||
[JsonProperty("until")]
|
||||
public DateTime? Until { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Segmentation filter in 'attribute operator value' format.
|
||||
/// </summary>
|
||||
[JsonProperty("filters")]
|
||||
public string? Filters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Array of dimension names to sort by, where each dimension may be prefixed by - (descending) or + (ascending).
|
||||
/// </summary>
|
||||
[JsonProperty("sort")]
|
||||
public IReadOnlyCollection<string>? Sort { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,13 @@ This package contains the feature set of the _DNS_ section of the Cloudflare API
|
||||
|
||||
### [DNS]
|
||||
|
||||
### [DNSSEC]
|
||||
#### [Analytics]
|
||||
|
||||
- [Get Report Table](https://developers.cloudflare.com/api/resources/dns/subresources/analytics/subresources/reports/methods/get/)
|
||||
- [Get Report By Time](https://developers.cloudflare.com/api/resources/dns/subresources/analytics/subresources/reports/subresources/bytimes/methods/get/)
|
||||
|
||||
|
||||
#### [DNSSEC]
|
||||
|
||||
- [Delete DNSSEC Records](https://developers.cloudflare.com/api/resources/dns/subresources/dnssec/methods/delete/)
|
||||
- [Edit DNSSEC Status](https://developers.cloudflare.com/api/resources/dns/subresources/dnssec/methods/edit/)
|
||||
@@ -65,6 +71,7 @@ Published under MIT License (see [choose a license])
|
||||
[Account Custom Nameservers]: https://developers.cloudflare.com/api/resources/custom_nameservers/
|
||||
|
||||
[DNS]: https://developers.cloudflare.com/api/resources/dns/
|
||||
[Analytics]: https://developers.cloudflare.com/api/resources/dns/subresources/analytics/
|
||||
[DNSSEC]: https://developers.cloudflare.com/api/resources/dns/subresources/dnssec/
|
||||
[Records]: https://developers.cloudflare.com/api/resources/dns/subresources/records/
|
||||
[Settings]: https://developers.cloudflare.com/api/resources/dns/subresources/settings/
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AMWD.Net.Api.Cloudflare;
|
||||
using AMWD.Net.Api.Cloudflare.Dns;
|
||||
using Moq;
|
||||
|
||||
namespace Cloudflare.Dns.Tests.DnsAnalyticsExtensions
|
||||
{
|
||||
[TestClass]
|
||||
public class GetDnsAnalyticsByTimeTest
|
||||
{
|
||||
public TestContext TestContext { get; set; }
|
||||
|
||||
private const string ZoneId = "023e105f4ecef8ad9ca31a8372d0c353";
|
||||
|
||||
private Mock<ICloudflareClient> _clientMock;
|
||||
private CloudflareResponse<DnsAnalyticsByTime> _response;
|
||||
private List<(string RequestPath, IQueryParameterFilter QueryFilter)> _callbacks;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
_callbacks = [];
|
||||
|
||||
_response = new CloudflareResponse<DnsAnalyticsByTime>
|
||||
{
|
||||
Success = true,
|
||||
Messages = [
|
||||
new ResponseInfo(1000, "Message 1")
|
||||
],
|
||||
Errors = [
|
||||
new ResponseInfo(1001, "Error 1")
|
||||
],
|
||||
ResultInfo = new PaginationInfo
|
||||
{
|
||||
Count = 1,
|
||||
Page = 1,
|
||||
PerPage = 100,
|
||||
TotalCount = 100,
|
||||
TotalPages = 1,
|
||||
},
|
||||
Result = new DnsAnalyticsByTime()
|
||||
};
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldGetDnsAnalyticsByTime()
|
||||
{
|
||||
// Arrange
|
||||
var client = GetClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetDnsAnalyticsByTime(ZoneId, cancellationToken: TestContext.CancellationToken);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(response);
|
||||
Assert.IsTrue(response.Success);
|
||||
|
||||
Assert.IsNotNull(response.Result);
|
||||
Assert.IsInstanceOfType<DnsAnalyticsByTime>(response.Result);
|
||||
|
||||
Assert.HasCount(1, _callbacks);
|
||||
|
||||
var (requestPath, queryFilter) = _callbacks.First();
|
||||
Assert.AreEqual($"/zones/{ZoneId}/dns_analytics/report/bytime", requestPath);
|
||||
Assert.IsNull(queryFilter);
|
||||
|
||||
_clientMock.Verify(m => m.GetAsync<DnsAnalyticsByTime>($"/zones/{ZoneId}/dns_analytics/report/bytime", null, TestContext.CancellationToken), Times.Once);
|
||||
_clientMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldGetDnsAnalyticsByTimeWithFilter()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsByTimeFilter
|
||||
{
|
||||
Since = DateTime.UtcNow.AddDays(-7),
|
||||
Until = DateTime.UtcNow,
|
||||
TimeDelta = TimeDeltaUnit.Day
|
||||
};
|
||||
|
||||
var client = GetClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetDnsAnalyticsByTime(ZoneId, filter, TestContext.CancellationToken);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(response);
|
||||
Assert.IsTrue(response.Success);
|
||||
|
||||
Assert.IsNotNull(response.Result);
|
||||
Assert.IsInstanceOfType<DnsAnalyticsByTime>(response.Result);
|
||||
|
||||
Assert.HasCount(1, _callbacks);
|
||||
|
||||
var (requestPath, queryFilter) = _callbacks.First();
|
||||
Assert.AreEqual($"/zones/{ZoneId}/dns_analytics/report/bytime", requestPath);
|
||||
Assert.IsNotNull(queryFilter);
|
||||
|
||||
Assert.IsInstanceOfType<GetDnsAnalyticsByTimeFilter>(queryFilter);
|
||||
|
||||
_clientMock.Verify(m => m.GetAsync<DnsAnalyticsByTime>($"/zones/{ZoneId}/dns_analytics/report/bytime", filter, TestContext.CancellationToken), Times.Once);
|
||||
_clientMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnEmptyParameterList()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsByTimeFilter();
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnFullParameterList()
|
||||
{
|
||||
// Arrange
|
||||
var since = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
var until = new DateTime(2024, 1, 8, 0, 0, 0, DateTimeKind.Utc);
|
||||
var filter = new GetDnsAnalyticsByTimeFilter
|
||||
{
|
||||
Dimensions = ["queryName", "responseCode"],
|
||||
Filters = "queryType eq A",
|
||||
Limit = 1000,
|
||||
Metrics = ["requests", "responses"],
|
||||
Since = since,
|
||||
Sort = ["-requests", "+responses"],
|
||||
Until = until,
|
||||
TimeDelta = TimeDeltaUnit.Hour
|
||||
};
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.HasCount(8, dict);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("dimensions"));
|
||||
Assert.AreEqual("queryName,responseCode", dict["dimensions"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("filters"));
|
||||
Assert.AreEqual("queryType eq A", dict["filters"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("limit"));
|
||||
Assert.AreEqual("1000", dict["limit"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("metrics"));
|
||||
Assert.AreEqual("requests,responses", dict["metrics"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("since"));
|
||||
Assert.AreEqual(since.ToIso8601Format(), dict["since"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("sort"));
|
||||
Assert.AreEqual("-requests,+responses", dict["sort"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("until"));
|
||||
Assert.AreEqual(until.ToIso8601Format(), dict["until"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("time_delta"));
|
||||
Assert.AreEqual("hour", dict["time_delta"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(null)]
|
||||
[DataRow("")]
|
||||
[DataRow(" ")]
|
||||
public void ShouldNotAddFilters(string str)
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsByTimeFilter { Filters = str };
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(null)]
|
||||
[DataRow(0)]
|
||||
public void ShouldNotAddLimit(int? limit)
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsByTimeFilter { Limit = limit };
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotAddDimensionsIfEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsByTimeFilter { Dimensions = [] };
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotAddMetricsIfEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsByTimeFilter { Metrics = [] };
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotAddSortIfEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsByTimeFilter { Sort = [] };
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(null)]
|
||||
[DataRow((TimeDeltaUnit)0)]
|
||||
public void ShouldNotAddTimeDelta(TimeDeltaUnit? timeDelta)
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsByTimeFilter { TimeDelta = timeDelta };
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
private ICloudflareClient GetClient()
|
||||
{
|
||||
_clientMock = new Mock<ICloudflareClient>();
|
||||
_clientMock
|
||||
.Setup(m => m.GetAsync<DnsAnalyticsByTime>(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,253 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AMWD.Net.Api.Cloudflare;
|
||||
using AMWD.Net.Api.Cloudflare.Dns;
|
||||
using Moq;
|
||||
|
||||
namespace Cloudflare.Dns.Tests.DnsAnalyticsExtensions
|
||||
{
|
||||
[TestClass]
|
||||
public class GetDnsAnalyticsReportTest
|
||||
{
|
||||
public TestContext TestContext { get; set; }
|
||||
|
||||
private const string ZoneId = "023e105f4ecef8ad9ca31a8372d0c353";
|
||||
|
||||
private Mock<ICloudflareClient> _clientMock;
|
||||
private CloudflareResponse<DnsAnalyticsReport> _response;
|
||||
private List<(string RequestPath, IQueryParameterFilter QueryFilter)> _callbacks;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
_callbacks = [];
|
||||
|
||||
_response = new CloudflareResponse<DnsAnalyticsReport>
|
||||
{
|
||||
Success = true,
|
||||
Messages = [
|
||||
new ResponseInfo(1000, "Message 1")
|
||||
],
|
||||
Errors = [
|
||||
new ResponseInfo(1001, "Error 1")
|
||||
],
|
||||
ResultInfo = new PaginationInfo
|
||||
{
|
||||
Count = 1,
|
||||
Page = 1,
|
||||
PerPage = 100,
|
||||
TotalCount = 100,
|
||||
TotalPages = 1,
|
||||
},
|
||||
Result = new DnsAnalyticsReport()
|
||||
};
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldGetDnsAnalyticsReport()
|
||||
{
|
||||
// Arrange
|
||||
var client = GetClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetDnsAnalyticsReport(ZoneId, cancellationToken: TestContext.CancellationToken);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(response);
|
||||
Assert.IsTrue(response.Success);
|
||||
|
||||
Assert.IsNotNull(response.Result);
|
||||
Assert.IsInstanceOfType<DnsAnalyticsReport>(response.Result);
|
||||
|
||||
Assert.HasCount(1, _callbacks);
|
||||
|
||||
var (requestPath, queryFilter) = _callbacks.First();
|
||||
Assert.AreEqual($"/zones/{ZoneId}/dns_analytics/report", requestPath);
|
||||
Assert.IsNull(queryFilter);
|
||||
|
||||
_clientMock.Verify(m => m.GetAsync<DnsAnalyticsReport>($"/zones/{ZoneId}/dns_analytics/report", null, TestContext.CancellationToken), Times.Once);
|
||||
_clientMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldGetDnsAnalyticsReportWithFilter()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsReportFilter
|
||||
{
|
||||
Since = DateTime.UtcNow.AddDays(-7),
|
||||
Until = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var client = GetClient();
|
||||
|
||||
// Act
|
||||
var response = await client.GetDnsAnalyticsReport(ZoneId, filter, TestContext.CancellationToken);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(response);
|
||||
Assert.IsTrue(response.Success);
|
||||
|
||||
Assert.IsNotNull(response.Result);
|
||||
Assert.IsInstanceOfType<DnsAnalyticsReport>(response.Result);
|
||||
|
||||
Assert.HasCount(1, _callbacks);
|
||||
|
||||
var (requestPath, queryFilter) = _callbacks.First();
|
||||
Assert.AreEqual($"/zones/{ZoneId}/dns_analytics/report", requestPath);
|
||||
Assert.IsNotNull(queryFilter);
|
||||
|
||||
Assert.IsInstanceOfType<GetDnsAnalyticsReportFilter>(queryFilter);
|
||||
|
||||
_clientMock.Verify(m => m.GetAsync<DnsAnalyticsReport>($"/zones/{ZoneId}/dns_analytics/report", filter, TestContext.CancellationToken), Times.Once);
|
||||
_clientMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnEmptyParameterList()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsReportFilter();
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnFullParameterList()
|
||||
{
|
||||
// Arrange
|
||||
var since = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
var until = new DateTime(2024, 1, 8, 0, 0, 0, DateTimeKind.Utc);
|
||||
var filter = new GetDnsAnalyticsReportFilter
|
||||
{
|
||||
Dimensions = ["queryName", "responseCode"],
|
||||
Filters = "queryType eq A",
|
||||
Limit = 1000,
|
||||
Metrics = ["requests", "responses"],
|
||||
Since = since,
|
||||
Sort = ["-requests", "+responses"],
|
||||
Until = until
|
||||
};
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.HasCount(7, dict);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("dimensions"));
|
||||
Assert.AreEqual("queryName,responseCode", dict["dimensions"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("filters"));
|
||||
Assert.AreEqual("queryType eq A", dict["filters"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("limit"));
|
||||
Assert.AreEqual("1000", dict["limit"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("metrics"));
|
||||
Assert.AreEqual("requests,responses", dict["metrics"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("since"));
|
||||
Assert.AreEqual(since.ToIso8601Format(), dict["since"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("sort"));
|
||||
Assert.AreEqual("-requests,+responses", dict["sort"]);
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey("until"));
|
||||
Assert.AreEqual(until.ToIso8601Format(), dict["until"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(null)]
|
||||
[DataRow("")]
|
||||
[DataRow(" ")]
|
||||
public void ShouldNotAddFilters(string str)
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsReportFilter { Filters = str };
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(null)]
|
||||
[DataRow(0)]
|
||||
public void ShouldNotAddLimit(int? limit)
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsReportFilter { Limit = limit };
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotAddDimensionsIfEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsReportFilter { Dimensions = [] };
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotAddMetricsIfEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsReportFilter { Metrics = [] };
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotAddSortIfEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new GetDnsAnalyticsReportFilter { Sort = [] };
|
||||
|
||||
// Act
|
||||
var dict = filter.GetQueryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(dict);
|
||||
Assert.IsEmpty(dict);
|
||||
}
|
||||
|
||||
private ICloudflareClient GetClient()
|
||||
{
|
||||
_clientMock = new Mock<ICloudflareClient>();
|
||||
_clientMock
|
||||
.Setup(m => m.GetAsync<DnsAnalyticsReport>(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