/*
* Copyright 2016 Google LLC
*
* 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.cloud.dns;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.api.core.ApiFunction;
import com.google.api.services.dns.model.Change;
import com.google.cloud.StringEnumType;
import com.google.cloud.StringEnumValue;
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 java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import org.threeten.bp.Instant;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.format.DateTimeFormatter;
/**
* A class representing an atomic update to a collection of {@link RecordSet}s within a {@code
* Zone}.
*
* @see Google Cloud DNS documentation
*/
public class ChangeRequestInfo implements Serializable {
static final Function FROM_PB_FUNCTION =
new Function() {
@Override
public ChangeRequestInfo apply(Change pb) {
return ChangeRequestInfo.fromPb(pb);
}
};
private static final long serialVersionUID = -6029143477639439169L;
private final List additions;
private final List deletions;
private final String generatedId;
private final Long startTimeMillis;
private final ChangeRequestInfo.Status status;
/**
* This enumerates the possible states of a change request.
*
* @see Google Cloud DNS
* documentation
*/
public static final class Status extends StringEnumValue {
private static final long serialVersionUID = -294992980062438246L;
private static final ApiFunction CONSTRUCTOR =
new ApiFunction() {
@Override
public Status apply(String constant) {
return new Status(constant);
}
};
private static final StringEnumType type =
new StringEnumType(Status.class, CONSTRUCTOR);
public static final Status PENDING = type.createAndRegister("PENDING");
public static final Status DONE = type.createAndRegister("DONE");
private Status(String constant) {
super(constant);
}
/**
* Get the Status for the given String constant, and throw an exception if the constant is not
* recognized.
*/
public static Status valueOfStrict(String constant) {
return type.valueOfStrict(constant);
}
/** Get the Status for the given String constant, and allow unrecognized values. */
public static Status valueOf(String constant) {
return type.valueOf(constant);
}
/** Return the known values for Status. */
public static Status[] values() {
return type.values();
}
}
/** A builder for {@code ChangeRequestInfo}. */
public abstract static class Builder {
/**
* Sets a collection of {@link RecordSet}s which are to be added to the zone upon executing this
* {@code ChangeRequestInfo}.
*/
public abstract Builder setAdditions(List additions);
/**
* Sets a collection of {@link RecordSet}s which are to be deleted from the zone upon executing
* this {@code ChangeRequestInfo}.
*/
public abstract Builder setDeletions(List deletions);
/**
* Adds a {@link RecordSet} to be added to the zone upon executing this {@code
* ChangeRequestInfo}.
*/
public abstract Builder add(RecordSet recordSet);
/**
* Adds a {@link RecordSet} to be deleted to the zone upon executing this
* {@code ChangeRequestInfo}.
*/
public abstract Builder delete(RecordSet recordSet);
/**
* Clears the collection of {@link RecordSet}s which are to be added to the zone upon executing
* this {@code ChangeRequestInfo}.
*/
public abstract Builder clearAdditions();
/**
* Clears the collection of {@link RecordSet}s which are to be deleted from the zone upon
* executing this {@code ChangeRequestInfo}.
*/
public abstract Builder clearDeletions();
/**
* Removes a single {@link RecordSet} from the collection of records to be
* added to the zone upon executing this {@code ChangeRequestInfo}.
*/
public abstract Builder removeAddition(RecordSet recordSet);
/**
* Removes a single {@link RecordSet} from the collection of records to be
* deleted from the zone upon executing this {@code ChangeRequestInfo}.
*/
public abstract Builder removeDeletion(RecordSet recordSet);
/** Associates a service-generated id to this {@code ChangeRequestInfo}. */
abstract Builder setGeneratedId(String generatedId);
/** Sets the time when this change request was started by a server. */
abstract Builder setStartTime(long startTimeMillis);
/** Sets the current status of this {@code ChangeRequest}. */
abstract Builder setStatus(ChangeRequest.Status status);
/**
* Creates a {@code ChangeRequestInfo} instance populated by the values associated with this
* builder.
*/
public abstract ChangeRequestInfo build();
}
static class BuilderImpl extends Builder {
private List additions;
private List deletions;
private String generatedId;
private Long startTimeMillis;
private ChangeRequestInfo.Status status;
BuilderImpl() {
this.additions = new LinkedList<>();
this.deletions = new LinkedList<>();
}
BuilderImpl(ChangeRequestInfo info) {
this.additions = Lists.newLinkedList(info.getAdditions());
this.deletions = Lists.newLinkedList(info.getDeletions());
this.generatedId = info.generatedId;
this.startTimeMillis = info.startTimeMillis;
this.status = info.status;
}
@Override
public Builder setAdditions(List additions) {
this.additions = Lists.newLinkedList(checkNotNull(additions));
return this;
}
@Override
public Builder setDeletions(List deletions) {
this.deletions = Lists.newLinkedList(checkNotNull(deletions));
return this;
}
@Override
public Builder add(RecordSet recordSet) {
this.additions.add(checkNotNull(recordSet));
return this;
}
@Override
public Builder delete(RecordSet recordSet) {
this.deletions.add(checkNotNull(recordSet));
return this;
}
@Override
public Builder clearAdditions() {
this.additions.clear();
return this;
}
@Override
public Builder clearDeletions() {
this.deletions.clear();
return this;
}
@Override
public Builder removeAddition(RecordSet recordSet) {
this.additions.remove(recordSet);
return this;
}
@Override
public Builder removeDeletion(RecordSet recordSet) {
this.deletions.remove(recordSet);
return this;
}
@Override
public ChangeRequestInfo build() {
return new ChangeRequestInfo(this);
}
@Override
Builder setGeneratedId(String generatedId) {
this.generatedId = checkNotNull(generatedId);
return this;
}
@Override
Builder setStartTime(long startTimeMillis) {
this.startTimeMillis = startTimeMillis;
return this;
}
@Override
Builder setStatus(ChangeRequestInfo.Status status) {
this.status = checkNotNull(status);
return this;
}
}
ChangeRequestInfo(BuilderImpl builder) {
this.additions = ImmutableList.copyOf(builder.additions);
this.deletions = ImmutableList.copyOf(builder.deletions);
this.generatedId = builder.generatedId;
this.startTimeMillis = builder.startTimeMillis;
this.status = builder.status;
}
/** Returns an empty builder for the {@code ChangeRequestInfo} class. */
public static Builder newBuilder() {
return new BuilderImpl();
}
/** Creates a builder populated with values of this {@code ChangeRequestInfo}. */
public Builder toBuilder() {
return new BuilderImpl(this);
}
/**
* Returns the list of {@link RecordSet}s to be added to the zone upon submitting this change
* request.
*/
public List getAdditions() {
return additions;
}
/**
* Returns the list of {@link RecordSet}s to be deleted from the zone upon submitting this change
* request.
*/
public List getDeletions() {
return deletions;
}
/** Returns the service-generated id for this change request. */
public String getGeneratedId() {
return generatedId;
}
/** Returns the time when this change request was started by the server. */
public Long getStartTimeMillis() {
return startTimeMillis;
}
/**
* Returns the status of this change request. If the change request has not been applied yet, the
* status is {@code PENDING}.
*/
public ChangeRequestInfo.Status status() {
return getStatus();
}
/**
* Returns the status of this change request. If the change request has not been applied yet, the
* status is {@code PENDING}.
*/
public ChangeRequestInfo.Status getStatus() {
return status;
}
Change toPb() {
Change pb = new Change();
// set id
if (getGeneratedId() != null) {
pb.setId(getGeneratedId());
}
// set timestamp
if (getStartTimeMillis() != null) {
pb.setStartTime(
DateTimeFormatter.ISO_DATE_TIME
.withZone(ZoneOffset.UTC)
.format(Instant.ofEpochMilli(getStartTimeMillis())));
}
// set status
if (status() != null) {
pb.setStatus(status().name().toLowerCase());
}
// set a list of additions
pb.setAdditions(Lists.transform(getAdditions(), RecordSet.TO_PB_FUNCTION));
// set a list of deletions
pb.setDeletions(Lists.transform(getDeletions(), RecordSet.TO_PB_FUNCTION));
return pb;
}
static ChangeRequestInfo fromPb(Change pb) {
Builder builder = newBuilder();
if (pb.getId() != null) {
builder.setGeneratedId(pb.getId());
}
if (pb.getStartTime() != null) {
builder.setStartTime(
DateTimeFormatter.ISO_DATE_TIME.parse(pb.getStartTime(), Instant.FROM).toEpochMilli());
}
if (pb.getStatus() != null) {
// we are assuming that status indicated in pb is a lower case version of the enum name
builder.setStatus(ChangeRequest.Status.valueOf(pb.getStatus().toUpperCase()));
}
if (pb.getDeletions() != null) {
builder.setDeletions(Lists.transform(pb.getDeletions(), RecordSet.FROM_PB_FUNCTION));
}
if (pb.getAdditions() != null) {
builder.setAdditions(Lists.transform(pb.getAdditions(), RecordSet.FROM_PB_FUNCTION));
}
return builder.build();
}
@Override
public boolean equals(Object obj) {
return obj == this
|| obj != null
&& obj.getClass().equals(ChangeRequestInfo.class)
&& toPb().equals(((ChangeRequestInfo) obj).toPb());
}
@Override
public int hashCode() {
return Objects.hash(additions, deletions, generatedId, startTimeMillis, status);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("additions", additions)
.add("deletions", deletions)
.add("generatedId", generatedId)
.add("startTimeMillis", startTimeMillis)
.add("status", status)
.toString();
}
}