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
|
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
|
||||||
script:
|
script:
|
||||||
- dotnet test -c Debug --nologo /p:CoverletOutputFormat=Cobertura
|
- 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
|
- cat /reports/Summary.txt
|
||||||
artifacts:
|
artifacts:
|
||||||
when: always
|
when: always
|
||||||
@@ -105,7 +105,7 @@ core-test:
|
|||||||
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
|
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
|
||||||
script:
|
script:
|
||||||
- dotnet test -c Release --nologo /p:CoverletOutputFormat=Cobertura test/Cloudflare.Tests/Cloudflare.Tests.csproj
|
- 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
|
- cat /reports/Summary.txt
|
||||||
artifacts:
|
artifacts:
|
||||||
when: always
|
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.
|
- New automatic documentation generation using docfx.
|
||||||
- Additional articles for the documentation.
|
- Additional articles for the documentation.
|
||||||
- `DateTime` extensions for ISO 8601 formatting.
|
- `DateTime` extensions for ISO 8601 formatting.
|
||||||
|
- DNS Analytics
|
||||||
|
|
||||||
|
|
||||||
## [v0.1.0], [zones/v0.1.0], [dns/v0.1.0] - 2025-08-05
|
## [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;
|
namespace AMWD.Net.Api.Cloudflare.Dns
|
||||||
using Newtonsoft.Json.Converters;
|
|
||||||
|
|
||||||
namespace AMWD.Net.Api.Cloudflare.Dns
|
|
||||||
{
|
{
|
||||||
internal class DNSAnalyticsQuery
|
/// <summary>
|
||||||
|
/// The DNS Analytics query.
|
||||||
|
/// </summary>
|
||||||
|
public class DnsAnalyticsQuery
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Array of dimension names.
|
/// Array of dimension names.
|
||||||
@@ -35,6 +35,9 @@ namespace AMWD.Net.Api.Cloudflare.Dns
|
|||||||
[JsonProperty("time_delta")]
|
[JsonProperty("time_delta")]
|
||||||
public TimeDeltaUnit? TimeDelta { get; set; }
|
public TimeDeltaUnit? TimeDelta { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End date and time of requesting data period.
|
||||||
|
/// </summary>
|
||||||
[JsonProperty("until")]
|
[JsonProperty("until")]
|
||||||
public DateTime? Until { get; set; }
|
public DateTime? Until { get; set; }
|
||||||
|
|
||||||
@@ -51,72 +54,4 @@ namespace AMWD.Net.Api.Cloudflare.Dns
|
|||||||
[JsonProperty("sort")]
|
[JsonProperty("sort")]
|
||||||
public IReadOnlyCollection<string>? Sort { get; set; }
|
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]
|
### [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/)
|
- [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/)
|
- [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/
|
[Account Custom Nameservers]: https://developers.cloudflare.com/api/resources/custom_nameservers/
|
||||||
|
|
||||||
[DNS]: https://developers.cloudflare.com/api/resources/dns/
|
[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/
|
[DNSSEC]: https://developers.cloudflare.com/api/resources/dns/subresources/dnssec/
|
||||||
[Records]: https://developers.cloudflare.com/api/resources/dns/subresources/records/
|
[Records]: https://developers.cloudflare.com/api/resources/dns/subresources/records/
|
||||||
[Settings]: https://developers.cloudflare.com/api/resources/dns/subresources/settings/
|
[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