diff --git a/gcloud-java-dns/pom.xml b/gcloud-java-dns/pom.xml new file mode 100644 index 000000000000..5f04f261d500 --- /dev/null +++ b/gcloud-java-dns/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + com.google.gcloud + gcloud-java-dns + jar + GCloud Java DNS + + Java idiomatic client for Google Cloud DNS. + + + com.google.gcloud + gcloud-java-pom + 0.1.3-SNAPSHOT + + + gcloud-java-dns + + + + ${project.groupId} + gcloud-java-core + ${project.version} + + + com.google.apis + google-api-services-dns + v1-rev7-1.21.0 + compile + + + com.google.guava + guava-jdk5 + + + com.google.api-client + google-api-client + + + + + junit + junit + 4.12 + test + + + org.easymock + easymock + 3.3 + test + + + diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/AbstractOption.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/AbstractOption.java new file mode 100644 index 000000000000..a148468d14b5 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/AbstractOption.java @@ -0,0 +1,70 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.MoreObjects; +import com.google.gcloud.spi.DnsRpc; + +import java.io.Serializable; +import java.util.Objects; + +/** + * A base class for options. + */ +abstract class AbstractOption implements Serializable { + + private static final long serialVersionUID = -5912727967831484228L; + private final Object value; + private final DnsRpc.Option rpcOption; + + AbstractOption(DnsRpc.Option rpcOption, Object value) { + this.rpcOption = checkNotNull(rpcOption); + this.value = value; + } + + Object value() { + return value; + } + + DnsRpc.Option rpcOption() { + return rpcOption; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AbstractOption)) { + return false; + } + AbstractOption other = (AbstractOption) obj; + return Objects.equals(value, other.value) && Objects.equals(rpcOption, other.rpcOption); + } + + @Override + public int hashCode() { + return Objects.hash(value, rpcOption); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("value", value) + .add("rpcOption", rpcOption) + .toString(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ChangeRequest.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ChangeRequest.java new file mode 100644 index 000000000000..76d231b704c4 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ChangeRequest.java @@ -0,0 +1,317 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.services.dns.model.Change; +import com.google.common.base.Function; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import org.joda.time.DateTime; +import org.joda.time.format.ISODateTimeFormat; + +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * A class representing an atomic update to a collection of {@link DnsRecord}s within a {@code + * Zone}. + * + * @see Google Cloud DNS documentation + */ +public class ChangeRequest implements Serializable { + + static final Function FROM_PB_FUNCTION = + new Function() { + @Override + public ChangeRequest apply(com.google.api.services.dns.model.Change pb) { + return ChangeRequest.fromPb(pb); + } + }; + private static final long serialVersionUID = -9027378042756366333L; + private final List additions; + private final List deletions; + private final String id; + private final Long startTimeMillis; + private final Status status; + + /** + * This enumerates the possible states of a {@code ChangeRequest}. + * + * @see Google Cloud DNS + * documentation + */ + public enum Status { + PENDING, + DONE + } + + /** + * A builder for {@code ChangeRequest}s. + */ + public static class Builder { + + private List additions = new LinkedList<>(); + private List deletions = new LinkedList<>(); + private String id; + private Long startTimeMillis; + private Status status; + + private Builder(ChangeRequest cr) { + this.additions = Lists.newLinkedList(cr.additions()); + this.deletions = Lists.newLinkedList(cr.deletions()); + this.id = cr.id(); + this.startTimeMillis = cr.startTimeMillis(); + this.status = cr.status(); + } + + private Builder() { + } + + /** + * Sets a collection of {@link DnsRecord}s which are to be added to the zone upon executing this + * {@code ChangeRequest}. + */ + public Builder additions(List additions) { + this.additions = Lists.newLinkedList(checkNotNull(additions)); + return this; + } + + /** + * Sets a collection of {@link DnsRecord}s which are to be deleted from the zone upon executing + * this {@code ChangeRequest}. + */ + public Builder deletions(List deletions) { + this.deletions = Lists.newLinkedList(checkNotNull(deletions)); + return this; + } + + /** + * Adds a {@link DnsRecord} to be added to the zone upon executing this {@code + * ChangeRequest}. + */ + public Builder add(DnsRecord record) { + this.additions.add(checkNotNull(record)); + return this; + } + + /** + * Adds a {@link DnsRecord} to be deleted to the zone upon executing this + * {@code ChangeRequest}. + */ + public Builder delete(DnsRecord record) { + this.deletions.add(checkNotNull(record)); + return this; + } + + /** + * Clears the collection of {@link DnsRecord}s which are to be added to the zone upon executing + * this {@code ChangeRequest}. + */ + public Builder clearAdditions() { + this.additions.clear(); + return this; + } + + /** + * Clears the collection of {@link DnsRecord}s which are to be deleted from the zone upon + * executing this {@code ChangeRequest}. + */ + public Builder clearDeletions() { + this.deletions.clear(); + return this; + } + + /** + * Removes a single {@link DnsRecord} from the collection of records to be + * added to the zone upon executing this {@code ChangeRequest}. + */ + public Builder removeAddition(DnsRecord record) { + this.additions.remove(record); + return this; + } + + /** + * Removes a single {@link DnsRecord} from the collection of records to be + * deleted from the zone upon executing this {@code ChangeRequest}. + */ + public Builder removeDeletion(DnsRecord record) { + this.deletions.remove(record); + return this; + } + + /** + * Associates a server-assigned id to this {@code ChangeRequest}. + */ + Builder id(String id) { + this.id = checkNotNull(id); + return this; + } + + /** + * Sets the time when this {@code ChangeRequest} was started by a server. + */ + Builder startTimeMillis(long startTimeMillis) { + this.startTimeMillis = startTimeMillis; + return this; + } + + /** + * Sets the current status of this {@code ChangeRequest}. + */ + Builder status(Status status) { + this.status = checkNotNull(status); + return this; + } + + /** + * Creates a {@code ChangeRequest} instance populated by the values associated with this + * builder. + */ + public ChangeRequest build() { + return new ChangeRequest(this); + } + } + + private ChangeRequest(Builder builder) { + this.additions = ImmutableList.copyOf(builder.additions); + this.deletions = ImmutableList.copyOf(builder.deletions); + this.id = builder.id; + this.startTimeMillis = builder.startTimeMillis; + this.status = builder.status; + } + + /** + * Returns an empty builder for the {@code ChangeRequest} class. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Creates a builder populated with values of this {@code ChangeRequest}. + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Returns the list of {@link DnsRecord}s to be added to the zone upon submitting this {@code + * ChangeRequest}. + */ + public List additions() { + return additions; + } + + /** + * Returns the list of {@link DnsRecord}s to be deleted from the zone upon submitting this {@code + * ChangeRequest}. + */ + public List deletions() { + return deletions; + } + + /** + * Returns the id assigned to this {@code ChangeRequest} by the server. + */ + public String id() { + return id; + } + + /** + * Returns the time when this {@code ChangeRequest} was started by the server. + */ + public Long startTimeMillis() { + return startTimeMillis; + } + + /** + * Returns the status of this {@code ChangeRequest}. + */ + public Status status() { + return status; + } + + com.google.api.services.dns.model.Change toPb() { + com.google.api.services.dns.model.Change pb = + new com.google.api.services.dns.model.Change(); + // set id + if (id() != null) { + pb.setId(id()); + } + // set timestamp + if (startTimeMillis() != null) { + pb.setStartTime(ISODateTimeFormat.dateTime().withZoneUTC().print(startTimeMillis())); + } + // set status + if (status() != null) { + pb.setStatus(status().name().toLowerCase()); + } + // set a list of additions + pb.setAdditions(Lists.transform(additions(), DnsRecord.TO_PB_FUNCTION)); + // set a list of deletions + pb.setDeletions(Lists.transform(deletions(), DnsRecord.TO_PB_FUNCTION)); + return pb; + } + + static ChangeRequest fromPb(com.google.api.services.dns.model.Change pb) { + Builder builder = builder(); + if (pb.getId() != null) { + builder.id(pb.getId()); + } + if (pb.getStartTime() != null) { + builder.startTimeMillis(DateTime.parse(pb.getStartTime()).getMillis()); + } + if (pb.getStatus() != null) { + // we are assuming that status indicated in pb is a lower case version of the enum name + builder.status(ChangeRequest.Status.valueOf(pb.getStatus().toUpperCase())); + } + if (pb.getDeletions() != null) { + builder.deletions(Lists.transform(pb.getDeletions(), DnsRecord.FROM_PB_FUNCTION)); + } + if (pb.getAdditions() != null) { + builder.additions(Lists.transform(pb.getAdditions(), DnsRecord.FROM_PB_FUNCTION)); + } + return builder.build(); + } + + @Override + public boolean equals(Object other) { + return (other instanceof ChangeRequest) && toPb().equals(((ChangeRequest) other).toPb()); + } + + @Override + public int hashCode() { + return Objects.hash(additions, deletions, id, startTimeMillis, status); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("additions", additions) + .add("deletions", deletions) + .add("id", id) + .add("startTimeMillis", startTimeMillis) + .add("status", status) + .toString(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/Dns.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/Dns.java new file mode 100644 index 000000000000..3ad2094ec2e3 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/Dns.java @@ -0,0 +1,533 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import com.google.common.base.Joiner; +import com.google.common.collect.Sets; +import com.google.gcloud.Page; +import com.google.gcloud.Service; +import com.google.gcloud.spi.DnsRpc; + +import java.io.Serializable; +import java.util.Set; + +/** + * An interface for the Google Cloud DNS service. + * + * @see Google Cloud DNS + */ +public interface Dns extends Service { + + /** + * The fields of a project. + * + *

These values can be used to specify the fields to include in a partial response when calling + * {@link Dns#getProject(ProjectOption...)}. Project ID is always returned, even if not + * specified. + */ + enum ProjectField { + PROJECT_ID("id"), + PROJECT_NUMBER("number"), + QUOTA("quota"); + + private final String selector; + + ProjectField(String selector) { + this.selector = selector; + } + + String selector() { + return selector; + } + + static String selector(ProjectField... fields) { + Set fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 1); + fieldStrings.add(PROJECT_ID.selector()); + for (ProjectField field : fields) { + fieldStrings.add(field.selector()); + } + return Joiner.on(',').join(fieldStrings); + } + } + + /** + * The fields of a zone. + * + *

These values can be used to specify the fields to include in a partial response when calling + * {@link Dns#getZone(String, ZoneOption...)}. The name is always returned, even if not specified. + */ + enum ZoneField { + CREATION_TIME("creationTime"), + DESCRIPTION("description"), + DNS_NAME("dnsName"), + ZONE_ID("id"), + NAME("name"), + NAME_SERVER_SET("nameServerSet"), + NAME_SERVERS("nameServers"); + + private final String selector; + + ZoneField(String selector) { + this.selector = selector; + } + + String selector() { + return selector; + } + + static String selector(ZoneField... fields) { + Set fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 1); + fieldStrings.add(NAME.selector()); + for (ZoneField field : fields) { + fieldStrings.add(field.selector()); + } + return Joiner.on(',').join(fieldStrings); + } + } + + /** + * The fields of a DNS record. + * + *

These values can be used to specify the fields to include in a partial response when calling + * {@link Dns#listDnsRecords(String, DnsRecordListOption...)}. The name is always returned even if + * not selected. + */ + enum DnsRecordField { + DNS_RECORDS("rrdatas"), + NAME("name"), + TTL("ttl"), + TYPE("type"); + + private final String selector; + + DnsRecordField(String selector) { + this.selector = selector; + } + + String selector() { + return selector; + } + + static String selector(DnsRecordField... fields) { + Set fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 1); + fieldStrings.add(NAME.selector()); + for (DnsRecordField field : fields) { + fieldStrings.add(field.selector()); + } + return Joiner.on(',').join(fieldStrings); + } + } + + /** + * The fields of a change request. + * + *

These values can be used to specify the fields to include in a partial response when calling + * {@link Dns#applyChangeRequest(String, ChangeRequest, ChangeRequestOption...)} The ID is always + * returned even if not selected. + */ + enum ChangeRequestField { + ID("id"), + START_TIME("startTime"), + STATUS("status"), + ADDITIONS("additions"), + DELETIONS("deletions"); + + private final String selector; + + ChangeRequestField(String selector) { + this.selector = selector; + } + + String selector() { + return selector; + } + + static String selector(ChangeRequestField... fields) { + Set fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 1); + fieldStrings.add(ID.selector()); + for (ChangeRequestField field : fields) { + fieldStrings.add(field.selector()); + } + return Joiner.on(',').join(fieldStrings); + } + } + + /** + * The sorting order for listing. + */ + enum SortingOrder { + DESCENDING, ASCENDING; + + public String selector() { + return name().toLowerCase(); + } + } + + /** + * Class that for specifying DNS record options. + */ + class DnsRecordListOption extends AbstractOption implements Serializable { + + private static final long serialVersionUID = 1009627025381096098L; + + DnsRecordListOption(DnsRpc.Option option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify the DNS record's fields to be returned by the RPC call. + * + *

If this option is not provided all record fields are returned. {@code + * DnsRecordField.fields} can be used to specify only the fields of interest. The name of the + * DNS record always returned, even if not specified. {@link DnsRecordField} provides a list of + * fields that can be used. + */ + public static DnsRecordListOption fields(DnsRecordField... fields) { + StringBuilder builder = new StringBuilder(); + builder.append("rrsets(").append(DnsRecordField.selector(fields)).append(')'); + return new DnsRecordListOption(DnsRpc.Option.FIELDS, builder.toString()); + } + + /** + * Returns an option to specify a page token. + * + *

The page token (returned from a previous call to list) indicates from where listing should + * continue. + */ + public static DnsRecordListOption pageToken(String pageToken) { + return new DnsRecordListOption(DnsRpc.Option.PAGE_TOKEN, pageToken); + } + + /** + * The maximum number of DNS records to return per RPC. + * + *

The server can return fewer records than requested. When there are more results than the + * page size, the server will return a page token that can be used to fetch other results. + */ + public static DnsRecordListOption pageSize(int pageSize) { + return new DnsRecordListOption(DnsRpc.Option.PAGE_SIZE, pageSize); + } + + /** + * Restricts the list to only DNS records with this fully qualified domain name. + */ + public static DnsRecordListOption dnsName(String dnsName) { + return new DnsRecordListOption(DnsRpc.Option.NAME, dnsName); + } + + /** + * Restricts the list to return only records of this type. If present, {@link + * Dns.DnsRecordListOption#dnsName(String)} must also be present. + */ + public static DnsRecordListOption type(DnsRecord.Type type) { + return new DnsRecordListOption(DnsRpc.Option.DNS_TYPE, type); + } + } + + /** + * Class for specifying zone field options. + */ + class ZoneOption extends AbstractOption implements Serializable { + + private static final long serialVersionUID = -8065564464895945037L; + + ZoneOption(DnsRpc.Option option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify the zones's fields to be returned by the RPC call. + * + *

If this option is not provided all zone fields are returned. {@code ZoneOption.fields} can + * be used to specify only the fields of interest. Zone ID is always returned, even if not + * specified. {@link ZoneField} provides a list of fields that can be used. + */ + public static ZoneOption fields(ZoneField... fields) { + return new ZoneOption(DnsRpc.Option.FIELDS, ZoneField.selector(fields)); + } + } + + /** + * Class for specifying zone listing options. + */ + class ZoneListOption extends AbstractOption implements Serializable { + + private static final long serialVersionUID = -2830645032124504717L; + + ZoneListOption(DnsRpc.Option option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify the zones's fields to be returned by the RPC call. + * + *

If this option is not provided all zone fields are returned. {@code ZoneOption.fields} can + * be used to specify only the fields of interest. Zone ID is always returned, even if not + * specified. {@link ZoneField} provides a list of fields that can be used. + */ + public static ZoneListOption fields(ZoneField... fields) { + StringBuilder builder = new StringBuilder(); + builder.append("managedZones(").append(ZoneField.selector(fields)).append(')'); + return new ZoneListOption(DnsRpc.Option.FIELDS, builder.toString()); + } + + /** + * Returns an option to specify a page token. + * + *

The page token (returned from a previous call to list) indicates from where listing should + * continue. + */ + public static ZoneListOption pageToken(String pageToken) { + return new ZoneListOption(DnsRpc.Option.PAGE_TOKEN, pageToken); + } + + /** + * Restricts the list to only zone with this fully qualified domain name. + */ + public static ZoneListOption dnsName(String dnsName) { + StringBuilder builder = new StringBuilder(); + return new ZoneListOption(DnsRpc.Option.DNS_NAME, dnsName); + } + + /** + * The maximum number of zones to return per RPC. + * + *

The server can return fewer zones than requested. When there are more results than the + * page size, the server will return a page token that can be used to fetch other results. + */ + public static ZoneListOption pageSize(int pageSize) { + return new ZoneListOption(DnsRpc.Option.PAGE_SIZE, pageSize); + } + } + + /** + * Class for specifying project options. + */ + class ProjectOption extends AbstractOption implements Serializable { + + private static final long serialVersionUID = 6817937338218847748L; + + ProjectOption(DnsRpc.Option option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify the project's fields to be returned by the RPC call. + * + *

If this option is not provided all project fields are returned. {@code + * ProjectOption.fields} can be used to specify only the fields of interest. Project ID is + * always returned, even if not specified. {@link ProjectField} provides a list of fields that + * can be used. + */ + public static ProjectOption fields(ProjectField... fields) { + return new ProjectOption(DnsRpc.Option.FIELDS, ProjectField.selector(fields)); + } + } + + /** + * Class for specifying change request field options. + */ + class ChangeRequestOption extends AbstractOption implements Serializable { + + private static final long serialVersionUID = 1067273695061077782L; + + ChangeRequestOption(DnsRpc.Option option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify which fields of {@link ChangeRequest} should be returned by the + * service. + * + *

If this option is not provided all change request fields are returned. {@code + * ChangeRequestOption.fields} can be used to specify only the fields of interest. The ID of the + * change request is always returned, even if not specified. {@link ChangeRequestField} provides + * a list of fields that can be used. + */ + public static ChangeRequestOption fields(ChangeRequestField... fields) { + return new ChangeRequestOption( + DnsRpc.Option.FIELDS, + ChangeRequestField.selector(fields) + ); + } + } + + /** + * Class for specifying change request listing options. + */ + class ChangeRequestListOption extends AbstractOption implements Serializable { + + private static final long serialVersionUID = -900209143895376089L; + + ChangeRequestListOption(DnsRpc.Option option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify which fields of{@link ChangeRequest} should be returned by the + * service. + * + *

If this option is not provided all change request fields are returned. {@code + * ChangeRequestOption.fields} can be used to specify only the fields of interest. The ID of the + * change request is always returned, even if not specified. {@link ChangeRequestField} provides + * a list of fields that can be used. + */ + public static ChangeRequestListOption fields(ChangeRequestField... fields) { + StringBuilder builder = new StringBuilder(); + builder.append("changes(").append(ChangeRequestField.selector(fields)).append(')'); + return new ChangeRequestListOption(DnsRpc.Option.FIELDS, builder.toString()); + } + + /** + * Returns an option to specify a page token. + * + *

The page token (returned from a previous call to list) indicates from where listing should + * continue. + */ + public static ChangeRequestListOption pageToken(String pageToken) { + return new ChangeRequestListOption(DnsRpc.Option.PAGE_TOKEN, pageToken); + } + + /** + * The maximum number of change requests to return per RPC. + * + *

The server can return fewer change requests than requested. When there are more results + * than the page size, the server will return a page token that can be used to fetch other + * results. + */ + public static ChangeRequestListOption pageSize(int pageSize) { + return new ChangeRequestListOption(DnsRpc.Option.PAGE_SIZE, pageSize); + } + + /** + * Returns an option to specify whether the the change requests should be listed in ascending + * (most-recent last) or descending (most-recent first) order with respect to when the change + * request was accepted by the server. If this option is not provided, the listing order is + * undefined. + */ + public static ChangeRequestListOption sortOrder(SortingOrder order) { + return new ChangeRequestListOption(DnsRpc.Option.SORTING_ORDER, order.selector()); + } + } + + /** + * Creates a new zone. + * + *

Returns {@link Zone} object representing the new zone's information. In addition to the + * name, dns name and description (supplied by the user within the {@code zoneInfo} parameter), + * the returned object can include the following read-only fields supplied by the server: creation + * time, id, and list of name servers. The returned fields can be optionally restricted by + * specifying {@link ZoneOption}s. + * + * @throws DnsException upon failure + * @see Cloud DNS Managed Zones: + * create + */ + Zone create(ZoneInfo zoneInfo, ZoneOption... options); + + /** + * Returns the zone by the specified zone name. Returns {@code null} if the zone is not found. The + * returned fields can be optionally restricted by specifying {@link ZoneOption}s. + * + * @throws DnsException upon failure + * @see Cloud DNS Managed Zones: + * get + */ + Zone getZone(String zoneName, ZoneOption... options); + + /** + * Lists the zones inside the project. + * + *

This method returns zones in an unspecified order. New zones do not necessarily appear at + * the end of the list. Use {@link ZoneListOption} to restrict the listing to a domain name, set + * page size, and set page token. + * + * @return a page of zones + * @throws DnsException upon failure + * @see Cloud DNS Managed Zones: + * list + */ + Page listZones(ZoneListOption... options); + + /** + * Deletes an existing zone identified by name. Returns {@code true} if the zone was successfully + * deleted and {@code false} otherwise. + * + * @return {@code true} if zone was found and deleted and {@code false} otherwise + * @throws DnsException upon failure + * @see Cloud DNS Managed Zones: + * delete + */ + boolean delete(String zoneName); // delete does not admit any options + + /** + * Lists the DNS records in the zone identified by name. + * + *

The fields to be returned, page size and page tokens can be specified using {@link + * DnsRecordListOption}s. + * + * @throws DnsException upon failure or if the zone cannot be found + * @see Cloud DNS + * ResourceRecordSets: list + */ + Page listDnsRecords(String zoneName, DnsRecordListOption... options); + + /** + * Retrieves the information about the current project. The returned fields can be optionally + * restricted by specifying {@link ProjectOption}s. + * + * @throws DnsException upon failure + * @see Cloud DNS Projects: get + */ + ProjectInfo getProject(ProjectOption... fields); + + /** + * Submits a change request for the specified zone. The returned object contains the following + * read-only fields supplied by the server: id, start time and status. time, id, and list of name + * servers. The fields to be returned can be selected by {@link ChangeRequestOption}s. + * + * @return the new {@link ChangeRequest} + * @throws DnsException upon failure if zone is not found + * @see Cloud DNS Changes: create + */ + ChangeRequest applyChangeRequest(String zoneName, ChangeRequest changeRequest, + ChangeRequestOption... options); + + /** + * Retrieves updated information about a change request previously submitted for a zone identified + * by ID. Returns {@code null} if the request cannot be found and throws an exception if the zone + * does not exist. The fields to be returned using can be specified using {@link + * ChangeRequestOption}s. + * + * @throws DnsException upon failure or if the zone cannot be found + * @see Cloud DNS Chages: get + */ + ChangeRequest getChangeRequest(String zoneName, String changeRequestId, + ChangeRequestOption... options); + + /** + * Lists the change requests for the zone identified by name that were submitted to the service. + * + *

The sorting order for changes (based on when they were received by the server), fields to be + * returned, page size and page token can be specified using {@link ChangeRequestListOption}s. + * + * @return A page of change requests + * @throws DnsException upon failure or if the zone cannot be found + * @see Cloud DNS Chages: list + */ + Page listChangeRequests(String zoneName, ChangeRequestListOption... options); +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsException.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsException.java new file mode 100644 index 000000000000..25fafcb69df9 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsException.java @@ -0,0 +1,65 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import com.google.common.collect.ImmutableSet; +import com.google.gcloud.BaseServiceException; +import com.google.gcloud.RetryHelper.RetryHelperException; +import com.google.gcloud.RetryHelper.RetryInterruptedException; + +import java.io.IOException; +import java.util.Set; + +/** + * DNS service exception. + */ +public class DnsException extends BaseServiceException { + + // see: https://cloud.google.com/dns/troubleshooting + private static final Set RETRYABLE_ERRORS = ImmutableSet.of( + new Error(404, null), + new Error(409, null), + new Error(500, null), + new Error(502, null), + new Error(503, null)); + private static final long serialVersionUID = 490302380416260252L; + + public DnsException(IOException exception) { + super(exception, true); + } + + public DnsException(int code, String message) { + super(code, message, null, true); + } + + @Override + protected Set retryableErrors() { + return RETRYABLE_ERRORS; + } + + /** + * Translate RetryHelperException to the DnsException that caused the error. This method will + * always throw an exception. + * + * @throws DnsException when {@code ex} was caused by a {@code DnsException} + * @throws RetryInterruptedException when {@code ex} is a {@code RetryInterruptedException} + */ + static DnsException translateAndThrow(RetryHelperException ex) { + BaseServiceException.translateAndPropagateIfPossible(ex); + throw new DnsException(UNKNOWN_CODE, ex.getMessage()); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsFactory.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsFactory.java new file mode 100644 index 000000000000..734652afb24d --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsFactory.java @@ -0,0 +1,25 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import com.google.gcloud.ServiceFactory; + +/** + * An interface for Dns factories. + */ +public interface DnsFactory extends ServiceFactory { +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsImpl.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsImpl.java new file mode 100644 index 000000000000..17521c13c625 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsImpl.java @@ -0,0 +1,320 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.gcloud.RetryHelper.RetryHelperException; +import static com.google.gcloud.RetryHelper.runWithRetries; +import static com.google.gcloud.dns.ChangeRequest.fromPb; + +import com.google.api.services.dns.model.Change; +import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.gcloud.BaseService; +import com.google.gcloud.Page; +import com.google.gcloud.PageImpl; +import com.google.gcloud.RetryHelper; +import com.google.gcloud.spi.DnsRpc; + +import java.util.Map; +import java.util.concurrent.Callable; + +/** + * A default implementation of Dns. + */ +final class DnsImpl extends BaseService implements Dns { + + private final DnsRpc dnsRpc; + + private static class ZonePageFetcher implements PageImpl.NextPageFetcher { + + private static final long serialVersionUID = 2158209410430566961L; + private final Map requestOptions; + private final DnsOptions serviceOptions; + + ZonePageFetcher(DnsOptions serviceOptions, String cursor, + Map optionMap) { + this.requestOptions = + PageImpl.nextRequestOptions(DnsRpc.Option.PAGE_TOKEN, cursor, optionMap); + this.serviceOptions = serviceOptions; + } + + @Override + public Page nextPage() { + return listZones(serviceOptions, requestOptions); + } + } + + private static class ChangeRequestPageFetcher implements PageImpl.NextPageFetcher { + + private static final long serialVersionUID = 4473265130673029139L; + private final String zoneName; + private final Map requestOptions; + private final DnsOptions serviceOptions; + + ChangeRequestPageFetcher(String zoneName, DnsOptions serviceOptions, String cursor, + Map optionMap) { + this.zoneName = zoneName; + this.requestOptions = + PageImpl.nextRequestOptions(DnsRpc.Option.PAGE_TOKEN, cursor, optionMap); + this.serviceOptions = serviceOptions; + } + + @Override + public Page nextPage() { + return listChangeRequests(zoneName, serviceOptions, requestOptions); + } + } + + private static class DnsRecordPageFetcher implements PageImpl.NextPageFetcher { + + private static final long serialVersionUID = -6039369212511530846L; + private final Map requestOptions; + private final DnsOptions serviceOptions; + private final String zoneName; + + DnsRecordPageFetcher(String zoneName, DnsOptions serviceOptions, String cursor, + Map optionMap) { + this.zoneName = zoneName; + this.requestOptions = + PageImpl.nextRequestOptions(DnsRpc.Option.PAGE_TOKEN, cursor, optionMap); + this.serviceOptions = serviceOptions; + } + + @Override + public Page nextPage() { + return listDnsRecords(zoneName, serviceOptions, requestOptions); + } + } + + DnsImpl(DnsOptions options) { + super(options); + dnsRpc = options.rpc(); + } + + @Override + public Page listZones(ZoneListOption... options) { + return listZones(options(), optionMap(options)); + } + + private static Page listZones(final DnsOptions serviceOptions, + final Map optionsMap) { + // define transformation function + // this differs from the other list operations since zone is functional and requires dns service + Function pbToZoneFunction = new Function() { + @Override + public Zone apply( + com.google.api.services.dns.model.ManagedZone zonePb) { + return Zone.fromPb(serviceOptions.service(), zonePb); + } + }; + try { + // get a list of managed zones + final DnsRpc rpc = serviceOptions.rpc(); + DnsRpc.ListResult result = + runWithRetries(new Callable>() { + @Override + public DnsRpc.ListResult call() { + return rpc.listZones(optionsMap); + } + }, serviceOptions.retryParams(), EXCEPTION_HANDLER); + String cursor = result.pageToken(); + // transform that list into zone objects + Iterable zones = result.results() == null + ? ImmutableList.of() : Iterables.transform(result.results(), pbToZoneFunction); + return new PageImpl<>(new ZonePageFetcher(serviceOptions, cursor, optionsMap), + cursor, zones); + } catch (RetryHelperException e) { + throw DnsException.translateAndThrow(e); + } + } + + @Override + public Page listChangeRequests(String zoneName, + ChangeRequestListOption... options) { + return listChangeRequests(zoneName, options(), optionMap(options)); + } + + private static Page listChangeRequests(final String zoneName, + final DnsOptions serviceOptions, final Map optionsMap) { + try { + // get a list of changes + final DnsRpc rpc = serviceOptions.rpc(); + DnsRpc.ListResult result = runWithRetries(new Callable>() { + @Override + public DnsRpc.ListResult call() { + return rpc.listChangeRequests(zoneName, optionsMap); + } + }, serviceOptions.retryParams(), EXCEPTION_HANDLER); + String cursor = result.pageToken(); + // transform that list into change request objects + Iterable changes = result.results() == null + ? ImmutableList.of() + : Iterables.transform(result.results(), ChangeRequest.FROM_PB_FUNCTION); + return new PageImpl<>(new ChangeRequestPageFetcher(zoneName, serviceOptions, cursor, + optionsMap), cursor, changes); + } catch (RetryHelperException e) { + throw DnsException.translateAndThrow(e); + } + } + + @Override + public Page listDnsRecords(String zoneName, DnsRecordListOption... options) { + return listDnsRecords(zoneName, options(), optionMap(options)); + } + + private static Page listDnsRecords(final String zoneName, + final DnsOptions serviceOptions, final Map optionsMap) { + try { + // get a list of resource record sets + final DnsRpc rpc = serviceOptions.rpc(); + DnsRpc.ListResult result = runWithRetries( + new Callable>() { + @Override + public DnsRpc.ListResult call() { + return rpc.listDnsRecords(zoneName, optionsMap); + } + }, serviceOptions.retryParams(), EXCEPTION_HANDLER); + String cursor = result.pageToken(); + // transform that list into dns records + Iterable records = result.results() == null + ? ImmutableList.of() + : Iterables.transform(result.results(), DnsRecord.FROM_PB_FUNCTION); + return new PageImpl<>(new DnsRecordPageFetcher(zoneName, serviceOptions, cursor, optionsMap), + cursor, records); + } catch (RetryHelperException e) { + throw DnsException.translateAndThrow(e); + } + } + + @Override + public Zone create(final ZoneInfo zoneInfo, Dns.ZoneOption... options) { + final Map optionsMap = optionMap(options); + try { + com.google.api.services.dns.model.ManagedZone answer = runWithRetries( + new Callable() { + @Override + public com.google.api.services.dns.model.ManagedZone call() { + return dnsRpc.create(zoneInfo.toPb(), optionsMap); + } + }, options().retryParams(), EXCEPTION_HANDLER); + return answer == null ? null : Zone.fromPb(this, answer); + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + @Override + public Zone getZone(final String zoneName, Dns.ZoneOption... options) { + final Map optionsMap = optionMap(options); + try { + com.google.api.services.dns.model.ManagedZone answer = runWithRetries( + new Callable() { + @Override + public com.google.api.services.dns.model.ManagedZone call() { + return dnsRpc.getZone(zoneName, optionsMap); + } + }, options().retryParams(), EXCEPTION_HANDLER); + return answer == null ? null : Zone.fromPb(this, answer); + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + @Override + public boolean delete(final String zoneName) { + try { + return runWithRetries(new Callable() { + @Override + public Boolean call() { + return dnsRpc.deleteZone(zoneName); + } + }, options().retryParams(), EXCEPTION_HANDLER); + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + @Override + public ProjectInfo getProject(Dns.ProjectOption... fields) { + final Map optionsMap = optionMap(fields); + try { + com.google.api.services.dns.model.Project answer = runWithRetries( + new Callable() { + @Override + public com.google.api.services.dns.model.Project call() { + return dnsRpc.getProject(optionsMap); + } + }, options().retryParams(), EXCEPTION_HANDLER); + return answer == null ? null : ProjectInfo.fromPb(answer); // should never be null + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + @Override + public ChangeRequest applyChangeRequest(final String zoneName, final ChangeRequest changeRequest, + Dns.ChangeRequestOption... options) { + final Map optionsMap = optionMap(options); + try { + com.google.api.services.dns.model.Change answer = + runWithRetries( + new Callable() { + @Override + public com.google.api.services.dns.model.Change call() { + return dnsRpc.applyChangeRequest(zoneName, changeRequest.toPb(), optionsMap); + } + }, options().retryParams(), EXCEPTION_HANDLER); + return answer == null ? null : fromPb(answer); // should never be null + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + @Override + public ChangeRequest getChangeRequest(final String zoneName, final String changeRequestId, + Dns.ChangeRequestOption... options) { + final Map optionsMap = optionMap(options); + try { + com.google.api.services.dns.model.Change answer = + runWithRetries( + new Callable() { + @Override + public com.google.api.services.dns.model.Change call() { + return dnsRpc.getChangeRequest(zoneName, changeRequestId, optionsMap); + } + }, options().retryParams(), EXCEPTION_HANDLER); + return answer == null ? null : fromPb(answer); + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + private Map optionMap(AbstractOption... options) { + Map temp = Maps.newEnumMap(DnsRpc.Option.class); + for (AbstractOption option : options) { + Object prev = temp.put(option.rpcOption(), option.value()); + checkArgument(prev == null, "Duplicate option %s", option); + } + return ImmutableMap.copyOf(temp); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsOptions.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsOptions.java new file mode 100644 index 000000000000..d9317546cea0 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsOptions.java @@ -0,0 +1,108 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import com.google.common.collect.ImmutableSet; +import com.google.gcloud.ServiceOptions; +import com.google.gcloud.spi.DefaultDnsRpc; +import com.google.gcloud.spi.DnsRpc; +import com.google.gcloud.spi.DnsRpcFactory; + +import java.util.Set; + +public class DnsOptions extends ServiceOptions { + + private static final long serialVersionUID = -519128051411747771L; + private static final String GC_DNS_RW = "https://www.googleapis.com/auth/ndev.clouddns.readwrite"; + private static final Set SCOPES = ImmutableSet.of(GC_DNS_RW); + + public static class DefaultDnsFactory implements DnsFactory { + private static final DnsFactory INSTANCE = new DefaultDnsFactory(); + + @Override + public Dns create(DnsOptions options) { + return new DnsImpl(options); + } + } + + public static class DefaultDnsRpcFactory implements DnsRpcFactory { + + private static final DnsRpcFactory INSTANCE = new DefaultDnsRpcFactory(); + + @Override + public DnsRpc create(DnsOptions options) { + return new DefaultDnsRpc(options); + } + } + + public static class Builder extends ServiceOptions.Builder { + + private Builder() { + } + + private Builder(DnsOptions options) { + super(options); + } + + @Override + public DnsOptions build() { + return new DnsOptions(this); + } + } + + private DnsOptions(Builder builder) { + super(DnsFactory.class, DnsRpcFactory.class, builder); + } + + @SuppressWarnings("unchecked") + @Override + protected DnsFactory defaultServiceFactory() { + return DefaultDnsFactory.INSTANCE; + } + + @SuppressWarnings("unchecked") + @Override + protected DnsRpcFactory defaultRpcFactory() { + return DefaultDnsRpcFactory.INSTANCE; + } + + @Override + protected Set scopes() { + return SCOPES; + } + + @SuppressWarnings("unchecked") + @Override + public Builder toBuilder() { + return new Builder(this); + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof DnsOptions && baseEquals((DnsOptions) obj); + } + + @Override + public int hashCode() { + return baseHashCode(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsRecord.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsRecord.java new file mode 100644 index 000000000000..c4e710bd0365 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/DnsRecord.java @@ -0,0 +1,318 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.common.base.Function; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.primitives.Ints; + +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * A class that represents a Google Cloud DNS record set. + * + *

A {@code DnsRecord} is the unit of data that will be returned by the DNS servers upon a DNS + * request for a specific domain. The {@code DnsRecord} holds the current state of the DNS records + * that make up a zone. You can read the records but you cannot modify them directly. Rather, you + * edit the records in a zone by creating a ChangeRequest. + * + * @see Google Cloud DNS + * documentation + */ +public class DnsRecord implements Serializable { + + static final Function FROM_PB_FUNCTION = + new Function() { + @Override + public DnsRecord apply(com.google.api.services.dns.model.ResourceRecordSet pb) { + return DnsRecord.fromPb(pb); + } + }; + static final Function TO_PB_FUNCTION = + new Function() { + @Override + public com.google.api.services.dns.model.ResourceRecordSet apply(DnsRecord error) { + return error.toPb(); + } + }; + private static final long serialVersionUID = 8148009870800115261L; + private final String name; + private final List rrdatas; + private final Integer ttl; // this is in seconds + private final Type type; + + /** + * Enum for the DNS record types supported by Cloud DNS. + * + *

Google Cloud DNS currently supports records of type A, AAAA, CNAME, MX NAPTR, NS, PTR, SOA, + * SPF, SRV, TXT. + * + * @see Cloud DNS + * supported record types + */ + public enum Type { + /** + * Address record, which is used to map host names to their IPv4 address. + */ + A, + /** + * IPv6 Address record, which is used to map host names to their IPv6 address. + */ + AAAA, + /** + * Canonical name record, which is used to alias names. + */ + CNAME, + /** + * Mail exchange record, which is used in routing requests to mail servers. + */ + MX, + /** + * Naming authority pointer record, defined by RFC3403. + */ + NAPTR, + /** + * Name server record, which delegates a DNS zone to an authoritative server. + */ + NS, + /** + * Pointer record, which is often used for reverse DNS lookups. + */ + PTR, + /** + * Start of authority record, which specifies authoritative information about a DNS zone. + */ + SOA, + /** + * Sender policy framework record, which is used in email validation systems. + */ + SPF, + /** + * Service locator record, which is used by some voice over IP, instant messaging protocols and + * other applications. + */ + SRV, + /** + * Text record, which can contain arbitrary text and can also be used to define machine readable + * data such as security or abuse prevention information. + */ + TXT + } + + /** + * A builder of {@link DnsRecord}. + */ + public static class Builder { + + private List rrdatas = new LinkedList<>(); + private String name; + private Integer ttl; + private Type type; + + private Builder(String name, Type type) { + this.name = checkNotNull(name); + this.type = checkNotNull(type); + } + + /** + * Creates a builder and pre-populates attributes with the values from the provided {@code + * DnsRecord} instance. + */ + private Builder(DnsRecord record) { + this.name = record.name; + this.ttl = record.ttl; + this.type = record.type; + this.rrdatas.addAll(record.rrdatas); + } + + /** + * Adds a record to the record set. The records should be as defined in RFC 1035 (section 5) and + * RFC 1034 (section 3.6.1). Examples of records are available in Google DNS documentation. + * + * @see Google + * DNS documentation . + */ + public Builder addRecord(String record) { + this.rrdatas.add(checkNotNull(record)); + return this; + } + + /** + * Removes a record from the set. An exact match is required. + */ + public Builder removeRecord(String record) { + this.rrdatas.remove(checkNotNull(record)); + return this; + } + + /** + * Removes all the records. + */ + public Builder clearRecords() { + this.rrdatas.clear(); + return this; + } + + /** + * Replaces the current records with the provided list of records. + */ + public Builder records(List records) { + this.rrdatas = Lists.newLinkedList(checkNotNull(records)); + return this; + } + + /** + * Sets name for this DNS record set. For example, www.example.com. + */ + public Builder name(String name) { + this.name = checkNotNull(name); + return this; + } + + /** + * Sets the time that this record can be cached by resolvers. This number must be non-negative. + * The maximum duration must be equivalent to at most {@link Integer#MAX_VALUE} seconds. + * + * @param duration A non-negative number of time units + * @param unit The unit of the ttl parameter + */ + public Builder ttl(int duration, TimeUnit unit) { + checkArgument(duration >= 0, + "Duration cannot be negative. The supplied value was %s.", duration); + checkNotNull(unit); + // we cannot have long because pb does not support it + long converted = unit.toSeconds(duration); + ttl = Ints.checkedCast(converted); + return this; + } + + /** + * The identifier of a supported record type, for example, A, AAAA, MX, TXT, and so on. + */ + public Builder type(Type type) { + this.type = checkNotNull(type); + return this; + } + + /** + * Builds the DNS record. + */ + public DnsRecord build() { + return new DnsRecord(this); + } + } + + private DnsRecord(Builder builder) { + this.name = builder.name; + this.rrdatas = ImmutableList.copyOf(builder.rrdatas); + this.ttl = builder.ttl; + this.type = builder.type; + } + + /** + * Creates a builder pre-populated with the attribute values of this instance. + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Creates a {@code DnsRecord} builder for the given {@code name} and {@code type}. + */ + public static Builder builder(String name, Type type) { + return new Builder(name, type); + } + + /** + * Returns the user-assigned name of this DNS record. + */ + public String name() { + return name; + } + + /** + * Returns a list of DNS record stored in this record set. + */ + public List records() { + return rrdatas; + } + + /** + * Returns the number of seconds that this DnsResource can be cached by resolvers. + */ + public Integer ttl() { + return ttl; + } + + /** + * Returns the type of this DNS record. + */ + public Type type() { + return type; + } + + @Override + public int hashCode() { + return Objects.hash(name, rrdatas, ttl, type); + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof DnsRecord) && Objects.equals(this.toPb(), ((DnsRecord) obj).toPb()); + } + + com.google.api.services.dns.model.ResourceRecordSet toPb() { + com.google.api.services.dns.model.ResourceRecordSet pb = + new com.google.api.services.dns.model.ResourceRecordSet(); + pb.setName(this.name()); + pb.setRrdatas(this.records()); + pb.setTtl(this.ttl()); + pb.setType(this.type().name()); + return pb; + } + + static DnsRecord fromPb(com.google.api.services.dns.model.ResourceRecordSet pb) { + Builder builder = builder(pb.getName(), Type.valueOf(pb.getType())); + if (pb.getRrdatas() != null) { + builder.records(pb.getRrdatas()); + } + if (pb.getTtl() != null) { + builder.ttl(pb.getTtl(), TimeUnit.SECONDS); + } + return builder.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", name()) + .add("rrdatas", records()) + .add("ttl", ttl()) + .add("type", type()) + .toString(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ProjectInfo.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ProjectInfo.java new file mode 100644 index 000000000000..f7b12b94bae8 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ProjectInfo.java @@ -0,0 +1,287 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static com.google.api.client.repackaged.com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.MoreObjects; + +import java.io.Serializable; +import java.math.BigInteger; +import java.util.Objects; + +/** + * The class provides the Google Cloud DNS information associated with this project. A project is a + * top level container for resources including {@code Zone}s. Projects can be created only in + * the APIs console. + * + * @see Google Cloud DNS documentation + */ +public class ProjectInfo implements Serializable { + + private static final long serialVersionUID = 8696578863323485036L; + private final String id; + private final BigInteger number; + private final Quota quota; + + /** + * This class represents quotas assigned to the {@code ProjectInfo}. + * + * @see Google Cloud DNS + * documentation + */ + public static class Quota implements Serializable { + + private static final long serialVersionUID = 6854685970605363639L; + private final int zones; + private final int resourceRecordsPerRrset; + private final int rrsetAdditionsPerChange; + private final int rrsetDeletionsPerChange; + private final int rrsetsPerZone; + private final int totalRrdataSizePerChange; + + /** + * Creates an instance of {@code Quota}. + * + *

This is the only way of creating an instance of {@code Quota}. As the service does not + * allow for specifying options, quota is an "all-or-nothing object" and we do not need a + * builder. + */ + Quota(int zones, + int resourceRecordsPerRrset, + int rrsetAdditionsPerChange, + int rrsetDeletionsPerChange, + int rrsetsPerZone, + int totalRrdataSizePerChange) { + this.zones = zones; + this.resourceRecordsPerRrset = resourceRecordsPerRrset; + this.rrsetAdditionsPerChange = rrsetAdditionsPerChange; + this.rrsetDeletionsPerChange = rrsetDeletionsPerChange; + this.rrsetsPerZone = rrsetsPerZone; + this.totalRrdataSizePerChange = totalRrdataSizePerChange; + } + + /** + * Returns the maximum allowed number of zones in the project. + */ + public int zones() { + return zones; + } + + /** + * Returns the maximum allowed number of records per {@link DnsRecord}. + */ + public int resourceRecordsPerRrset() { + return resourceRecordsPerRrset; + } + + /** + * Returns the maximum allowed number of {@link DnsRecord}s to add per {@link ChangeRequest}. + */ + public int rrsetAdditionsPerChange() { + return rrsetAdditionsPerChange; + } + + /** + * Returns the maximum allowed number of {@link DnsRecord}s to delete per {@link + * ChangeRequest}. + */ + public int rrsetDeletionsPerChange() { + return rrsetDeletionsPerChange; + } + + /** + * Returns the maximum allowed number of {@link DnsRecord}s per {@link ZoneInfo} in the + * project. + */ + public int rrsetsPerZone() { + return rrsetsPerZone; + } + + /** + * Returns the maximum allowed size for total records in one ChangesRequest in bytes. + */ + public int totalRrdataSizePerChange() { + return totalRrdataSizePerChange; + } + + @Override + public boolean equals(Object other) { + return (other instanceof Quota) && this.toPb().equals(((Quota) other).toPb()); + } + + @Override + public int hashCode() { + return Objects.hash(zones, resourceRecordsPerRrset, rrsetAdditionsPerChange, + rrsetDeletionsPerChange, rrsetsPerZone, totalRrdataSizePerChange); + } + + com.google.api.services.dns.model.Quota toPb() { + com.google.api.services.dns.model.Quota pb = new com.google.api.services.dns.model.Quota(); + pb.setManagedZones(zones); + pb.setResourceRecordsPerRrset(resourceRecordsPerRrset); + pb.setRrsetAdditionsPerChange(rrsetAdditionsPerChange); + pb.setRrsetDeletionsPerChange(rrsetDeletionsPerChange); + pb.setRrsetsPerManagedZone(rrsetsPerZone); + pb.setTotalRrdataSizePerChange(totalRrdataSizePerChange); + return pb; + } + + static Quota fromPb(com.google.api.services.dns.model.Quota pb) { + Quota quota = new Quota(pb.getManagedZones(), + pb.getResourceRecordsPerRrset(), + pb.getRrsetAdditionsPerChange(), + pb.getRrsetDeletionsPerChange(), + pb.getRrsetsPerManagedZone(), + pb.getTotalRrdataSizePerChange() + ); + return quota; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("zones", zones) + .add("resourceRecordsPerRrset", resourceRecordsPerRrset) + .add("rrsetAdditionsPerChange", rrsetAdditionsPerChange) + .add("rrsetDeletionsPerChange", rrsetDeletionsPerChange) + .add("rrsetsPerZone", rrsetsPerZone) + .add("totalRrdataSizePerChange", totalRrdataSizePerChange) + .toString(); + } + } + + /** + * A builder for {@code ProjectInfo}. + */ + static class Builder { + private String id; + private BigInteger number; + private Quota quota; + + private Builder() { + } + + /** + * Sets an id of the project. + */ + Builder id(String id) { + this.id = checkNotNull(id); + return this; + } + + /** + * Sets a number of the project. + */ + Builder number(BigInteger number) { + this.number = checkNotNull(number); + return this; + } + + /** + * Sets quotas assigned to the project. + */ + Builder quota(Quota quota) { + this.quota = checkNotNull(quota); + return this; + } + + /** + * Builds an instance of the {@code ProjectInfo}. + */ + ProjectInfo build() { + return new ProjectInfo(this); + } + } + + private ProjectInfo(Builder builder) { + this.id = builder.id; + this.number = builder.number; + this.quota = builder.quota; + } + + /** + * Returns a builder for {@code ProjectInfo}. + */ + static Builder builder() { + return new Builder(); + } + + /** + * Returns the {@code Quota} object which contains quotas assigned to this project. + */ + public Quota quota() { + return quota; + } + + /** + * Returns project number. For internal use only. + */ + BigInteger number() { + return number; + } + + /** + * Returns project id. For internal use only. + */ + String id() { + return id; + } + + com.google.api.services.dns.model.Project toPb() { + com.google.api.services.dns.model.Project pb = new com.google.api.services.dns.model.Project(); + pb.setId(id); + pb.setNumber(number); + if (this.quota != null) { + pb.setQuota(quota.toPb()); + } + return pb; + } + + static ProjectInfo fromPb(com.google.api.services.dns.model.Project pb) { + Builder builder = builder(); + if (pb.getId() != null) { + builder.id(pb.getId()); + } + if (pb.getNumber() != null) { + builder.number(pb.getNumber()); + } + if (pb.getQuota() != null) { + builder.quota(Quota.fromPb(pb.getQuota())); + } + return builder.build(); + } + + @Override + public boolean equals(Object other) { + return (other instanceof ProjectInfo) && toPb().equals(((ProjectInfo) other).toPb()); + } + + @Override + public int hashCode() { + return Objects.hash(id, number, quota); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("id", id) + .add("number", number) + .add("quota", quota) + .toString(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/Zone.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/Zone.java new file mode 100644 index 000000000000..41507647543a --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/Zone.java @@ -0,0 +1,217 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.gcloud.Page; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.List; +import java.util.Objects; + +/** + * A Google Cloud DNS Zone object. + * + *

A zone is the container for all of your DNS records that share the same DNS name prefix, for + * example, example.com. Zones are automatically assigned a set of name servers when they are + * created to handle responding to DNS queries for that zone. A zone has quotas for the number of + * resource records that it can include. + * + * @see Google Cloud DNS managed zone + * documentation + */ +public class Zone extends ZoneInfo { + + private static final long serialVersionUID = -5817771337847861598L; + private final DnsOptions options; + private transient Dns dns; + + /** + * Builder for {@code Zone}. + */ + public static class Builder extends ZoneInfo.Builder { + private final Dns dns; + private final ZoneInfo.BuilderImpl infoBuilder; + + private Builder(Zone zone) { + this.dns = zone.dns; + this.infoBuilder = new ZoneInfo.BuilderImpl(zone); + } + + @Override + public Builder name(String name) { + infoBuilder.name(name); + return this; + } + + @Override + Builder id(String id) { + infoBuilder.id(id); + return this; + } + + @Override + Builder creationTimeMillis(long creationTimeMillis) { + infoBuilder.creationTimeMillis(creationTimeMillis); + return this; + } + + @Override + public Builder dnsName(String dnsName) { + infoBuilder.dnsName(dnsName); + return this; + } + + @Override + public Builder description(String description) { + infoBuilder.description(description); + return this; + } + + @Override + Builder nameServerSet(String nameServerSet) { + infoBuilder.nameServerSet(nameServerSet); + return this; + } + + @Override + Builder nameServers(List nameServers) { + infoBuilder.nameServers(nameServers); // infoBuilder makes a copy + return this; + } + + @Override + public Zone build() { + return new Zone(dns, infoBuilder); + } + } + + Zone(Dns dns, ZoneInfo.BuilderImpl infoBuilder) { + super(infoBuilder); + this.dns = dns; + this.options = dns.options(); + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Retrieves the latest information about the zone. The method retrieves the zone by name. + * + * @param options optional restriction on what fields should be fetched + * @return zone object containing updated information or {@code null} if not not found + * @throws DnsException upon failure + */ + public Zone reload(Dns.ZoneOption... options) { + return dns.getZone(name(), options); + } + + /** + * Deletes the zone. The method deletes the zone by name. + * + * @return {@code true} is zone was found and deleted and {@code false} otherwise + * @throws DnsException upon failure + */ + public boolean delete() { + return dns.delete(name()); + } + + /** + * Lists all {@link DnsRecord}s associated with this zone. The method searches for zone by name. + * + * @param options optional restriction on listing and fields of {@link DnsRecord}s returned + * @return a page of DNS records + * @throws DnsException upon failure or if the zone is not found + */ + public Page listDnsRecords(Dns.DnsRecordListOption... options) { + return dns.listDnsRecords(name(), options); + } + + /** + * Submits {@link ChangeRequest} to the service for it to applied to this zone. The method + * searches for zone by name. + * + * @param options optional restriction on what fields of {@link ChangeRequest} should be returned + * @return ChangeRequest with server-assigned ID + * @throws DnsException upon failure or if the zone is not found + */ + public ChangeRequest applyChangeRequest(ChangeRequest changeRequest, + Dns.ChangeRequestOption... options) { + checkNotNull(changeRequest); + return dns.applyChangeRequest(name(), changeRequest, options); + } + + /** + * Retrieves an updated information about a change request previously submitted to be applied to + * this zone. Returns a {@link ChangeRequest} or {@code null} if the change request was not found. + * Throws {@link DnsException} if the zone is not found. + * + * @param options optional restriction on what fields of {@link ChangeRequest} should be returned + * @return updated ChangeRequest + * @throws DnsException upon failure or if the zone is not found + * @throws NullPointerException if {@code changeRequestId} is null + */ + public ChangeRequest getChangeRequest(String changeRequestId, + Dns.ChangeRequestOption... options) { + checkNotNull(changeRequestId); + return dns.getChangeRequest(name(), changeRequestId, options); + } + + /** + * Retrieves all change requests for this zone. The method searches for zone by name. Returns a + * page of {@link ChangeRequest}s. + * + * @param options optional restriction on listing and fields to be returned + * @return a page of change requests + * @throws DnsException upon failure or if the zone is not found + */ + public Page listChangeRequests(Dns.ChangeRequestListOption... options) { + return dns.listChangeRequests(name(), options); + } + + /** + * Returns the {@link Dns} service object associated with this zone. + */ + public Dns dns() { + return this.dns; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Zone && Objects.equals(toPb(), ((Zone) obj).toPb()) + && Objects.equals(options, ((Zone) obj).options); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), options); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + this.dns = options.service(); + } + + static Zone fromPb(Dns dns, com.google.api.services.dns.model.ManagedZone zone) { + ZoneInfo info = ZoneInfo.fromPb(zone); + return new Zone(dns, new ZoneInfo.BuilderImpl(info)); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ZoneInfo.java b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ZoneInfo.java new file mode 100644 index 000000000000..7dffbcdd365c --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/dns/ZoneInfo.java @@ -0,0 +1,317 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import org.joda.time.DateTime; +import org.joda.time.format.ISODateTimeFormat; + +import java.io.Serializable; +import java.math.BigInteger; +import java.util.List; +import java.util.Objects; + +/** + * A {@code Zone} represents a DNS zone hosted by the Google Cloud DNS service. A zone is a subtree + * of the DNS namespace under one administrative responsibility. See Google Cloud DNS documentation for + * more information. + */ +public class ZoneInfo implements Serializable { + + private static final long serialVersionUID = 201601191647L; + private final String name; + private final String id; + private final Long creationTimeMillis; + private final String dnsName; + private final String description; + private final String nameServerSet; + private final List nameServers; + + /** + * Builder for {@code ZoneInfo}. + */ + public abstract static class Builder { + /** + * Sets a mandatory user-provided name for the zone. It must be unique within the project. + */ + public abstract Builder name(String name); + + /** + * Sets an id for the zone which is assigned to the zone by the server. + */ + abstract Builder id(String id); + + /** + * Sets the time when this zone was created. + */ + abstract Builder creationTimeMillis(long creationTimeMillis); + + /** + * Sets a mandatory DNS name of this zone, for instance "example.com.". + */ + public abstract Builder dnsName(String dnsName); + + /** + * Sets a mandatory description for this zone. The value is a string of at most 1024 characters + * which has no effect on the zone's function. + */ + public abstract Builder description(String description); + + /** + * Optionally specifies the NameServerSet for this zone. A NameServerSet is a set of DNS name + * servers that all host the same zones. Most users will not need to specify this value. + */ + abstract Builder nameServerSet(String nameServerSet); + // this should not be included in tooling as per the service owners + + /** + * Sets a list of servers that hold the information about the zone. This information is provided + * by Google Cloud DNS and is read only. + */ + abstract Builder nameServers(List nameServers); + + /** + * Builds the instance of {@code ZoneInfo} based on the information set by this builder. + */ + public abstract ZoneInfo build(); + } + + static class BuilderImpl extends Builder { + private String name; + private String id; + private Long creationTimeMillis; + private String dnsName; + private String description; + private String nameServerSet; + private List nameServers; + + private BuilderImpl(String name) { + this.name = checkNotNull(name); + } + + /** + * Creates a builder from an existing ZoneInfo object. + */ + BuilderImpl(ZoneInfo info) { + this.name = info.name; + this.id = info.id; + this.creationTimeMillis = info.creationTimeMillis; + this.dnsName = info.dnsName; + this.description = info.description; + this.nameServerSet = info.nameServerSet; + if (info.nameServers != null) { + this.nameServers = ImmutableList.copyOf(info.nameServers); + } + } + + @Override + public Builder name(String name) { + this.name = checkNotNull(name); + return this; + } + + @Override + Builder id(String id) { + this.id = id; + return this; + } + + @Override + Builder creationTimeMillis(long creationTimeMillis) { + this.creationTimeMillis = creationTimeMillis; + return this; + } + + @Override + public Builder dnsName(String dnsName) { + this.dnsName = checkNotNull(dnsName); + return this; + } + + @Override + public Builder description(String description) { + this.description = checkNotNull(description); + return this; + } + + @Override + Builder nameServerSet(String nameServerSet) { + this.nameServerSet = checkNotNull(nameServerSet); + return this; + } + + @Override + Builder nameServers(List nameServers) { + checkNotNull(nameServers); + this.nameServers = Lists.newLinkedList(nameServers); + return this; + } + + @Override + public ZoneInfo build() { + return new ZoneInfo(this); + } + } + + ZoneInfo(BuilderImpl builder) { + this.name = builder.name; + this.id = builder.id; + this.creationTimeMillis = builder.creationTimeMillis; + this.dnsName = builder.dnsName; + this.description = builder.description; + this.nameServerSet = builder.nameServerSet; + this.nameServers = builder.nameServers == null + ? null : ImmutableList.copyOf(builder.nameServers); + } + + /** + * Returns a builder for {@code ZoneInfo} with an assigned {@code name}. + */ + public static Builder builder(String name) { + return new BuilderImpl(name); + } + + /** + * Returns the user-defined name of the zone. + */ + public String name() { + return name; + } + + /** + * Returns the read-only zone id assigned by the server. + */ + public String id() { + return id; + } + + /** + * Returns the time when this zone was created on the server. + */ + public Long creationTimeMillis() { + return creationTimeMillis; + } + + /** + * Returns the DNS name of this zone, for instance "example.com.". + */ + public String dnsName() { + return dnsName; + } + + /** + * Returns the description of this zone. + */ + public String description() { + return description; + } + + /** + * Returns the optionally specified set of DNS name servers that all host this zone. This value is + * set only for specific use cases and is left empty for vast majority of users. + */ + public String nameServerSet() { + return nameServerSet; + } + + /** + * The nameservers that the zone should be delegated to. This is defined by the Google DNS cloud. + */ + public List nameServers() { + return nameServers == null ? ImmutableList.of() : nameServers; + } + + /** + * Returns a builder for {@code ZoneInfo} prepopulated with the metadata of this zone. + */ + public Builder toBuilder() { + return new BuilderImpl(this); + } + + com.google.api.services.dns.model.ManagedZone toPb() { + com.google.api.services.dns.model.ManagedZone pb = + new com.google.api.services.dns.model.ManagedZone(); + pb.setDescription(this.description()); + pb.setDnsName(this.dnsName()); + if (this.id() != null) { + pb.setId(new BigInteger(this.id())); + } + pb.setName(this.name()); + pb.setNameServers(this.nameServers); // do use real attribute value which may be null + pb.setNameServerSet(this.nameServerSet()); + if (this.creationTimeMillis() != null) { + pb.setCreationTime(ISODateTimeFormat.dateTime() + .withZoneUTC() + .print(this.creationTimeMillis())); + } + return pb; + } + + static ZoneInfo fromPb(com.google.api.services.dns.model.ManagedZone pb) { + Builder builder = new BuilderImpl(pb.getName()); + if (pb.getDescription() != null) { + builder.description(pb.getDescription()); + } + if (pb.getDnsName() != null) { + builder.dnsName(pb.getDnsName()); + } + if (pb.getId() != null) { + builder.id(pb.getId().toString()); + } + if (pb.getNameServers() != null) { + builder.nameServers(pb.getNameServers()); + } + if (pb.getNameServerSet() != null) { + builder.nameServerSet(pb.getNameServerSet()); + } + if (pb.getCreationTime() != null) { + builder.creationTimeMillis(DateTime.parse(pb.getCreationTime()).getMillis()); + } + return builder.build(); + } + + @Override + public boolean equals(Object obj) { + return obj != null && obj.getClass().equals(ZoneInfo.class) + && Objects.equals(toPb(), ((ZoneInfo) obj).toPb()); + } + + @Override + public int hashCode() { + return Objects.hash(name, id, creationTimeMillis, dnsName, + description, nameServerSet, nameServers); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", name()) + .add("id", id()) + .add("description", description()) + .add("dnsName", dnsName()) + .add("nameServerSet", nameServerSet()) + .add("nameServers", nameServers()) + .add("creationTimeMillis", creationTimeMillis()) + .toString(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/spi/DefaultDnsRpc.java b/gcloud-java-dns/src/main/java/com/google/gcloud/spi/DefaultDnsRpc.java new file mode 100644 index 000000000000..6ed9c7e0f216 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/spi/DefaultDnsRpc.java @@ -0,0 +1,194 @@ +package com.google.gcloud.spi; + +import static com.google.gcloud.spi.DnsRpc.ListResult.of; +import static com.google.gcloud.spi.DnsRpc.Option.DNS_NAME; +import static com.google.gcloud.spi.DnsRpc.Option.DNS_TYPE; +import static com.google.gcloud.spi.DnsRpc.Option.FIELDS; +import static com.google.gcloud.spi.DnsRpc.Option.NAME; +import static com.google.gcloud.spi.DnsRpc.Option.PAGE_SIZE; +import static com.google.gcloud.spi.DnsRpc.Option.PAGE_TOKEN; +import static com.google.gcloud.spi.DnsRpc.Option.SORTING_ORDER; +import static java.net.HttpURLConnection.HTTP_NOT_FOUND; + +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.json.jackson.JacksonFactory; +import com.google.api.services.dns.Dns; +import com.google.api.services.dns.model.Change; +import com.google.api.services.dns.model.ChangesListResponse; +import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.ManagedZonesListResponse; +import com.google.api.services.dns.model.Project; +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.api.services.dns.model.ResourceRecordSetsListResponse; +import com.google.gcloud.dns.DnsException; +import com.google.gcloud.dns.DnsOptions; + +import java.io.IOException; +import java.util.Map; + +/** + * A default implementation of the DnsRpc interface. + */ +public class DefaultDnsRpc implements DnsRpc { + + private static final String SORT_BY = "changeSequence"; + private final Dns dns; + private final DnsOptions options; + + private static DnsException translate(IOException exception) { + return new DnsException(exception); + } + + /** + * Constructs an instance of this rpc client with provided {@link DnsOptions}. + */ + public DefaultDnsRpc(DnsOptions options) { + HttpTransport transport = options.httpTransportFactory().create(); + HttpRequestInitializer initializer = options.httpRequestInitializer(); + this.dns = new Dns.Builder(transport, new JacksonFactory(), initializer) + .setRootUrl(options.host()) + .setApplicationName(options.applicationName()) + .build(); + this.options = options; + } + + @Override + public ManagedZone create(ManagedZone zone, Map options) throws DnsException { + try { + return dns.managedZones() + .create(this.options.projectId(), zone) + .setFields(FIELDS.getString(options)) + .execute(); + } catch (IOException ex) { + throw translate(ex); + } + } + + @Override + public ManagedZone getZone(String zoneName, Map options) throws DnsException { + // just fields option + try { + return dns.managedZones().get(this.options.projectId(), zoneName) + .setFields(FIELDS.getString(options)) + .execute(); + } catch (IOException ex) { + DnsException serviceException = translate(ex); + if (serviceException.code() == HTTP_NOT_FOUND) { + return null; + } + throw serviceException; + } + } + + @Override + public ListResult listZones(Map options) throws DnsException { + // fields, page token, page size + try { + ManagedZonesListResponse zoneList = dns.managedZones().list(this.options.projectId()) + .setFields(FIELDS.getString(options)) + .setMaxResults(PAGE_SIZE.getInt(options)) + .setDnsName(DNS_NAME.getString(options)) + .setPageToken(PAGE_TOKEN.getString(options)) + .execute(); + return of(zoneList.getNextPageToken(), zoneList.getManagedZones()); + } catch (IOException ex) { + throw translate(ex); + } + } + + @Override + public boolean deleteZone(String zoneName) throws DnsException { + try { + dns.managedZones().delete(this.options.projectId(), zoneName).execute(); + return true; + } catch (IOException ex) { + DnsException serviceException = translate(ex); + if (serviceException.code() == HTTP_NOT_FOUND) { + return false; + } + throw serviceException; + } + } + + @Override + public ListResult listDnsRecords(String zoneName, Map options) + throws DnsException { + // options are fields, page token, dns name, type + try { + ResourceRecordSetsListResponse response = dns.resourceRecordSets() + .list(this.options.projectId(), zoneName) + .setFields(FIELDS.getString(options)) + .setPageToken(PAGE_TOKEN.getString(options)) + .setMaxResults(PAGE_SIZE.getInt(options)) + .setName(NAME.getString(options)) + .setType(DNS_TYPE.getString(options)) + .execute(); + return of(response.getNextPageToken(), response.getRrsets()); + } catch (IOException ex) { + throw translate(ex); + } + } + + @Override + public Project getProject(Map options) throws DnsException { + try { + return dns.projects().get(this.options.projectId()) + .setFields(FIELDS.getString(options)).execute(); + } catch (IOException ex) { + throw translate(ex); + } + } + + @Override + public Change applyChangeRequest(String zoneName, Change changeRequest, Map options) + throws DnsException { + try { + return dns.changes().create(this.options.projectId(), zoneName, changeRequest) + .setFields(FIELDS.getString(options)) + .execute(); + } catch (IOException ex) { + throw translate(ex); + } + } + + @Override + public Change getChangeRequest(String zoneName, String changeRequestId, Map options) + throws DnsException { + try { + return dns.changes().get(this.options.projectId(), zoneName, changeRequestId) + .setFields(FIELDS.getString(options)) + .execute(); + } catch (IOException ex) { + DnsException serviceException = translate(ex); + if (serviceException.code() == HTTP_NOT_FOUND) { + if (serviceException.location().equals("entity.parameters.changeId")) { + // the change id was not found, but the zone exists + return null; + } + // the zone does not exist, so throw an exception + } + throw serviceException; + } + } + + @Override + public ListResult listChangeRequests(String zoneName, Map options) + throws DnsException { + // options are fields, page token, page size, sort order + try { + Dns.Changes.List request = dns.changes().list(this.options.projectId(), zoneName) + .setFields(FIELDS.getString(options)) + .setMaxResults(PAGE_SIZE.getInt(options)) + .setPageToken(PAGE_TOKEN.getString(options)); + if (SORTING_ORDER.getString(options) != null) { + // todo check and change if more sorting options are implemented, issue #604 + request = request.setSortBy(SORT_BY).setSortOrder(SORTING_ORDER.getString(options)); + } + ChangesListResponse response = request.execute(); + return ListResult.of(response.getNextPageToken(), response.getChanges()); + } catch (IOException ex) { + throw translate(ex); + } + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/spi/DnsRpc.java b/gcloud-java-dns/src/main/java/com/google/gcloud/spi/DnsRpc.java new file mode 100644 index 000000000000..addb69d24b40 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/spi/DnsRpc.java @@ -0,0 +1,174 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.spi; + +import com.google.api.services.dns.model.Change; +import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.Project; +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.common.collect.ImmutableList; +import com.google.gcloud.dns.DnsException; + +import java.util.Map; + +public interface DnsRpc { + + enum Option { + FIELDS("fields"), + PAGE_SIZE("maxResults"), + PAGE_TOKEN("pageToken"), + DNS_NAME("dnsName"), + NAME("name"), + DNS_TYPE("type"), + SORTING_ORDER("sortOrder"); + + private final String value; + + Option(String value) { + this.value = value; + } + + public String value() { + return value; + } + + @SuppressWarnings("unchecked") + T get(Map options) { + return (T) options.get(this); + } + + String getString(Map options) { + return get(options); + } + + Integer getInt(Map options) { + return get(options); + } + } + + class ListResult { + + private final Iterable results; + private final String pageToken; + + public ListResult(String pageToken, Iterable results) { + this.results = ImmutableList.copyOf(results); + this.pageToken = pageToken; + } + + public static ListResult of(String pageToken, Iterable list) { + return new ListResult<>(pageToken, list); + } + + public Iterable results() { + return results; + } + + public String pageToken() { + return pageToken; + } + } + + /** + * Creates a new zone. + * + * @param zone a zone to be created + * @param options a map of options for the service call + * @return Updated {@code ManagedZone} object + * @throws DnsException upon failure + */ + ManagedZone create(ManagedZone zone, Map options) throws DnsException; + + /** + * Retrieves and returns an existing zone. + * + * @param zoneName name of the zone to be returned + * @param options a map of options for the service call + * @return a zone or {@code null} if not found + * @throws DnsException upon failure + */ + ManagedZone getZone(String zoneName, Map options) throws DnsException; + + /** + * Lists the zones that exist within the project. + * + * @param options a map of options for the service call + * @throws DnsException upon failure + */ + ListResult listZones(Map options) throws DnsException; + + /** + * Deletes the zone identified by the name. + * + * @return {@code true} if the zone was deleted and {@code false} otherwise + * @throws DnsException upon failure + */ + boolean deleteZone(String zoneName) throws DnsException; + + /** + * Lists DNS records for a given zone. + * + * @param zoneName name of the zone to be listed + * @param options a map of options for the service call + * @throws DnsException upon failure or if zone was not found + */ + ListResult listDnsRecords(String zoneName, Map options) + throws DnsException; + + /** + * Returns information about the current project. + * + * @param options a map of options for the service call + * @return up-to-date project information + * @throws DnsException upon failure or if the project is not found + */ + Project getProject(Map options) throws DnsException; + + /** + * Applies change request to a zone. + * + * @param zoneName the name of a zone to which the {@code Change} should be applied + * @param changeRequest change to be applied + * @param options a map of options for the service call + * @return updated change object with server-assigned ID + * @throws DnsException upon failure or if zone was not found + */ + Change applyChangeRequest(String zoneName, Change changeRequest, Map options) + throws DnsException; + + /** + * Returns an existing change request. + * + * @param zoneName the name of a zone to which the {@code Change} was be applied + * @param changeRequestId the unique id assigned to the change by the server + * @param options a map of options for the service call + * @return up-to-date change object or {@code null} if change was not found + * @throws DnsException upon failure or if zone was not found + */ + Change getChangeRequest(String zoneName, String changeRequestId, Map options) + throws DnsException; + + /** + * List existing change requests for a zone. + * + * @param zoneName the name of a zone to which the {@code Change}s were be applied + * @param options a map of options for the service call + * @throws DnsException upon failure or if zone was not found + */ + ListResult listChangeRequests(String zoneName, Map options) + throws DnsException; +} diff --git a/gcloud-java-dns/src/main/java/com/google/gcloud/spi/DnsRpcFactory.java b/gcloud-java-dns/src/main/java/com/google/gcloud/spi/DnsRpcFactory.java new file mode 100644 index 000000000000..3d25f09bb1e5 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/gcloud/spi/DnsRpcFactory.java @@ -0,0 +1,26 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.spi; + +import com.google.gcloud.dns.DnsOptions; + +/** + * An interface for DnsRpc factory. Implementation will be loaded via {@link + * java.util.ServiceLoader}. + */ +public interface DnsRpcFactory extends ServiceRpcFactory { +} diff --git a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/AbstractOptionTest.java b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/AbstractOptionTest.java new file mode 100644 index 000000000000..09e35527879b --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/AbstractOptionTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.fail; + +import com.google.gcloud.spi.DnsRpc; + +import org.junit.Test; + +public class AbstractOptionTest { + + private static final DnsRpc.Option RPC_OPTION = DnsRpc.Option.DNS_TYPE; + private static final DnsRpc.Option ANOTHER_RPC_OPTION = DnsRpc.Option.DNS_NAME; + private static final String VALUE = "some value"; + private static final String OTHER_VALUE = "another value"; + private static final AbstractOption OPTION = new AbstractOption(RPC_OPTION, VALUE) {}; + private static final AbstractOption OPTION_EQUALS = new AbstractOption(RPC_OPTION, VALUE) {}; + private static final AbstractOption OPTION_NOT_EQUALS1 = + new AbstractOption(RPC_OPTION, OTHER_VALUE) {}; + private static final AbstractOption OPTION_NOT_EQUALS2 = + new AbstractOption(ANOTHER_RPC_OPTION, VALUE) {}; + + @Test + public void testEquals() { + assertEquals(OPTION, OPTION_EQUALS); + assertNotEquals(OPTION, OPTION_NOT_EQUALS1); + assertNotEquals(OPTION, OPTION_NOT_EQUALS2); + } + + @Test + public void testHashCode() { + assertEquals(OPTION.hashCode(), OPTION_EQUALS.hashCode()); + } + + @Test + public void testConstructor() { + assertEquals(RPC_OPTION, OPTION.rpcOption()); + assertEquals(VALUE, OPTION.value()); + try { + new AbstractOption(null, VALUE) {}; + fail("Cannot build with empty option."); + } catch (NullPointerException e) { + // expected + } + new AbstractOption(RPC_OPTION, null) {}; // null value is ok + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ChangeRequestTest.java b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ChangeRequestTest.java new file mode 100644 index 000000000000..8b40a4dcff34 --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ChangeRequestTest.java @@ -0,0 +1,218 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableList; + +import org.junit.Test; + +import java.util.List; + +public class ChangeRequestTest { + + private static final String ID = "cr-id-1"; + private static final Long START_TIME_MILLIS = 12334567890L; + private static final ChangeRequest.Status STATUS = ChangeRequest.Status.PENDING; + private static final String NAME1 = "dns1"; + private static final DnsRecord.Type TYPE1 = DnsRecord.Type.A; + private static final String NAME2 = "dns2"; + private static final DnsRecord.Type TYPE2 = DnsRecord.Type.AAAA; + private static final String NAME3 = "dns3"; + private static final DnsRecord.Type TYPE3 = DnsRecord.Type.MX; + private static final DnsRecord RECORD1 = DnsRecord.builder(NAME1, TYPE1).build(); + private static final DnsRecord RECORD2 = DnsRecord.builder(NAME2, TYPE2).build(); + private static final DnsRecord RECORD3 = DnsRecord.builder(NAME3, TYPE3).build(); + private static final List ADDITIONS = ImmutableList.of(RECORD1, RECORD2); + private static final List DELETIONS = ImmutableList.of(RECORD3); + private static final ChangeRequest CHANGE = ChangeRequest.builder() + .add(RECORD1) + .add(RECORD2) + .delete(RECORD3) + .startTimeMillis(START_TIME_MILLIS) + .status(STATUS) + .id(ID) + .build(); + + @Test + public void testEmptyBuilder() { + ChangeRequest cr = ChangeRequest.builder().build(); + assertNotNull(cr.deletions()); + assertTrue(cr.deletions().isEmpty()); + assertNotNull(cr.additions()); + assertTrue(cr.additions().isEmpty()); + } + + @Test + public void testBuilder() { + assertEquals(ID, CHANGE.id()); + assertEquals(STATUS, CHANGE.status()); + assertEquals(START_TIME_MILLIS, CHANGE.startTimeMillis()); + assertEquals(ADDITIONS, CHANGE.additions()); + assertEquals(DELETIONS, CHANGE.deletions()); + List recordList = ImmutableList.of(RECORD1); + ChangeRequest another = CHANGE.toBuilder().additions(recordList).build(); + assertEquals(recordList, another.additions()); + assertEquals(CHANGE.deletions(), another.deletions()); + another = CHANGE.toBuilder().deletions(recordList).build(); + assertEquals(recordList, another.deletions()); + assertEquals(CHANGE.additions(), another.additions()); + } + + @Test + public void testEqualsAndNotEquals() { + ChangeRequest clone = CHANGE.toBuilder().build(); + assertEquals(CHANGE, clone); + clone = ChangeRequest.fromPb(CHANGE.toPb()); + assertEquals(CHANGE, clone); + clone = CHANGE.toBuilder().id("some-other-id").build(); + assertNotEquals(CHANGE, clone); + clone = CHANGE.toBuilder().startTimeMillis(CHANGE.startTimeMillis() + 1).build(); + assertNotEquals(CHANGE, clone); + clone = CHANGE.toBuilder().add(RECORD3).build(); + assertNotEquals(CHANGE, clone); + clone = CHANGE.toBuilder().delete(RECORD1).build(); + assertNotEquals(CHANGE, clone); + ChangeRequest empty = ChangeRequest.builder().build(); + assertNotEquals(CHANGE, empty); + assertEquals(empty, ChangeRequest.builder().build()); + } + + @Test + public void testSameHashCodeOnEquals() { + ChangeRequest clone = CHANGE.toBuilder().build(); + assertEquals(CHANGE, clone); + assertEquals(CHANGE.hashCode(), clone.hashCode()); + ChangeRequest empty = ChangeRequest.builder().build(); + assertEquals(empty.hashCode(), ChangeRequest.builder().build().hashCode()); + } + + @Test + public void testToAndFromPb() { + assertEquals(CHANGE, ChangeRequest.fromPb(CHANGE.toPb())); + ChangeRequest partial = ChangeRequest.builder().build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().id(ID).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().add(RECORD1).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().delete(RECORD1).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().additions(ADDITIONS).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().deletions(DELETIONS).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().startTimeMillis(START_TIME_MILLIS).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().status(STATUS).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + } + + @Test + public void testToBuilder() { + assertEquals(CHANGE, CHANGE.toBuilder().build()); + ChangeRequest partial = ChangeRequest.builder().build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().id(ID).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().add(RECORD1).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().delete(RECORD1).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().additions(ADDITIONS).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().deletions(DELETIONS).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().startTimeMillis(START_TIME_MILLIS).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().status(STATUS).build(); + assertEquals(partial, partial.toBuilder().build()); + } + + @Test + public void testClearAdditions() { + ChangeRequest clone = CHANGE.toBuilder().clearAdditions().build(); + assertTrue(clone.additions().isEmpty()); + assertFalse(clone.deletions().isEmpty()); + } + + @Test + public void testAddAddition() { + try { + CHANGE.toBuilder().add(null); + fail("Should not be able to add null DnsRecord."); + } catch (NullPointerException e) { + // expected + } + ChangeRequest clone = CHANGE.toBuilder().add(RECORD1).build(); + assertEquals(CHANGE.additions().size() + 1, clone.additions().size()); + } + + @Test + public void testAddDeletion() { + try { + CHANGE.toBuilder().delete(null); + fail("Should not be able to delete null DnsRecord."); + } catch (NullPointerException e) { + // expected + } + ChangeRequest clone = CHANGE.toBuilder().delete(RECORD1).build(); + assertEquals(CHANGE.deletions().size() + 1, clone.deletions().size()); + } + + @Test + public void testClearDeletions() { + ChangeRequest clone = CHANGE.toBuilder().clearDeletions().build(); + assertTrue(clone.deletions().isEmpty()); + assertFalse(clone.additions().isEmpty()); + } + + @Test + public void testRemoveAddition() { + ChangeRequest clone = CHANGE.toBuilder().removeAddition(RECORD1).build(); + assertTrue(clone.additions().contains(RECORD2)); + assertFalse(clone.additions().contains(RECORD1)); + assertTrue(clone.deletions().contains(RECORD3)); + clone = CHANGE.toBuilder().removeAddition(RECORD2).removeAddition(RECORD1).build(); + assertFalse(clone.additions().contains(RECORD2)); + assertFalse(clone.additions().contains(RECORD1)); + assertTrue(clone.additions().isEmpty()); + assertTrue(clone.deletions().contains(RECORD3)); + } + + @Test + public void testRemoveDeletion() { + ChangeRequest clone = CHANGE.toBuilder().removeDeletion(RECORD3).build(); + assertTrue(clone.deletions().isEmpty()); + } + + @Test + public void testDateParsing() { + String startTime = "2016-01-26T18:33:43.512Z"; // obtained from service + com.google.api.services.dns.model.Change change = CHANGE.toPb().setStartTime(startTime); + ChangeRequest converted = ChangeRequest.fromPb(change); + assertNotNull(converted.startTimeMillis()); + assertEquals(change, converted.toPb()); + assertEquals(change.getStartTime(), converted.toPb().getStartTime()); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsImplTest.java b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsImplTest.java new file mode 100644 index 000000000000..89ad90f27654 --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsImplTest.java @@ -0,0 +1,375 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.api.services.dns.model.Change; +import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.gcloud.Page; +import com.google.gcloud.RetryParams; +import com.google.gcloud.ServiceOptions; +import com.google.gcloud.spi.DnsRpc; +import com.google.gcloud.spi.DnsRpcFactory; + +import org.easymock.Capture; +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +public class DnsImplTest { + + // Dns entities + private static final String ZONE_NAME = "some zone name"; + private static final String DNS_NAME = "example.com."; + private static final String CHANGE_ID = "some change id"; + private static final DnsRecord DNS_RECORD1 = DnsRecord.builder("Something", DnsRecord.Type.AAAA) + .build(); + private static final DnsRecord DNS_RECORD2 = DnsRecord.builder("Different", DnsRecord.Type.AAAA) + .build(); + private static final Integer MAX_SIZE = 20; + private static final String PAGE_TOKEN = "some token"; + private static final ZoneInfo ZONE_INFO = ZoneInfo.builder(ZONE_NAME).build(); + private static final ProjectInfo PROJECT_INFO = ProjectInfo.builder().build(); + private static final ChangeRequest CHANGE_REQUEST_PARTIAL = ChangeRequest.builder() + .add(DNS_RECORD1) + .build(); + private static final ChangeRequest CHANGE_REQUEST_COMPLETE = ChangeRequest.builder() + .add(DNS_RECORD1) + .startTimeMillis(123L) + .status(ChangeRequest.Status.PENDING) + .id(CHANGE_ID) + .build(); + + // Result lists + private static final DnsRpc.ListResult LIST_RESULT_OF_PB_CHANGES = + DnsRpc.ListResult.of("cursor", ImmutableList.of(CHANGE_REQUEST_COMPLETE.toPb(), + CHANGE_REQUEST_PARTIAL.toPb())); + private static final DnsRpc.ListResult LIST_RESULT_OF_PB_ZONES = + DnsRpc.ListResult.of("cursor", ImmutableList.of(ZONE_INFO.toPb())); + private static final DnsRpc.ListResult LIST_OF_PB_DNS_RECORDS = + DnsRpc.ListResult.of("cursor", ImmutableList.of(DNS_RECORD1.toPb(), DNS_RECORD2.toPb())); + + // Field options + private static final Dns.ZoneOption ZONE_FIELDS = + Dns.ZoneOption.fields(Dns.ZoneField.CREATION_TIME); + private static final Dns.ProjectOption PROJECT_FIELDS = + Dns.ProjectOption.fields(Dns.ProjectField.QUOTA); + private static final Dns.ChangeRequestOption CHANGE_GET_FIELDS = + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.STATUS); + + // Listing options + private static final Dns.ZoneListOption[] ZONE_LIST_OPTIONS = { + Dns.ZoneListOption.pageSize(MAX_SIZE), Dns.ZoneListOption.pageToken(PAGE_TOKEN), + Dns.ZoneListOption.fields(Dns.ZoneField.DESCRIPTION), + Dns.ZoneListOption.dnsName(DNS_NAME)}; + private static final Dns.ChangeRequestListOption[] CHANGE_LIST_OPTIONS = { + Dns.ChangeRequestListOption.pageSize(MAX_SIZE), + Dns.ChangeRequestListOption.pageToken(PAGE_TOKEN), + Dns.ChangeRequestListOption.fields(Dns.ChangeRequestField.STATUS), + Dns.ChangeRequestListOption.sortOrder(Dns.SortingOrder.ASCENDING)}; + private static final Dns.DnsRecordListOption[] DNS_RECORD_LIST_OPTIONS = { + Dns.DnsRecordListOption.pageSize(MAX_SIZE), + Dns.DnsRecordListOption.pageToken(PAGE_TOKEN), + Dns.DnsRecordListOption.fields(Dns.DnsRecordField.TTL), + Dns.DnsRecordListOption.dnsName(DNS_NAME), + Dns.DnsRecordListOption.type(DnsRecord.Type.AAAA)}; + + // Other + private static final Map EMPTY_RPC_OPTIONS = ImmutableMap.of(); + private static final ServiceOptions.Clock TIME_SOURCE = new ServiceOptions.Clock() { + @Override + public long millis() { + return 42000L; + } + }; + + private DnsOptions options; + private DnsRpcFactory rpcFactoryMock; + private DnsRpc dnsRpcMock; + private Dns dns; + + @Before + public void setUp() { + rpcFactoryMock = EasyMock.createMock(DnsRpcFactory.class); + dnsRpcMock = EasyMock.createMock(DnsRpc.class); + EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(DnsOptions.class))) + .andReturn(dnsRpcMock); + EasyMock.replay(rpcFactoryMock); + options = DnsOptions.builder() + .projectId("projectId") + .clock(TIME_SOURCE) + .serviceRpcFactory(rpcFactoryMock) + .retryParams(RetryParams.noRetries()) + .build(); + } + + @After + public void tearDown() throws Exception { + EasyMock.verify(rpcFactoryMock); + } + + @Test + public void testCreateZone() { + EasyMock.expect(dnsRpcMock.create(ZONE_INFO.toPb(), EMPTY_RPC_OPTIONS)) + .andReturn(ZONE_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Zone zone = dns.create(ZONE_INFO); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), zone); + } + + @Test + public void testCreateZoneWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.create(EasyMock.eq(ZONE_INFO.toPb()), + EasyMock.capture(capturedOptions))).andReturn(ZONE_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Zone zone = dns.create(ZONE_INFO, ZONE_FIELDS); + String selector = (String) capturedOptions.getValue().get(ZONE_FIELDS.rpcOption()); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), zone); + assertTrue(selector.contains(Dns.ZoneField.CREATION_TIME.selector())); + assertTrue(selector.contains(Dns.ZoneField.NAME.selector())); + } + + @Test + public void testGetZone() { + EasyMock.expect(dnsRpcMock.getZone(ZONE_INFO.name(), EMPTY_RPC_OPTIONS)) + .andReturn(ZONE_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Zone zone = dns.getZone(ZONE_INFO.name()); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), zone); + } + + @Test + public void testGetZoneWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.getZone(EasyMock.eq(ZONE_INFO.name()), + EasyMock.capture(capturedOptions))).andReturn(ZONE_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Zone zone = dns.getZone(ZONE_INFO.name(), ZONE_FIELDS); + String selector = (String) capturedOptions.getValue().get(ZONE_FIELDS.rpcOption()); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), zone); + assertTrue(selector.contains(Dns.ZoneField.CREATION_TIME.selector())); + assertTrue(selector.contains(Dns.ZoneField.NAME.selector())); + } + + @Test + public void testDeleteZone() { + EasyMock.expect(dnsRpcMock.deleteZone(ZONE_INFO.name())) + .andReturn(true); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + assertTrue(dns.delete(ZONE_INFO.name())); + } + + @Test + public void testGetProject() { + EasyMock.expect(dnsRpcMock.getProject(EMPTY_RPC_OPTIONS)) + .andReturn(PROJECT_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ProjectInfo projectInfo = dns.getProject(); + assertEquals(PROJECT_INFO, projectInfo); + } + + @Test + public void testProjectGetWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.getProject(EasyMock.capture(capturedOptions))) + .andReturn(PROJECT_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ProjectInfo projectInfo = dns.getProject(PROJECT_FIELDS); + String selector = (String) capturedOptions.getValue().get(PROJECT_FIELDS.rpcOption()); + assertEquals(PROJECT_INFO, projectInfo); + assertTrue(selector.contains(Dns.ProjectField.QUOTA.selector())); + assertTrue(selector.contains(Dns.ProjectField.PROJECT_ID.selector())); + } + + @Test + public void testGetChangeRequest() { + EasyMock.expect(dnsRpcMock.getChangeRequest(ZONE_INFO.name(), CHANGE_REQUEST_COMPLETE.id(), + EMPTY_RPC_OPTIONS)).andReturn(CHANGE_REQUEST_COMPLETE.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ChangeRequest changeRequest = dns.getChangeRequest(ZONE_INFO.name(), + CHANGE_REQUEST_COMPLETE.id()); + assertEquals(CHANGE_REQUEST_COMPLETE, changeRequest); + } + + @Test + public void testGetChangeRequestWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.getChangeRequest(EasyMock.eq(ZONE_INFO.name()), + EasyMock.eq(CHANGE_REQUEST_COMPLETE.id()), EasyMock.capture(capturedOptions))) + .andReturn(CHANGE_REQUEST_COMPLETE.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ChangeRequest changeRequest = dns.getChangeRequest(ZONE_INFO.name(), + CHANGE_REQUEST_COMPLETE.id(), CHANGE_GET_FIELDS); + String selector = (String) capturedOptions.getValue().get(CHANGE_GET_FIELDS.rpcOption()); + assertEquals(CHANGE_REQUEST_COMPLETE, changeRequest); + assertTrue(selector.contains(Dns.ChangeRequestField.STATUS.selector())); + assertTrue(selector.contains(Dns.ChangeRequestField.ID.selector())); + } + + @Test + public void testApplyChangeRequest() { + EasyMock.expect(dnsRpcMock.applyChangeRequest(ZONE_INFO.name(), CHANGE_REQUEST_PARTIAL.toPb(), + EMPTY_RPC_OPTIONS)).andReturn(CHANGE_REQUEST_COMPLETE.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ChangeRequest changeRequest = dns.applyChangeRequest(ZONE_INFO.name(), + CHANGE_REQUEST_PARTIAL); + assertEquals(CHANGE_REQUEST_COMPLETE, changeRequest); + } + + @Test + public void testApplyChangeRequestWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.applyChangeRequest(EasyMock.eq(ZONE_INFO.name()), + EasyMock.eq(CHANGE_REQUEST_PARTIAL.toPb()), EasyMock.capture(capturedOptions))) + .andReturn(CHANGE_REQUEST_COMPLETE.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ChangeRequest changeRequest = dns.applyChangeRequest(ZONE_INFO.name(), + CHANGE_REQUEST_PARTIAL, CHANGE_GET_FIELDS); + String selector = (String) capturedOptions.getValue().get(CHANGE_GET_FIELDS.rpcOption()); + assertEquals(CHANGE_REQUEST_COMPLETE, changeRequest); + assertTrue(selector.contains(Dns.ChangeRequestField.STATUS.selector())); + assertTrue(selector.contains(Dns.ChangeRequestField.ID.selector())); + } + + // lists + @Test + public void testListChangeRequests() { + EasyMock.expect(dnsRpcMock.listChangeRequests(ZONE_INFO.name(), EMPTY_RPC_OPTIONS)) + .andReturn(LIST_RESULT_OF_PB_CHANGES); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page changeRequestPage = dns.listChangeRequests(ZONE_INFO.name()); + assertTrue(Lists.newArrayList(changeRequestPage.values()).contains(CHANGE_REQUEST_COMPLETE)); + assertTrue(Lists.newArrayList(changeRequestPage.values()).contains(CHANGE_REQUEST_PARTIAL)); + assertEquals(2, Lists.newArrayList(changeRequestPage.values()).size()); + } + + @Test + public void testListChangeRequestsWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.listChangeRequests(EasyMock.eq(ZONE_NAME), + EasyMock.capture(capturedOptions))).andReturn(LIST_RESULT_OF_PB_CHANGES); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page changeRequestPage = dns.listChangeRequests(ZONE_NAME, CHANGE_LIST_OPTIONS); + assertTrue(Lists.newArrayList(changeRequestPage.values()).contains(CHANGE_REQUEST_COMPLETE)); + assertTrue(Lists.newArrayList(changeRequestPage.values()).contains(CHANGE_REQUEST_PARTIAL)); + assertEquals(2, Lists.newArrayList(changeRequestPage.values()).size()); + Integer size = (Integer) capturedOptions.getValue().get(CHANGE_LIST_OPTIONS[0].rpcOption()); + assertEquals(MAX_SIZE, size); + String selector = (String) capturedOptions.getValue().get(CHANGE_LIST_OPTIONS[1].rpcOption()); + assertEquals(PAGE_TOKEN, selector); + selector = (String) capturedOptions.getValue().get(CHANGE_LIST_OPTIONS[2].rpcOption()); + assertTrue(selector.contains(Dns.ChangeRequestField.STATUS.selector())); + assertTrue(selector.contains(Dns.ChangeRequestField.ID.selector())); + selector = (String) capturedOptions.getValue().get(CHANGE_LIST_OPTIONS[3].rpcOption()); + assertTrue(selector.contains(Dns.SortingOrder.ASCENDING.selector())); + } + + @Test + public void testListZones() { + EasyMock.expect(dnsRpcMock.listZones(EMPTY_RPC_OPTIONS)) + .andReturn(LIST_RESULT_OF_PB_ZONES); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page zonePage = dns.listZones(); + assertEquals(1, Lists.newArrayList(zonePage.values()).size()); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), + Lists.newArrayList(zonePage.values()).get(0)); + } + + @Test + public void testListZonesWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.listZones(EasyMock.capture(capturedOptions))) + .andReturn(LIST_RESULT_OF_PB_ZONES); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page zonePage = dns.listZones(ZONE_LIST_OPTIONS); + assertEquals(1, Lists.newArrayList(zonePage.values()).size()); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), + Lists.newArrayList(zonePage.values()).get(0)); + Integer size = (Integer) capturedOptions.getValue().get(ZONE_LIST_OPTIONS[0].rpcOption()); + assertEquals(MAX_SIZE, size); + String selector = (String) capturedOptions.getValue().get(ZONE_LIST_OPTIONS[1].rpcOption()); + assertEquals(PAGE_TOKEN, selector); + selector = (String) capturedOptions.getValue().get(ZONE_LIST_OPTIONS[2].rpcOption()); + assertTrue(selector.contains(Dns.ZoneField.DESCRIPTION.selector())); + assertTrue(selector.contains(Dns.ZoneField.NAME.selector())); + selector = (String) capturedOptions.getValue().get(ZONE_LIST_OPTIONS[3].rpcOption()); + assertEquals(DNS_NAME, selector); + } + + @Test + public void testListDnsRecords() { + EasyMock.expect(dnsRpcMock.listDnsRecords(ZONE_INFO.name(), EMPTY_RPC_OPTIONS)) + .andReturn(LIST_OF_PB_DNS_RECORDS); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page dnsPage = dns.listDnsRecords(ZONE_INFO.name()); + assertEquals(2, Lists.newArrayList(dnsPage.values()).size()); + assertTrue(Lists.newArrayList(dnsPage.values()).contains(DNS_RECORD1)); + assertTrue(Lists.newArrayList(dnsPage.values()).contains(DNS_RECORD2)); + } + + @Test + public void testListDnsRecordsWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.listDnsRecords(EasyMock.eq(ZONE_NAME), + EasyMock.capture(capturedOptions))).andReturn(LIST_OF_PB_DNS_RECORDS); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page dnsPage = dns.listDnsRecords(ZONE_NAME, DNS_RECORD_LIST_OPTIONS); + assertEquals(2, Lists.newArrayList(dnsPage.values()).size()); + assertTrue(Lists.newArrayList(dnsPage.values()).contains(DNS_RECORD1)); + assertTrue(Lists.newArrayList(dnsPage.values()).contains(DNS_RECORD2)); + Integer size = (Integer) capturedOptions.getValue().get(DNS_RECORD_LIST_OPTIONS[0].rpcOption()); + assertEquals(MAX_SIZE, size); + String selector = (String) capturedOptions.getValue() + .get(DNS_RECORD_LIST_OPTIONS[1].rpcOption()); + assertEquals(PAGE_TOKEN, selector); + selector = (String) capturedOptions.getValue().get(DNS_RECORD_LIST_OPTIONS[2].rpcOption()); + assertTrue(selector.contains(Dns.DnsRecordField.NAME.selector())); + assertTrue(selector.contains(Dns.DnsRecordField.TTL.selector())); + selector = (String) capturedOptions.getValue().get(DNS_RECORD_LIST_OPTIONS[3].rpcOption()); + assertEquals(DNS_RECORD_LIST_OPTIONS[3].value(), selector); + DnsRecord.Type type = (DnsRecord.Type) capturedOptions.getValue().get(DNS_RECORD_LIST_OPTIONS[4] + .rpcOption()); + assertEquals(DNS_RECORD_LIST_OPTIONS[4].value(), type); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsRecordTest.java b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsRecordTest.java new file mode 100644 index 000000000000..5fc972cede78 --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsRecordTest.java @@ -0,0 +1,149 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static com.google.gcloud.dns.DnsRecord.builder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +public class DnsRecordTest { + + private static final String NAME = "example.com."; + private static final Integer TTL = 3600; + private static final TimeUnit UNIT = TimeUnit.HOURS; + private static final Integer UNIT_TTL = 1; + private static final DnsRecord.Type TYPE = DnsRecord.Type.AAAA; + private static final DnsRecord record = builder(NAME, TYPE) + .ttl(UNIT_TTL, UNIT) + .build(); + + @Test + public void testDefaultDnsRecord() { + DnsRecord record = builder(NAME, TYPE).build(); + assertEquals(0, record.records().size()); + assertEquals(TYPE, record.type()); + assertEquals(NAME, record.name()); + } + + @Test + public void testBuilder() { + assertEquals(NAME, record.name()); + assertEquals(TTL, record.ttl()); + assertEquals(TYPE, record.type()); + assertEquals(0, record.records().size()); + // verify that one can add records to the record set + String testingRecord = "Testing record"; + String anotherTestingRecord = "Another record 123"; + DnsRecord anotherRecord = record.toBuilder() + .addRecord(testingRecord) + .addRecord(anotherTestingRecord) + .build(); + assertEquals(2, anotherRecord.records().size()); + assertTrue(anotherRecord.records().contains(testingRecord)); + assertTrue(anotherRecord.records().contains(anotherTestingRecord)); + } + + @Test + public void testValidTtl() { + try { + builder(NAME, TYPE).ttl(-1, TimeUnit.SECONDS); + fail("A negative value is not acceptable for ttl."); + } catch (IllegalArgumentException e) { + // expected + } + builder(NAME, TYPE).ttl(0, TimeUnit.SECONDS); + builder(NAME, TYPE).ttl(Integer.MAX_VALUE, TimeUnit.SECONDS); + try { + builder(NAME, TYPE).ttl(Integer.MAX_VALUE, TimeUnit.HOURS); + fail("This value is too large for int."); + } catch (IllegalArgumentException e) { + // expected + } + DnsRecord record = DnsRecord.builder(NAME, TYPE).ttl(UNIT_TTL, UNIT).build(); + assertEquals(TTL, record.ttl()); + } + + @Test + public void testEqualsAndNotEquals() { + DnsRecord clone = record.toBuilder().build(); + assertEquals(record, clone); + clone = record.toBuilder().addRecord("another record").build(); + assertNotEquals(record, clone); + String differentName = "totally different name"; + clone = record.toBuilder().name(differentName).build(); + assertNotEquals(record, clone); + clone = record.toBuilder().ttl(record.ttl() + 1, TimeUnit.SECONDS).build(); + assertNotEquals(record, clone); + clone = record.toBuilder().type(DnsRecord.Type.TXT).build(); + assertNotEquals(record, clone); + } + + @Test + public void testSameHashCodeOnEquals() { + int hash = record.hashCode(); + DnsRecord clone = record.toBuilder().build(); + assertEquals(clone.hashCode(), hash); + } + + @Test + public void testToAndFromPb() { + assertEquals(record, DnsRecord.fromPb(record.toPb())); + DnsRecord partial = builder(NAME, TYPE).build(); + assertEquals(partial, DnsRecord.fromPb(partial.toPb())); + partial = builder(NAME, TYPE).addRecord("test").build(); + assertEquals(partial, DnsRecord.fromPb(partial.toPb())); + partial = builder(NAME, TYPE).ttl(15, TimeUnit.SECONDS).build(); + assertEquals(partial, DnsRecord.fromPb(partial.toPb())); + } + + @Test + public void testToBuilder() { + assertEquals(record, record.toBuilder().build()); + DnsRecord partial = builder(NAME, TYPE).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = builder(NAME, TYPE).addRecord("test").build(); + assertEquals(partial, partial.toBuilder().build()); + partial = builder(NAME, TYPE).ttl(15, TimeUnit.SECONDS).build(); + assertEquals(partial, partial.toBuilder().build()); + } + + @Test + public void clearRecordSet() { + // make sure that we are starting not empty + DnsRecord clone = record.toBuilder().addRecord("record").addRecord("another").build(); + assertNotEquals(0, clone.records().size()); + clone = clone.toBuilder().clearRecords().build(); + assertEquals(0, clone.records().size()); + clone.toPb(); // verify that pb allows it + } + + @Test + public void removeFromRecordSet() { + String recordString = "record"; + // make sure that we are starting not empty + DnsRecord clone = record.toBuilder().addRecord(recordString).build(); + assertNotEquals(0, clone.records().size()); + clone = clone.toBuilder().removeRecord(recordString).build(); + assertEquals(0, clone.records().size()); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsTest.java b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsTest.java new file mode 100644 index 000000000000..92a18ad9c1e7 --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/DnsTest.java @@ -0,0 +1,146 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.gcloud.spi.DnsRpc; + +import org.junit.Test; + +public class DnsTest { + + private static final Integer PAGE_SIZE = 20; + private static final String PAGE_TOKEN = "page token"; + private static final String DNS_NAME = "www.example.com."; + + @Test + public void testDnsRecordListOption() { + // dns name + String dnsName = "some name"; + Dns.DnsRecordListOption dnsRecordListOption = Dns.DnsRecordListOption.dnsName(dnsName); + assertEquals(dnsName, dnsRecordListOption.value()); + assertEquals(DnsRpc.Option.NAME, dnsRecordListOption.rpcOption()); + // page token + dnsRecordListOption = Dns.DnsRecordListOption.pageToken(PAGE_TOKEN); + assertEquals(PAGE_TOKEN, dnsRecordListOption.value()); + assertEquals(DnsRpc.Option.PAGE_TOKEN, dnsRecordListOption.rpcOption()); + // page size + dnsRecordListOption = Dns.DnsRecordListOption.pageSize(PAGE_SIZE); + assertEquals(PAGE_SIZE, dnsRecordListOption.value()); + assertEquals(DnsRpc.Option.PAGE_SIZE, dnsRecordListOption.rpcOption()); + // record type + DnsRecord.Type recordType = DnsRecord.Type.AAAA; + dnsRecordListOption = Dns.DnsRecordListOption.type(recordType); + assertEquals(recordType, dnsRecordListOption.value()); + assertEquals(DnsRpc.Option.DNS_TYPE, dnsRecordListOption.rpcOption()); + // fields + dnsRecordListOption = Dns.DnsRecordListOption.fields(Dns.DnsRecordField.NAME, + Dns.DnsRecordField.TTL); + assertEquals(DnsRpc.Option.FIELDS, dnsRecordListOption.rpcOption()); + assertTrue(dnsRecordListOption.value() instanceof String); + assertTrue(((String) dnsRecordListOption.value()).contains( + Dns.DnsRecordField.NAME.selector())); + assertTrue(((String) dnsRecordListOption.value()).contains( + Dns.DnsRecordField.TTL.selector())); + assertTrue(((String) dnsRecordListOption.value()).contains( + Dns.DnsRecordField.NAME.selector())); + } + + @Test + public void testZoneOption() { + Dns.ZoneOption fields = Dns.ZoneOption.fields(Dns.ZoneField.CREATION_TIME, + Dns.ZoneField.DESCRIPTION); + assertEquals(DnsRpc.Option.FIELDS, fields.rpcOption()); + assertTrue(fields.value() instanceof String); + assertTrue(((String) fields.value()).contains(Dns.ZoneField.CREATION_TIME.selector())); + assertTrue(((String) fields.value()).contains(Dns.ZoneField.DESCRIPTION.selector())); + } + + @Test + public void testZoneList() { + // fields + Dns.ZoneListOption fields = Dns.ZoneListOption.fields(Dns.ZoneField.CREATION_TIME, + Dns.ZoneField.DESCRIPTION); + assertEquals(DnsRpc.Option.FIELDS, fields.rpcOption()); + assertTrue(fields.value() instanceof String); + assertTrue(((String) fields.value()).contains(Dns.ZoneField.CREATION_TIME.selector())); + assertTrue(((String) fields.value()).contains(Dns.ZoneField.DESCRIPTION.selector())); + assertTrue(((String) fields.value()).contains(Dns.ZoneField.NAME.selector())); + // page token + Dns.ZoneListOption option = Dns.ZoneListOption.pageToken(PAGE_TOKEN); + assertEquals(PAGE_TOKEN, option.value()); + assertEquals(DnsRpc.Option.PAGE_TOKEN, option.rpcOption()); + // page size + option = Dns.ZoneListOption.pageSize(PAGE_SIZE); + assertEquals(PAGE_SIZE, option.value()); + assertEquals(DnsRpc.Option.PAGE_SIZE, option.rpcOption()); + // dnsName filter + option = Dns.ZoneListOption.dnsName(DNS_NAME); + assertEquals(DNS_NAME, option.value()); + assertEquals(DnsRpc.Option.DNS_NAME, option.rpcOption()); + } + + @Test + public void testProjectGetOption() { + // fields + Dns.ProjectOption fields = Dns.ProjectOption.fields(Dns.ProjectField.QUOTA); + assertEquals(DnsRpc.Option.FIELDS, fields.rpcOption()); + assertTrue(fields.value() instanceof String); + assertTrue(((String) fields.value()).contains(Dns.ProjectField.QUOTA.selector())); + assertTrue(((String) fields.value()).contains(Dns.ProjectField.PROJECT_ID.selector())); + } + + @Test + public void testChangeRequestOption() { + // fields + Dns.ChangeRequestOption fields = Dns.ChangeRequestOption.fields( + Dns.ChangeRequestField.START_TIME, Dns.ChangeRequestField.STATUS); + assertEquals(DnsRpc.Option.FIELDS, fields.rpcOption()); + assertTrue(fields.value() instanceof String); + assertTrue(((String) fields.value()).contains( + Dns.ChangeRequestField.START_TIME.selector())); + assertTrue(((String) fields.value()).contains(Dns.ChangeRequestField.STATUS.selector())); + assertTrue(((String) fields.value()).contains(Dns.ChangeRequestField.ID.selector())); + } + + @Test + public void testChangeRequestListOption() { + // fields + Dns.ChangeRequestListOption fields = Dns.ChangeRequestListOption.fields( + Dns.ChangeRequestField.START_TIME, Dns.ChangeRequestField.STATUS); + assertEquals(DnsRpc.Option.FIELDS, fields.rpcOption()); + assertTrue(fields.value() instanceof String); + assertTrue(((String) fields.value()).contains( + Dns.ChangeRequestField.START_TIME.selector())); + assertTrue(((String) fields.value()).contains(Dns.ChangeRequestField.STATUS.selector())); + assertTrue(((String) fields.value()).contains(Dns.ChangeRequestField.ID.selector())); + // page token + Dns.ChangeRequestListOption option = Dns.ChangeRequestListOption.pageToken(PAGE_TOKEN); + assertEquals(PAGE_TOKEN, option.value()); + assertEquals(DnsRpc.Option.PAGE_TOKEN, option.rpcOption()); + // page size + option = Dns.ChangeRequestListOption.pageSize(PAGE_SIZE); + assertEquals(PAGE_SIZE, option.value()); + assertEquals(DnsRpc.Option.PAGE_SIZE, option.rpcOption()); + // sort order + option = Dns.ChangeRequestListOption.sortOrder(Dns.SortingOrder.ASCENDING); + assertEquals(DnsRpc.Option.SORTING_ORDER, option.rpcOption()); + assertEquals(Dns.SortingOrder.ASCENDING.selector(), option.value()); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ProjectInfoTest.java b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ProjectInfoTest.java new file mode 100644 index 000000000000..d959d44d4351 --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ProjectInfoTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +import java.math.BigInteger; + +public class ProjectInfoTest { + + private static final String ID = "project-id-123"; + private static final BigInteger NUMBER = new BigInteger("123"); + private static final ProjectInfo.Quota QUOTA = new ProjectInfo.Quota(1, 2, 3, 4, 5, 6); + private static final ProjectInfo PROJECT_INFO = ProjectInfo.builder() + .id(ID).number(NUMBER).quota(QUOTA).build(); + + @Test + public void testBuilder() { + ProjectInfo withId = ProjectInfo.builder().id(ID).build(); + assertEquals(ID, withId.id()); + assertNull(withId.number()); + assertNull(withId.quota()); + ProjectInfo withNumber = ProjectInfo.builder().number(NUMBER).build(); + assertEquals(NUMBER, withNumber.number()); + assertNull(withNumber.quota()); + assertNull(withNumber.id()); + ProjectInfo withQuota = ProjectInfo.builder().quota(QUOTA).build(); + assertEquals(QUOTA, withQuota.quota()); + assertNull(withQuota.id()); + assertNull(withQuota.number()); + assertEquals(QUOTA, PROJECT_INFO.quota()); + assertEquals(NUMBER, PROJECT_INFO.number()); + assertEquals(ID, PROJECT_INFO.id()); + } + + @Test + public void testQuotaConstructor() { + assertEquals(1, QUOTA.zones()); + assertEquals(2, QUOTA.resourceRecordsPerRrset()); + assertEquals(3, QUOTA.rrsetAdditionsPerChange()); + assertEquals(4, QUOTA.rrsetDeletionsPerChange()); + assertEquals(5, QUOTA.rrsetsPerZone()); + assertEquals(6, QUOTA.totalRrdataSizePerChange()); + } + + @Test + public void testEqualsAndNotEqualsQuota() { + ProjectInfo.Quota clone = new ProjectInfo.Quota(6, 5, 4, 3, 2, 1); + assertNotEquals(QUOTA, clone); + clone = ProjectInfo.Quota.fromPb(QUOTA.toPb()); + assertEquals(QUOTA, clone); + } + + @Test + public void testSameHashCodeOnEqualsQuota() { + ProjectInfo.Quota clone = ProjectInfo.Quota.fromPb(QUOTA.toPb()); + assertEquals(QUOTA, clone); + assertEquals(QUOTA.hashCode(), clone.hashCode()); + } + + @Test + public void testEqualsAndNotEquals() { + ProjectInfo clone = ProjectInfo.builder().build(); + assertNotEquals(PROJECT_INFO, clone); + clone = ProjectInfo.builder().id(PROJECT_INFO.id()).number(PROJECT_INFO.number()).build(); + assertNotEquals(PROJECT_INFO, clone); + clone = ProjectInfo.builder().id(PROJECT_INFO.id()).quota(PROJECT_INFO.quota()).build(); + assertNotEquals(PROJECT_INFO, clone); + clone = ProjectInfo.builder().number(PROJECT_INFO.number()).quota(PROJECT_INFO.quota()).build(); + assertNotEquals(PROJECT_INFO, clone); + clone = ProjectInfo.fromPb(PROJECT_INFO.toPb()); + assertEquals(PROJECT_INFO, clone); + } + + @Test + public void testSameHashCodeOnEquals() { + ProjectInfo clone = ProjectInfo.fromPb(PROJECT_INFO.toPb()); + assertEquals(PROJECT_INFO, clone); + assertEquals(PROJECT_INFO.hashCode(), clone.hashCode()); + } + + @Test + public void testToAndFromPb() { + assertEquals(PROJECT_INFO, ProjectInfo.fromPb(PROJECT_INFO.toPb())); + ProjectInfo partial = ProjectInfo.builder().id(ID).build(); + assertEquals(partial, ProjectInfo.fromPb(partial.toPb())); + partial = ProjectInfo.builder().number(NUMBER).build(); + assertEquals(partial, ProjectInfo.fromPb(partial.toPb())); + partial = ProjectInfo.builder().quota(QUOTA).build(); + assertEquals(partial, ProjectInfo.fromPb(partial.toPb())); + assertNotEquals(PROJECT_INFO, partial); + } + + @Test + public void testToAndFromPbQuota() { + assertEquals(QUOTA, ProjectInfo.Quota.fromPb(QUOTA.toPb())); + ProjectInfo.Quota wrong = new ProjectInfo.Quota(5, 6, 3, 6, 2, 1); + assertNotEquals(QUOTA, ProjectInfo.Quota.fromPb(wrong.toPb())); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/SerializationTest.java b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/SerializationTest.java new file mode 100644 index 000000000000..adf5744d854e --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/SerializationTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; + +import com.google.common.collect.ImmutableList; +import com.google.gcloud.RetryParams; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; + +public class SerializationTest { + + private static final ZoneInfo FULL_ZONE_INFO = Zone.builder("some zone name") + .creationTimeMillis(132L) + .description("some descriptions") + .dnsName("www.example.com") + .id("123333") + .nameServers(ImmutableList.of("server 1", "server 2")) + .nameServerSet("specificationstring") + .build(); + private static final ZoneInfo PARTIAL_ZONE_INFO = Zone.builder("some zone name") + .build(); + private static final ProjectInfo PARTIAL_PROJECT_INFO = ProjectInfo.builder().id("13").build(); + private static final ProjectInfo FULL_PROJECT_INFO = ProjectInfo.builder() + .id("342") + .number(new BigInteger("2343245")) + .quota(new ProjectInfo.Quota(12, 13, 14, 15, 16, 17)) + .build(); + private static final Dns.ZoneListOption ZONE_LIST_OPTION = + Dns.ZoneListOption.dnsName("www.example.com."); + private static final Dns.DnsRecordListOption DNS_REOCRD_LIST_OPTION = + Dns.DnsRecordListOption.fields(Dns.DnsRecordField.TTL); + private static final Dns.ChangeRequestListOption CHANGE_REQUEST_LIST_OPTION = + Dns.ChangeRequestListOption.fields(Dns.ChangeRequestField.STATUS); + private static final Dns.ZoneOption ZONE_OPTION = + Dns.ZoneOption.fields(Dns.ZoneField.CREATION_TIME); + private static final Dns.ChangeRequestOption CHANGE_REQUEST_OPTION = + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.STATUS); + private static final Dns.ProjectOption PROJECT_OPTION = + Dns.ProjectOption.fields(Dns.ProjectField.QUOTA); + private static final DnsOptions OPTIONS = DnsOptions.builder() + .projectId("some-unnecessary-project-ID") + .retryParams(RetryParams.defaultInstance()) + .build(); + private static final Dns DNS = OPTIONS.service(); + private static final Zone FULL_ZONE = new Zone(DNS, new ZoneInfo.BuilderImpl(FULL_ZONE_INFO)); + private static final Zone PARTIAL_ZONE = + new Zone(DNS, new ZoneInfo.BuilderImpl(PARTIAL_ZONE_INFO)); + private static final ChangeRequest CHANGE_REQUEST_PARTIAL = ChangeRequest.builder().build(); + private static final DnsRecord DNS_RECORD_PARTIAL = + DnsRecord.builder("www.www.com", DnsRecord.Type.AAAA).build(); + private static final DnsRecord DNS_RECORD_COMPLETE = + DnsRecord.builder("www.sadfa.com", DnsRecord.Type.A) + .ttl(12, TimeUnit.HOURS) + .addRecord("record") + .build(); + private static final ChangeRequest CHANGE_REQUEST_COMPLETE = ChangeRequest.builder() + .add(DNS_RECORD_COMPLETE) + .delete(DNS_RECORD_PARTIAL) + .status(ChangeRequest.Status.PENDING) + .id("some id") + .startTimeMillis(132L) + .build(); + + + @Test + public void testModelAndRequests() throws Exception { + Serializable[] objects = {FULL_ZONE_INFO, PARTIAL_ZONE_INFO, ZONE_LIST_OPTION, + DNS_REOCRD_LIST_OPTION, CHANGE_REQUEST_LIST_OPTION, ZONE_OPTION, CHANGE_REQUEST_OPTION, + PROJECT_OPTION, PARTIAL_PROJECT_INFO, FULL_PROJECT_INFO, OPTIONS, FULL_ZONE, PARTIAL_ZONE, + OPTIONS, CHANGE_REQUEST_PARTIAL, DNS_RECORD_PARTIAL, DNS_RECORD_COMPLETE, + CHANGE_REQUEST_COMPLETE}; + for (Serializable obj : objects) { + Object copy = serializeAndDeserialize(obj); + assertEquals(obj, obj); + assertEquals(obj, copy); + assertNotSame(obj, copy); + assertEquals(copy, copy); + } + } + + @SuppressWarnings("unchecked") + private T serializeAndDeserialize(T obj) throws IOException, ClassNotFoundException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try (ObjectOutputStream output = new ObjectOutputStream(bytes)) { + output.writeObject(obj); + } + try (ObjectInputStream input = + new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) { + return (T) input.readObject(); + } + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ZoneInfoTest.java b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ZoneInfoTest.java new file mode 100644 index 000000000000..227916b46f96 --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ZoneInfoTest.java @@ -0,0 +1,167 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import org.junit.Test; + +import java.util.LinkedList; +import java.util.List; + +public class ZoneInfoTest { + + private static final String NAME = "mz-example.com"; + private static final String ID = "123456"; + private static final Long CREATION_TIME_MILLIS = 1123468321321L; + private static final String DNS_NAME = "example.com."; + private static final String DESCRIPTION = "description for the zone"; + private static final String NAME_SERVER_SET = "some set"; + private static final String NS1 = "name server 1"; + private static final String NS2 = "name server 2"; + private static final String NS3 = "name server 3"; + private static final List NAME_SERVERS = ImmutableList.of(NS1, NS2, NS3); + private static final ZoneInfo INFO = ZoneInfo.builder(NAME) + .creationTimeMillis(CREATION_TIME_MILLIS) + .id(ID) + .dnsName(DNS_NAME) + .description(DESCRIPTION) + .nameServerSet(NAME_SERVER_SET) + .nameServers(NAME_SERVERS) + .build(); + + @Test + public void testDefaultBuilders() { + ZoneInfo zone = ZoneInfo.builder(NAME).build(); + assertTrue(zone.nameServers().isEmpty()); + assertEquals(NAME, zone.name()); + assertNull(zone.id()); + assertNull(zone.creationTimeMillis()); + assertNull(zone.nameServerSet()); + assertNull(zone.description()); + assertNull(zone.dnsName()); + } + + @Test + public void testBuilder() { + assertEquals(3, INFO.nameServers().size()); + assertEquals(NS1, INFO.nameServers().get(0)); + assertEquals(NS2, INFO.nameServers().get(1)); + assertEquals(NS3, INFO.nameServers().get(2)); + assertEquals(NAME, INFO.name()); + assertEquals(ID, INFO.id()); + assertEquals(CREATION_TIME_MILLIS, INFO.creationTimeMillis()); + assertEquals(NAME_SERVER_SET, INFO.nameServerSet()); + assertEquals(DESCRIPTION, INFO.description()); + assertEquals(DNS_NAME, INFO.dnsName()); + } + + @Test + public void testEqualsAndNotEquals() { + ZoneInfo clone = INFO.toBuilder().build(); + assertEquals(INFO, clone); + List moreServers = Lists.newLinkedList(NAME_SERVERS); + moreServers.add(NS1); + clone = INFO.toBuilder().nameServers(moreServers).build(); + assertNotEquals(INFO, clone); + String differentName = "totally different name"; + clone = INFO.toBuilder().name(differentName).build(); + assertNotEquals(INFO, clone); + clone = INFO.toBuilder().creationTimeMillis(INFO.creationTimeMillis() + 1).build(); + assertNotEquals(INFO, clone); + clone = INFO.toBuilder().description(INFO.description() + "aaaa").build(); + assertNotEquals(INFO, clone); + clone = INFO.toBuilder().dnsName(differentName).build(); + assertNotEquals(INFO, clone); + clone = INFO.toBuilder().id(INFO.id() + "1111").build(); + assertNotEquals(INFO, clone); + clone = INFO.toBuilder().nameServerSet(INFO.nameServerSet() + "salt").build(); + assertNotEquals(INFO, clone); + } + + @Test + public void testSameHashCodeOnEquals() { + int hash = INFO.hashCode(); + ZoneInfo clone = INFO.toBuilder().build(); + assertEquals(clone.hashCode(), hash); + } + + @Test + public void testToBuilder() { + assertEquals(INFO, INFO.toBuilder().build()); + ZoneInfo partial = ZoneInfo.builder(NAME).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ZoneInfo.builder(NAME).id(ID).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ZoneInfo.builder(NAME).description(DESCRIPTION).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ZoneInfo.builder(NAME).dnsName(DNS_NAME).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ZoneInfo.builder(NAME).creationTimeMillis(CREATION_TIME_MILLIS).build(); + assertEquals(partial, partial.toBuilder().build()); + List nameServers = new LinkedList<>(); + nameServers.add(NS1); + partial = ZoneInfo.builder(NAME).nameServers(nameServers).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ZoneInfo.builder(NAME).nameServerSet(NAME_SERVER_SET).build(); + assertEquals(partial, partial.toBuilder().build()); + } + + @Test + public void testToAndFromPb() { + assertEquals(INFO, ZoneInfo.fromPb(INFO.toPb())); + ZoneInfo partial = ZoneInfo.builder(NAME).build(); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + partial = ZoneInfo.builder(NAME).id(ID).build(); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + partial = ZoneInfo.builder(NAME).description(DESCRIPTION).build(); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + partial = ZoneInfo.builder(NAME).dnsName(DNS_NAME).build(); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + partial = ZoneInfo.builder(NAME).creationTimeMillis(CREATION_TIME_MILLIS).build(); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + List nameServers = new LinkedList<>(); + nameServers.add(NS1); + partial = ZoneInfo.builder(NAME).nameServers(nameServers).build(); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + partial = ZoneInfo.builder(NAME).nameServerSet(NAME_SERVER_SET).build(); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + } + + @Test + public void testEmptyNameServers() { + ZoneInfo clone = INFO.toBuilder().nameServers(new LinkedList()).build(); + assertTrue(clone.nameServers().isEmpty()); + clone.toPb(); // test that this is allowed + } + + @Test + public void testDateParsing() { + com.google.api.services.dns.model.ManagedZone pb = INFO.toPb(); + pb.setCreationTime("2016-01-19T18:00:12.854Z"); // a real value obtained from Google Cloud DNS + ZoneInfo mz = ZoneInfo.fromPb(pb); // parses the string timestamp to millis + com.google.api.services.dns.model.ManagedZone pbClone = mz.toPb(); // converts it back to string + assertEquals(pb, pbClone); + assertEquals(pb.getCreationTime(), pbClone.getCreationTime()); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ZoneTest.java b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ZoneTest.java new file mode 100644 index 000000000000..5164dfb6001c --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/gcloud/dns/ZoneTest.java @@ -0,0 +1,533 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.dns; + +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.reset; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableList; +import com.google.gcloud.Page; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigInteger; + +public class ZoneTest { + + private static final String ZONE_NAME = "dns-zone-name"; + private static final String ZONE_ID = "123"; + private static final ZoneInfo ZONE_INFO = Zone.builder(ZONE_NAME) + .id(ZONE_ID) + .dnsName("example.com") + .creationTimeMillis(123478946464L) + .build(); + private static final ZoneInfo NO_ID_INFO = ZoneInfo.builder(ZONE_NAME) + .dnsName("another-example.com") + .creationTimeMillis(893123464L) + .build(); + private static final Dns.ZoneOption ZONE_FIELD_OPTIONS = + Dns.ZoneOption.fields(Dns.ZoneField.CREATION_TIME); + private static final Dns.DnsRecordListOption DNS_RECORD_OPTIONS = + Dns.DnsRecordListOption.dnsName("some-dns"); + private static final Dns.ChangeRequestOption CHANGE_REQUEST_FIELD_OPTIONS = + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.START_TIME); + private static final Dns.ChangeRequestListOption CHANGE_REQUEST_LIST_OPTIONS = + Dns.ChangeRequestListOption.fields(Dns.ChangeRequestField.START_TIME); + private static final ChangeRequest CHANGE_REQUEST = ChangeRequest.builder().id("someid").build(); + private static final ChangeRequest CHANGE_REQUEST_AFTER = CHANGE_REQUEST.toBuilder() + .startTimeMillis(123465L).build(); + private static final ChangeRequest CHANGE_REQUEST_NO_ID = ChangeRequest.builder().build(); + private static final DnsException EXCEPTION = createStrictMock(DnsException.class); + private static final DnsOptions OPTIONS = createStrictMock(DnsOptions.class); + + private Dns dns; + private Zone zone; + private Zone zoneNoId; + + + @Before + public void setUp() throws Exception { + dns = createStrictMock(Dns.class); + expect(dns.options()).andReturn(OPTIONS); + expect(dns.options()).andReturn(OPTIONS); + replay(dns); + zone = new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)); + zoneNoId = new Zone(dns, new ZoneInfo.BuilderImpl(NO_ID_INFO)); + reset(dns); + } + + @After + public void tearDown() throws Exception { + verify(dns); + } + + @Test + public void testConstructor() { + replay(dns); + assertEquals(ZONE_INFO.toPb(), zone.toPb()); + assertNotNull(zone.dns()); + assertEquals(dns, zone.dns()); + } + + @Test + public void deleteByNameAndFound() { + expect(dns.delete(ZONE_NAME)).andReturn(true); + expect(dns.delete(ZONE_NAME)).andReturn(true); + replay(dns); + boolean result = zone.delete(); + assertTrue(result); + result = zoneNoId.delete(); + assertTrue(result); + } + + @Test + public void deleteByNameAndNotFound() { + expect(dns.delete(ZONE_NAME)).andReturn(false); + expect(dns.delete(ZONE_NAME)).andReturn(false); + replay(dns); + boolean result = zoneNoId.delete(); + assertFalse(result); + result = zone.delete(); + assertFalse(result); + } + + @Test + public void listDnsRecordsByNameAndFound() { + @SuppressWarnings("unchecked") + Page pageMock = createStrictMock(Page.class); + replay(pageMock); + expect(dns.listDnsRecords(ZONE_NAME)).andReturn(pageMock); + expect(dns.listDnsRecords(ZONE_NAME)).andReturn(pageMock); + // again for options + expect(dns.listDnsRecords(ZONE_NAME, DNS_RECORD_OPTIONS)).andReturn(pageMock); + expect(dns.listDnsRecords(ZONE_NAME, DNS_RECORD_OPTIONS)).andReturn(pageMock); + replay(dns); + Page result = zone.listDnsRecords(); + assertSame(pageMock, result); + result = zoneNoId.listDnsRecords(); + assertSame(pageMock, result); + verify(pageMock); + zone.listDnsRecords(DNS_RECORD_OPTIONS); // check options + zoneNoId.listDnsRecords(DNS_RECORD_OPTIONS); // check options + } + + @Test + public void listDnsRecordsByNameAndNotFound() { + expect(dns.listDnsRecords(ZONE_NAME)).andThrow(EXCEPTION); + expect(dns.listDnsRecords(ZONE_NAME)).andThrow(EXCEPTION); + // again for options + expect(dns.listDnsRecords(ZONE_NAME, DNS_RECORD_OPTIONS)).andThrow(EXCEPTION); + expect(dns.listDnsRecords(ZONE_NAME, DNS_RECORD_OPTIONS)).andThrow(EXCEPTION); + replay(dns); + try { + zoneNoId.listDnsRecords(); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.listDnsRecords(); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zoneNoId.listDnsRecords(DNS_RECORD_OPTIONS); // check options + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.listDnsRecords(DNS_RECORD_OPTIONS); // check options + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + } + + @Test + public void reloadByNameAndFound() { + expect(dns.getZone(ZONE_NAME)).andReturn(zone); + expect(dns.getZone(ZONE_NAME)).andReturn(zone); + // again for options + expect(dns.getZone(ZONE_NAME, ZONE_FIELD_OPTIONS)).andReturn(zoneNoId); + expect(dns.getZone(ZONE_NAME, ZONE_FIELD_OPTIONS)).andReturn(zone); + replay(dns); + Zone result = zoneNoId.reload(); + assertSame(zone.dns(), result.dns()); + assertEquals(zone, result); + result = zone.reload(); + assertSame(zone.dns(), result.dns()); + assertEquals(zone, result); + zoneNoId.reload(ZONE_FIELD_OPTIONS); // check options + zone.reload(ZONE_FIELD_OPTIONS); // check options + } + + @Test + public void reloadByNameAndNotFound() { + expect(dns.getZone(ZONE_NAME)).andReturn(null); + expect(dns.getZone(ZONE_NAME)).andReturn(null); + // again for options + expect(dns.getZone(ZONE_NAME, ZONE_FIELD_OPTIONS)).andReturn(null); + expect(dns.getZone(ZONE_NAME, ZONE_FIELD_OPTIONS)).andReturn(null); + replay(dns); + Zone result = zoneNoId.reload(); + assertNull(result); + result = zone.reload(); + assertNull(result); + zoneNoId.reload(ZONE_FIELD_OPTIONS); // for options + zone.reload(ZONE_FIELD_OPTIONS); // for options + } + + @Test + public void applyChangeByNameAndFound() { + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST)) + .andReturn(CHANGE_REQUEST_AFTER); + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST)) + .andReturn(CHANGE_REQUEST_AFTER); + // again for options + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS)) + .andReturn(CHANGE_REQUEST_AFTER); + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS)) + .andReturn(CHANGE_REQUEST_AFTER); + replay(dns); + ChangeRequest result = zoneNoId.applyChangeRequest(CHANGE_REQUEST); + assertEquals(CHANGE_REQUEST_AFTER, result); + result = zone.applyChangeRequest(CHANGE_REQUEST); + assertEquals(CHANGE_REQUEST_AFTER, result); + // check options + result = zoneNoId.applyChangeRequest(CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS); + assertEquals(CHANGE_REQUEST_AFTER, result); + result = zone.applyChangeRequest(CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS); + assertEquals(CHANGE_REQUEST_AFTER, result); + } + + @Test + public void applyChangeByNameAndNotFound() { + // ID is not set + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST)).andThrow(EXCEPTION); + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST)).andThrow(EXCEPTION); + // again for options + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS)) + .andThrow(EXCEPTION); + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS)) + .andThrow(EXCEPTION); + replay(dns); + try { + zoneNoId.applyChangeRequest(CHANGE_REQUEST); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.applyChangeRequest(CHANGE_REQUEST); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + // check options + try { + zoneNoId.applyChangeRequest(CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.applyChangeRequest(CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + } + + @Test + public void applyNullChangeRequest() { + replay(dns); // no calls expected + try { + zone.applyChangeRequest(null); + fail("Cannot apply null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zone.applyChangeRequest(null, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot apply null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.applyChangeRequest(null); + fail("Cannot apply null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.applyChangeRequest(null, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot apply null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + } + + @Test + public void getChangeAndZoneFoundByName() { + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id())) + .andReturn(CHANGE_REQUEST_AFTER); + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id())) + .andReturn(CHANGE_REQUEST_AFTER); + // again for options + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS)) + .andReturn(CHANGE_REQUEST_AFTER); + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS)) + .andReturn(CHANGE_REQUEST_AFTER); + replay(dns); + ChangeRequest result = zoneNoId.getChangeRequest(CHANGE_REQUEST.id()); + assertEquals(CHANGE_REQUEST_AFTER, result); + result = zone.getChangeRequest(CHANGE_REQUEST.id()); + assertEquals(CHANGE_REQUEST_AFTER, result); + // check options + result = zoneNoId.getChangeRequest(CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS); + assertEquals(CHANGE_REQUEST_AFTER, result); + result = zone.getChangeRequest(CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS); + assertEquals(CHANGE_REQUEST_AFTER, result); + } + + @Test + public void getChangeAndZoneNotFoundByName() { + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id())).andThrow(EXCEPTION); + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id())).andThrow(EXCEPTION); + // again for options + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS)) + .andThrow(EXCEPTION); + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS)) + .andThrow(EXCEPTION); + replay(dns); + try { + zoneNoId.getChangeRequest(CHANGE_REQUEST.id()); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.getChangeRequest(CHANGE_REQUEST.id()); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + // check options + try { + zoneNoId.getChangeRequest(CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.getChangeRequest(CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + } + + @Test + public void getChangedWhichDoesNotExistZoneFound() { + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id())).andReturn(null); + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id())).andReturn(null); + // again for options + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS)) + .andReturn(null); + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS)) + .andReturn(null); + replay(dns); + assertNull(zoneNoId.getChangeRequest(CHANGE_REQUEST.id())); + assertNull(zone.getChangeRequest(CHANGE_REQUEST.id())); + assertNull(zoneNoId.getChangeRequest(CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS)); + assertNull(zone.getChangeRequest(CHANGE_REQUEST.id(), CHANGE_REQUEST_FIELD_OPTIONS)); + } + + @Test + public void getNullChangeRequest() { + replay(dns); // no calls expected + try { + zone.getChangeRequest(null); + fail("Cannot get null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zone.getChangeRequest(null, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot get null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.getChangeRequest(null); + fail("Cannot get null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.getChangeRequest(null, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot get null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + } + + @Test + public void getChangeRequestWithNoId() { + replay(dns); // no calls expected + try { + zone.getChangeRequest(CHANGE_REQUEST_NO_ID.id()); + fail("Cannot get ChangeRequest by null id."); + } catch (NullPointerException e) { + // expected + } + try { + zone.getChangeRequest(CHANGE_REQUEST_NO_ID.id(), CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot get ChangeRequest by null id."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.getChangeRequest(CHANGE_REQUEST_NO_ID.id()); + fail("Cannot get ChangeRequest by null id."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.getChangeRequest(CHANGE_REQUEST_NO_ID.id(), CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot get ChangeRequest by null id."); + } catch (NullPointerException e) { + // expected + } + } + + @Test + public void listChangeRequestsAndZoneFound() { + @SuppressWarnings("unchecked") + Page pageMock = createStrictMock(Page.class); + replay(pageMock); + expect(dns.listChangeRequests(ZONE_NAME)).andReturn(pageMock); + expect(dns.listChangeRequests(ZONE_NAME)).andReturn(pageMock); + // again for options + expect(dns.listChangeRequests(ZONE_NAME, CHANGE_REQUEST_LIST_OPTIONS)) + .andReturn(pageMock); + expect(dns.listChangeRequests(ZONE_NAME, CHANGE_REQUEST_LIST_OPTIONS)) + .andReturn(pageMock); + replay(dns); + Page result = zoneNoId.listChangeRequests(); + assertSame(pageMock, result); + result = zone.listChangeRequests(); + assertSame(pageMock, result); + verify(pageMock); + zoneNoId.listChangeRequests(CHANGE_REQUEST_LIST_OPTIONS); // check options + zone.listChangeRequests(CHANGE_REQUEST_LIST_OPTIONS); // check options + } + + @Test + public void listChangeRequestsAndZoneNotFound() { + expect(dns.listChangeRequests(ZONE_NAME)).andThrow(EXCEPTION); + expect(dns.listChangeRequests(ZONE_NAME)).andThrow(EXCEPTION); + // again for options + expect(dns.listChangeRequests(ZONE_NAME, CHANGE_REQUEST_LIST_OPTIONS)).andThrow(EXCEPTION); + expect(dns.listChangeRequests(ZONE_NAME, CHANGE_REQUEST_LIST_OPTIONS)).andThrow(EXCEPTION); + replay(dns); + try { + zoneNoId.listChangeRequests(); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.listChangeRequests(); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zoneNoId.listChangeRequests(CHANGE_REQUEST_LIST_OPTIONS); // check options + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.listChangeRequests(CHANGE_REQUEST_LIST_OPTIONS); // check options + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + } + + @Test + public void testFromPb() { + expect(dns.options()).andReturn(OPTIONS); + replay(dns); + assertEquals(Zone.fromPb(dns, zone.toPb()), zone); + } + + @Test + public void testEqualsAndToBuilder() { + expect(dns.options()).andReturn(OPTIONS); + expect(dns.options()).andReturn(OPTIONS); + replay(dns); + assertEquals(zone, zone.toBuilder().build()); + assertEquals(zone.hashCode(), zone.toBuilder().build().hashCode()); + } + + @Test + public void testBuilder() { + // one for each build() call because it invokes a constructor + expect(dns.options()).andReturn(OPTIONS); + expect(dns.options()).andReturn(OPTIONS); + expect(dns.options()).andReturn(OPTIONS); + expect(dns.options()).andReturn(OPTIONS); + expect(dns.options()).andReturn(OPTIONS); + expect(dns.options()).andReturn(OPTIONS); + expect(dns.options()).andReturn(OPTIONS); + expect(dns.options()).andReturn(OPTIONS); + replay(dns); + assertNotEquals(zone, zone.toBuilder() + .id((new BigInteger(zone.id())).add(BigInteger.ONE).toString()) + .build()); + assertNotEquals(zone, zone.toBuilder().dnsName(zone.name() + "aaaa").build()); + assertNotEquals(zone, zone.toBuilder().nameServerSet(zone.nameServerSet() + "aaaa").build()); + assertNotEquals(zone, zone.toBuilder().nameServers(ImmutableList.of("nameserverpppp")).build()); + assertNotEquals(zone, zone.toBuilder().dnsName(zone.dnsName() + "aaaa").build()); + assertNotEquals(zone, zone.toBuilder().creationTimeMillis(zone.creationTimeMillis() + 1) + .build()); + Zone.Builder builder = zone.toBuilder(); + builder.id(ZONE_ID) + .dnsName("example.com") + .creationTimeMillis(123478946464L) + .build(); + assertEquals(zone, builder.build()); + } +} diff --git a/pom.xml b/pom.xml index cd5c19720cd8..7a435cc1dbae 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,7 @@ gcloud-java-bigquery gcloud-java-core gcloud-java-datastore + gcloud-java-dns gcloud-java-examples gcloud-java-resourcemanager gcloud-java-storage