diff --git a/CHANGELOG.md b/CHANGELOG.md index 62510038..c9a17cdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This project uses [Semantic Versioning 2.0.0](http://semver.org/), the format is - Added `restoreDomain` to restore a domain. (#267) - Added `getDomainRestore` to retrieve the details of a domain restore. (#267) - Added `batchChangeZoneRecords` to create, update, and delete records in a zone in one atomic request. (#273) +- Added `DnsAnalytics.query` to query DNS Analytics data. The API is in Public Beta. (#274) ### Changed diff --git a/src/main/java/com/dnsimple/Client.java b/src/main/java/com/dnsimple/Client.java index 3d28a90d..212a0348 100644 --- a/src/main/java/com/dnsimple/Client.java +++ b/src/main/java/com/dnsimple/Client.java @@ -25,6 +25,7 @@ public class Client { public final Billing billing; public final Certificates certificates; public final Contacts contacts; + public final DnsAnalytics dnsAnalytics; public final Domains domains; public final Identity identity; public final Oauth oauth; @@ -36,12 +37,13 @@ public class Client { public final Webhooks webhooks; public final Zones zones; - private Client(HttpEndpointClient endpointClient, Accounts accounts, Billing billing, Certificates certificates, Contacts contacts, Domains domains, Identity identity, Oauth oauth, Registrar registrar, Services services, Templates templates, Tlds tlds, VanityNameServers vanityNameServers, Webhooks webhooks, Zones zones) { + private Client(HttpEndpointClient endpointClient, Accounts accounts, Billing billing, Certificates certificates, Contacts contacts, DnsAnalytics dnsAnalytics, Domains domains, Identity identity, Oauth oauth, Registrar registrar, Services services, Templates templates, Tlds tlds, VanityNameServers vanityNameServers, Webhooks webhooks, Zones zones) { this.endpointClient = endpointClient; this.accounts = accounts; this.billing = billing; this.certificates = certificates; this.contacts = contacts; + this.dnsAnalytics = dnsAnalytics; this.domains = domains; this.identity = identity; this.oauth = oauth; @@ -62,6 +64,7 @@ private static Client of(HttpRequestFactory httpRequestFactory, URL apiBase, Str new Billing(endpointClient), new Certificates(endpointClient), new Contacts(endpointClient), + new DnsAnalytics(endpointClient), new Domains(endpointClient), new Identity(endpointClient), new Oauth(endpointClient), diff --git a/src/main/java/com/dnsimple/data/DnsAnalyticsEntry.java b/src/main/java/com/dnsimple/data/DnsAnalyticsEntry.java new file mode 100644 index 00000000..2e606f80 --- /dev/null +++ b/src/main/java/com/dnsimple/data/DnsAnalyticsEntry.java @@ -0,0 +1,27 @@ +package com.dnsimple.data; + +import java.time.LocalDate; + +public class DnsAnalyticsEntry { + private final String zoneName; + private final LocalDate date; + private final Long volume; + + public DnsAnalyticsEntry(String zoneName, LocalDate date, Long volume) { + this.zoneName = zoneName; + this.date = date; + this.volume = volume; + } + + public String getZoneName() { + return zoneName; + } + + public LocalDate getDate() { + return date; + } + + public Long getVolume() { + return volume; + } +} diff --git a/src/main/java/com/dnsimple/data/DnsAnalyticsQuery.java b/src/main/java/com/dnsimple/data/DnsAnalyticsQuery.java new file mode 100644 index 00000000..a32c9912 --- /dev/null +++ b/src/main/java/com/dnsimple/data/DnsAnalyticsQuery.java @@ -0,0 +1,51 @@ +package com.dnsimple.data; + +import java.time.LocalDate; + +public class DnsAnalyticsQuery { + private final Long accountId; + private final LocalDate startDate; + private final LocalDate endDate; + private final String sort; + private final Integer page; + private final Integer perPage; + private final String groupings; + + public DnsAnalyticsQuery(Long accountId, LocalDate startDate, LocalDate endDate, String sort, Integer page, Integer perPage, String groupings) { + this.accountId = accountId; + this.startDate = startDate; + this.endDate = endDate; + this.sort = sort; + this.page = page; + this.perPage = perPage; + this.groupings = groupings; + } + + public Long getAccountId() { + return accountId; + } + + public LocalDate getStartDate() { + return startDate; + } + + public LocalDate getEndDate() { + return endDate; + } + + public String getSort() { + return sort; + } + + public Integer getPage() { + return page; + } + + public Integer getPerPage() { + return perPage; + } + + public String getGroupings() { + return groupings; + } +} diff --git a/src/main/java/com/dnsimple/endpoints/DnsAnalytics.java b/src/main/java/com/dnsimple/endpoints/DnsAnalytics.java new file mode 100644 index 00000000..7baf9fcc --- /dev/null +++ b/src/main/java/com/dnsimple/endpoints/DnsAnalytics.java @@ -0,0 +1,69 @@ +package com.dnsimple.endpoints; + +import com.dnsimple.data.DnsAnalyticsEntry; +import com.dnsimple.data.DnsAnalyticsQuery; +import com.dnsimple.http.HttpEndpointClient; +import com.dnsimple.request.ListOptions; +import com.dnsimple.response.PaginatedResponseWithQuery; +import com.dnsimple.response.Pagination; + +import java.time.LocalDate; +import java.util.List; + +import static com.dnsimple.http.HttpMethod.GET; +import static java.util.stream.Collectors.toList; + +/** + * Provides access to the DNSimple DNS Analytics API. + * + * @see https://developer.dnsimple.com/v2/dns-analytics + */ +public class DnsAnalytics { + private final HttpEndpointClient client; + + public DnsAnalytics(HttpEndpointClient client) { + this.client = client; + } + + /** + * Queries DNS Analytics data for the account. This API is in Public Beta. + * + * @param account The account ID + * @param options The options for the query request + * @return The DNS Analytics query response. An entry field is null when the query does not group by it. + * @see https://developer.dnsimple.com/v2/dns-analytics/#queryDnsAnalytics + */ + public PaginatedResponseWithQuery query(Number account, ListOptions options) { + QueryResponse response = client.raw(GET, account + "/dns_analytics", options, null, QueryResponse.class); + List entries = response.data.rows.stream() + .map(row -> toEntry(response.data.headers, row)) + .collect(toList()); + return new PaginatedResponseWithQuery<>(entries, response.pagination, response.query); + } + + private static DnsAnalyticsEntry toEntry(List headers, List row) { + String date = (String) valueOf("date", headers, row); + Number volume = (Number) valueOf("volume", headers, row); + return new DnsAnalyticsEntry( + (String) valueOf("zone_name", headers, row), + date == null ? null : LocalDate.parse(date), + volume == null ? null : volume.longValue() + ); + } + + private static Object valueOf(String header, List headers, List row) { + int index = headers.indexOf(header); + return index < 0 ? null : row.get(index); + } + + private static class QueryResponse { + private QueryData data; + private DnsAnalyticsQuery query; + private Pagination pagination; + } + + private static class QueryData { + private List headers; + private List> rows; + } +} diff --git a/src/main/java/com/dnsimple/response/PaginatedResponseWithQuery.java b/src/main/java/com/dnsimple/response/PaginatedResponseWithQuery.java new file mode 100644 index 00000000..4ce4d23c --- /dev/null +++ b/src/main/java/com/dnsimple/response/PaginatedResponseWithQuery.java @@ -0,0 +1,16 @@ +package com.dnsimple.response; + +import java.util.List; + +public class PaginatedResponseWithQuery extends PaginatedResponse { + private final Q query; + + public PaginatedResponseWithQuery(List data, Pagination pagination, Q query) { + super(data, pagination); + this.query = query; + } + + public Q getQuery() { + return query; + } +} diff --git a/src/test/java/com/dnsimple/endpoints/DnsAnalyticsTest.java b/src/test/java/com/dnsimple/endpoints/DnsAnalyticsTest.java new file mode 100644 index 00000000..5fda80eb --- /dev/null +++ b/src/test/java/com/dnsimple/endpoints/DnsAnalyticsTest.java @@ -0,0 +1,76 @@ +package com.dnsimple.endpoints; + +import com.dnsimple.data.DnsAnalyticsEntry; +import com.dnsimple.data.DnsAnalyticsQuery; +import com.dnsimple.request.ListOptions; +import com.dnsimple.response.PaginatedResponseWithQuery; +import com.dnsimple.tools.DnsimpleTestBase; +import org.junit.Test; + +import java.time.LocalDate; +import java.util.List; + +import static com.dnsimple.http.HttpMethod.GET; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; + +public class DnsAnalyticsTest extends DnsimpleTestBase { + @Test + public void testQuery() { + server.stubFixtureAt("dnsAnalytics/success.http"); + client.dnsAnalytics.query(1, ListOptions.empty()); + assertThat(server.getRecordedRequest().getMethod(), is(GET)); + assertThat(server.getRecordedRequest().getPath(), is("/v2/1/dns_analytics")); + } + + @Test + public void testQuerySupportsFilteringSortingAndPagination() { + server.stubFixtureAt("dnsAnalytics/success.http"); + ListOptions options = ListOptions.empty() + .page(0, 100) + .filter("start_date", "2023-12-08") + .filter("end_date", "2024-01-08") + .filter("groupings", "zone_name,date") + .sortAsc("zone_name") + .sortAsc("date"); + client.dnsAnalytics.query(1, options); + assertThat(server.getRecordedRequest().getPath(), is("/v2/1/dns_analytics?page=0&per_page=100&start_date=2023-12-08&end_date=2024-01-08&groupings=zone_name%2Cdate&sort=zone_name%3Aasc,date%3Aasc")); + } + + @Test + public void testQueryProducesEntryList() { + server.stubFixtureAt("dnsAnalytics/success.http"); + List entries = client.dnsAnalytics.query(1, ListOptions.empty()).getData(); + assertThat(entries, hasSize(12)); + assertThat(entries.get(0).getZoneName(), is("bar.com")); + assertThat(entries.get(0).getDate(), is(LocalDate.of(2023, 12, 8))); + assertThat(entries.get(0).getVolume(), is(1200L)); + assertThat(entries.get(11).getZoneName(), is("foo.com")); + assertThat(entries.get(11).getDate(), is(LocalDate.of(2024, 1, 8))); + assertThat(entries.get(11).getVolume(), is(1200L)); + } + + @Test + public void testQueryExposesPaginationInfo() { + server.stubFixtureAt("dnsAnalytics/success.http"); + PaginatedResponseWithQuery response = client.dnsAnalytics.query(1, ListOptions.empty()); + assertThat(response.getPagination().getCurrentPage(), is(0)); + assertThat(response.getPagination().getPerPage(), is(100)); + assertThat(response.getPagination().getTotalEntries(), is(93)); + assertThat(response.getPagination().getTotalPages(), is(1)); + } + + @Test + public void testQueryExposesQuery() { + server.stubFixtureAt("dnsAnalytics/success.http"); + DnsAnalyticsQuery query = client.dnsAnalytics.query(1, ListOptions.empty()).getQuery(); + assertThat(query.getAccountId(), is(1L)); + assertThat(query.getStartDate(), is(LocalDate.of(2023, 12, 8))); + assertThat(query.getEndDate(), is(LocalDate.of(2024, 1, 8))); + assertThat(query.getSort(), is("zone_name:asc,date:asc")); + assertThat(query.getPage(), is(0)); + assertThat(query.getPerPage(), is(100)); + assertThat(query.getGroupings(), is("zone_name,date")); + } +}