From b7fca7bff623759f3d29cde47317808294ad413e Mon Sep 17 00:00:00 2001 From: adriancole Date: Mon, 4 Feb 2013 16:30:34 -0800 Subject: [PATCH] finished zone support for dynect --- .../java/org/jclouds/dynect/v3/DynECTApi.java | 11 ++ .../org/jclouds/dynect/v3/DynECTAsyncApi.java | 27 ++- .../v3/config/DynECTRestClientModule.java | 3 + .../dynect/v3/domain/CreatePrimaryZone.java | 161 ++++++++++++++++++ .../org/jclouds/dynect/v3/domain/Job.java | 99 +++++++++++ .../org/jclouds/dynect/v3/domain/Zone.java | 34 ++-- .../jclouds/dynect/v3/features/ZoneApi.java | 70 +++++++- .../dynect/v3/features/ZoneAsyncApi.java | 84 ++++++++- .../GetJobRedirectionRetryHandler.java | 64 +++++++ .../dynect/v3/features/ZoneApiExpectTest.java | 91 +++++++++- .../dynect/v3/features/ZoneApiLiveTest.java | 70 +++++++- ...tJobRedirectionRetryHandlerExpectTest.java | 70 ++++++++ .../v3/internal/BaseDynECTExpectTest.java | 6 + .../v3/parse/DeleteZoneResponseTest.java | 45 +++++ .../dynect/v3/parse/GetZoneResponseTest.java | 2 +- .../src/test/resources/delete_zone.json | 1 + 16 files changed, 798 insertions(+), 40 deletions(-) create mode 100644 labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/CreatePrimaryZone.java create mode 100644 labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/Job.java create mode 100644 labs/dynect/src/main/java/org/jclouds/dynect/v3/handlers/GetJobRedirectionRetryHandler.java create mode 100644 labs/dynect/src/test/java/org/jclouds/dynect/v3/handlers/GetJobRedirectionRetryHandlerExpectTest.java create mode 100644 labs/dynect/src/test/java/org/jclouds/dynect/v3/parse/DeleteZoneResponseTest.java create mode 100644 labs/dynect/src/test/resources/delete_zone.json diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTApi.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTApi.java index fb45ca5909..e4bb4c727a 100644 --- a/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTApi.java +++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTApi.java @@ -18,8 +18,10 @@ */ package org.jclouds.dynect.v3; +import org.jclouds.dynect.v3.domain.Job; import org.jclouds.dynect.v3.features.SessionApi; import org.jclouds.dynect.v3.features.ZoneApi; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.rest.annotations.Delegate; /** @@ -32,6 +34,15 @@ import org.jclouds.rest.annotations.Delegate; * @author Adrian Cole */ public interface DynECTApi { + /** + * returns the current status of a job. + * + * @param jobId + * The ID of the job + * @return null, if not found + */ + @Nullable + Job getJob(long jobId); /** * Provides synchronous access to Session features. diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTAsyncApi.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTAsyncApi.java index 761864cd16..3f4a7c5a35 100644 --- a/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTAsyncApi.java +++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTAsyncApi.java @@ -20,12 +20,24 @@ package org.jclouds.dynect.v3; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import javax.inject.Named; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import org.jclouds.Fallbacks.NullOnNotFoundOr404; +import org.jclouds.dynect.v3.domain.Job; import org.jclouds.dynect.v3.features.SessionAsyncApi; import org.jclouds.dynect.v3.features.ZoneAsyncApi; +import org.jclouds.dynect.v3.filters.SessionManager; import org.jclouds.rest.annotations.Delegate; +import org.jclouds.rest.annotations.Fallback; import org.jclouds.rest.annotations.Headers; +import org.jclouds.rest.annotations.RequestFilters; + +import com.google.common.util.concurrent.ListenableFuture; /** * Provides access to DynECT Managed DNS through the API2 api @@ -35,10 +47,19 @@ import org.jclouds.rest.annotations.Headers; * @see * @author Adrian Cole */ -// required for all calls -@Produces(APPLICATION_JSON) -@Headers(keys = "API-Version", values = "{jclouds.api-version}") public interface DynECTAsyncApi { + /** + * @see DynECTApi#getJob + */ + @Named("GetJob") + @GET + @Path("/Job/{jobId}") + @Produces(APPLICATION_JSON) + @RequestFilters(SessionManager.class) + @Headers(keys = "API-Version", values = "{jclouds.api-version}") + @Fallback(NullOnNotFoundOr404.class) + @Consumes(APPLICATION_JSON) + ListenableFuture getJob(@PathParam("jobId") long jobId); /** * Provides asynchronous access to Session features. diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTRestClientModule.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTRestClientModule.java index c020aef8b9..770ed7dbf1 100644 --- a/labs/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTRestClientModule.java +++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTRestClientModule.java @@ -29,8 +29,10 @@ import org.jclouds.dynect.v3.features.SessionAsyncApi; import org.jclouds.dynect.v3.features.ZoneApi; import org.jclouds.dynect.v3.features.ZoneAsyncApi; import org.jclouds.dynect.v3.filters.SessionManager; +import org.jclouds.dynect.v3.handlers.GetJobRedirectionRetryHandler; import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.annotation.ClientError; +import org.jclouds.http.handlers.RedirectionRetryHandler; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.config.RestClientModule; @@ -61,6 +63,7 @@ public class DynECTRestClientModule extends RestClientModule { + public String apply(Object in) { + return CreatePrimaryZone.class.cast(in).getFQDN(); + } + } +} \ No newline at end of file diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/Job.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/Job.java new file mode 100644 index 0000000000..6247997821 --- /dev/null +++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/Job.java @@ -0,0 +1,99 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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 org.jclouds.dynect.v3.domain; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.beans.ConstructorProperties; + +import javax.inject.Named; + +import com.google.common.base.Objects; + +/** + * @author Adrian Cole + */ +public final class Job { + + @Named("job_id") + private final long id; + private final Status status; + + @ConstructorProperties({ "job_id", "status" }) + private Job(long id, Status status) { + this.id = checkNotNull(id, "id"); + this.status = checkNotNull(status, "status for %s", id); + } + + /** + * The ID of the job. + */ + public long getId() { + return id; + } + + /** + * The current status of the job. + */ + public Status getStatus() { + return status; + } + + public enum Status { + SUCCESS, FAILURE, UNRECOGNIZED; + + public static Status fromValue(String status) { + try { + return valueOf(checkNotNull(status, "status").toUpperCase()); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + Job that = Job.class.cast(obj); + return equal(this.id, that.id); + } + + @Override + public String toString() { + return toStringHelper(this).add("id", id).add("status", status).toString(); + } + + public static Job success(long id) { + return new Job(id, Status.SUCCESS); + } + + public static Job failure(long id) { + return new Job(id, Status.FAILURE); + } +} diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/Zone.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/Zone.java index c4cda630c3..9d064ecad7 100644 --- a/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/Zone.java +++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/Zone.java @@ -34,7 +34,7 @@ import com.google.common.base.Objects; */ public final class Zone { - private final String name; + private final String fqdn; @Named("zone_type") private final Type type; private final int serial; @@ -92,18 +92,18 @@ public final class Zone { } @ConstructorProperties({ "zone", "zone_type", "serial", "serial_style" }) - private Zone(String name, Type type, int serial, SerialStyle serialStyle) { - this.name = checkNotNull(name, "name"); - this.type = checkNotNull(type, "type for %s", name); - this.serial = checkNotNull(serial, "serial for %s", name); + private Zone(String fqdn, Type type, int serial, SerialStyle serialStyle) { + this.fqdn = checkNotNull(fqdn, "fqdn"); + this.type = checkNotNull(type, "type for %s", fqdn); + this.serial = checkNotNull(serial, "serial for %s", fqdn); this.serialStyle = checkNotNull(serialStyle, "serialStyle for %s", serialStyle); } /** - * The name of the requested zone + * The fqdn of the requested zone */ - public String getName() { - return name; + public String getFQDN() { + return fqdn; } /** @@ -129,7 +129,7 @@ public final class Zone { @Override public int hashCode() { - return Objects.hashCode(name, type); + return Objects.hashCode(fqdn, type); } @Override @@ -139,12 +139,12 @@ public final class Zone { if (obj == null || getClass() != obj.getClass()) return false; Zone that = Zone.class.cast(obj); - return equal(this.name, that.name) && equal(this.type, that.type); + return equal(this.fqdn, that.fqdn) && equal(this.type, that.type); } @Override public String toString() { - return toStringHelper(this).omitNullValues().add("name", name).add("type", type).add("serial", serial) + return toStringHelper(this).omitNullValues().add("fqdn", fqdn).add("type", type).add("serial", serial) .add("serialStyle", serialStyle).toString(); } @@ -157,16 +157,16 @@ public final class Zone { } public final static class Builder { - private String name; + private String fqdn; private Type type; private int serial; private SerialStyle serialStyle; /** - * @see Zone#getName() + * @see Zone#getFQDN() */ - public Builder name(String name) { - this.name = name; + public Builder fqdn(String fqdn) { + this.fqdn = fqdn; return this; } @@ -195,11 +195,11 @@ public final class Zone { } public Zone build() { - return new Zone(name, type, serial, serialStyle); + return new Zone(fqdn, type, serial, serialStyle); } public Builder from(Zone in) { - return this.name(in.name).type(in.type).serial(in.serial).serialStyle(in.serialStyle); + return this.fqdn(in.fqdn).type(in.type).serial(in.serial).serialStyle(in.serialStyle); } } } \ No newline at end of file diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/ZoneApi.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/ZoneApi.java index d879147317..193f520177 100644 --- a/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/ZoneApi.java +++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/ZoneApi.java @@ -18,7 +18,10 @@ */ package org.jclouds.dynect.v3.features; +import org.jclouds.dynect.v3.domain.CreatePrimaryZone; +import org.jclouds.dynect.v3.domain.Job; import org.jclouds.dynect.v3.domain.Zone; +import org.jclouds.dynect.v3.domain.Zone.SerialStyle; import org.jclouds.javax.annotation.Nullable; import com.google.common.collect.FluentIterable; @@ -34,14 +37,69 @@ public interface ZoneApi { FluentIterable list(); /** - * Retrieves information about the specified zone, including its nameserver - * configuration + * Creates a new primary zone. * - * @param name - * name of the zone to get information about. ex - * {@code Z1PA6795UKMFR9} + * @param zone + * required parameters to create the zone. + * @return unpublished zone + */ + Zone create(CreatePrimaryZone zone); + + /** + * Creates a new primary zone with one hour default TTL and + * {@link SerialStyle#INCREMENT} + * + * @param fqdn + * fqdn of the zone to create {@ex. jclouds.org} + * @param contact + * email address of the contact + * @return unpublished zone + */ + Zone createWithContact(String fqdn, String contact); + + /** + * Retrieves information about the specified zone. + * + * @param fqdn + * fqdn of the zone to get information about. ex + * {@code jclouds.org} * @return null if not found */ @Nullable - Zone get(String name); + Zone get(String fqdn); + + /** + * deletes the specified zone. + * + * @param fqdn + * fqdn of the zone to delete ex {@code jclouds.org} + * @return null if not found + */ + @Nullable + Job delete(String fqdn); + + /** + * Publishes the current zone + * + * @param fqdn + * fqdn of the zone to publish. ex + * {@code jclouds.org} + */ + Zone publish(String fqdn); + + /** + * freezes the specified zone. + * + * @param fqdn + * fqdn of the zone to freeze ex {@code jclouds.org} + */ + Job freeze(String fqdn); + + /** + * thaws the specified zone. + * + * @param fqdn + * fqdn of the zone to thaw ex {@code jclouds.org} + */ + Job thaw(String fqdn); } \ No newline at end of file diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/ZoneAsyncApi.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/ZoneAsyncApi.java index 5a18b000e6..f84c024792 100644 --- a/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/ZoneAsyncApi.java +++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/ZoneAsyncApi.java @@ -21,20 +21,32 @@ package org.jclouds.dynect.v3.features; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import javax.inject.Named; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import org.jclouds.Fallbacks.NullOnNotFoundOr404; +import org.jclouds.dynect.v3.domain.CreatePrimaryZone; +import org.jclouds.dynect.v3.domain.CreatePrimaryZone.ToFQDN; +import org.jclouds.dynect.v3.domain.Job; import org.jclouds.dynect.v3.domain.Zone; import org.jclouds.dynect.v3.filters.SessionManager; import org.jclouds.dynect.v3.functions.ExtractNames; +import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.Fallback; import org.jclouds.rest.annotations.Headers; +import org.jclouds.rest.annotations.ParamParser; +import org.jclouds.rest.annotations.Payload; +import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.SelectJson; import org.jclouds.rest.annotations.Transform; +import org.jclouds.rest.binders.BindToJsonPayload; import com.google.common.collect.FluentIterable; import com.google.common.util.concurrent.ListenableFuture; @@ -56,19 +68,79 @@ public interface ZoneAsyncApi { /** * @see ZoneApi#list */ - @Named("GET:ZoneList") + @Named("ListZones") @GET @SelectJson("data") @Transform(ExtractNames.class) ListenableFuture> list(); - + /** - * @see ZoneApi#isValid + * @see ZoneApi#get */ - @Named("GET:Zone") + @Named("GetZone") @GET - @Path("/{name}") + @Path("/{fqdn}") @SelectJson("data") @Fallback(NullOnNotFoundOr404.class) - ListenableFuture get(@PathParam("name") String name); + ListenableFuture get(@PathParam("fqdn") String fqdn); + + /** + * @see ZoneApi#create + */ + @Named("CreatePrimaryZone") + @POST + @Path("/{fqdn}") + @SelectJson("data") + ListenableFuture create( + @PathParam("fqdn") @ParamParser(ToFQDN.class) @BinderParam(BindToJsonPayload.class) CreatePrimaryZone createZone); + + /** + * @see ZoneApi#createWithContact + */ + @Named("CreatePrimaryZone") + @POST + @Payload("%7B\"rname\":\"{contact}\",\"serial_style\":\"increment\",\"ttl\":3600%7D") + @Path("/{fqdn}") + @SelectJson("data") + ListenableFuture createWithContact(@PathParam("fqdn") String fqdn, @PayloadParam("contact") String contact); + + /** + * @see ZoneApi#delete + */ + @Named("DeleteZone") + @DELETE + @Path("/{fqdn}") + @Fallback(NullOnNotFoundOr404.class) + @Consumes(APPLICATION_JSON) + ListenableFuture delete(@PathParam("fqdn") String fqdn); + + /** + * @see ZoneApi#publish + */ + @Named("PublishZone") + @PUT + @Payload("{\"publish\":true}") + @Path("/{fqdn}") + @SelectJson("data") + ListenableFuture publish(@PathParam("fqdn") String fqdn); + + /** + * @see ZoneApi#freeze + */ + @Named("FreezeZone") + @PUT + @Path("/{fqdn}") + @Payload("{\"freeze\":true}") + @Consumes(APPLICATION_JSON) + ListenableFuture freeze(@PathParam("fqdn") String fqdn); + + /** + * @see ZoneApi#thaw + */ + @Named("ThawZone") + @PUT + @Path("/{fqdn}") + @Payload("{\"thaw\":true}") + @Consumes(APPLICATION_JSON) + ListenableFuture thaw(@PathParam("fqdn") String fqdn); } diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/handlers/GetJobRedirectionRetryHandler.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/handlers/GetJobRedirectionRetryHandler.java new file mode 100644 index 0000000000..2b0d99a50f --- /dev/null +++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/handlers/GetJobRedirectionRetryHandler.java @@ -0,0 +1,64 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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 org.jclouds.dynect.v3.handlers; + +import static com.google.common.net.HttpHeaders.LOCATION; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.handlers.BackoffLimitedRetryHandler; +import org.jclouds.http.handlers.RedirectionRetryHandler; +import org.jclouds.io.Payload; +import org.jclouds.io.Payloads; + +/** + * if the redirection URL is a Job, do not replay the original request; just get + * the job. + * + * @author Adrian Cole + */ +@Singleton +public class GetJobRedirectionRetryHandler extends RedirectionRetryHandler { + + private final Payload emptyPayload; + + @Inject + protected GetJobRedirectionRetryHandler(BackoffLimitedRetryHandler backoffHandler) { + super(backoffHandler); + this.emptyPayload = Payloads.newPayload(new byte[]{}); + this.emptyPayload.getContentMetadata().setContentType(APPLICATION_JSON); + } + + @Override + public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) { + String location = response.getFirstHeaderOrNull(LOCATION); + if (location != null && location.indexOf("Job") != -1) { + HttpRequest getRequest = command.getCurrentRequest().toBuilder() + .method("GET") + .payload(emptyPayload).build(); + command.setCurrentRequest(getRequest); + } + return super.shouldRetryRequest(command, response); + } +} diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/ZoneApiExpectTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/ZoneApiExpectTest.java index 420f3024eb..b1cdc9fdaf 100644 --- a/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/ZoneApiExpectTest.java +++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/ZoneApiExpectTest.java @@ -18,12 +18,15 @@ */ package org.jclouds.dynect.v3.features; +import static com.google.common.net.HttpHeaders.ACCEPT; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; import org.jclouds.dynect.v3.DynECTApi; +import org.jclouds.dynect.v3.domain.CreatePrimaryZone; import org.jclouds.dynect.v3.internal.BaseDynECTApiExpectTest; +import org.jclouds.dynect.v3.parse.DeleteZoneResponseTest; import org.jclouds.dynect.v3.parse.GetZoneResponseTest; import org.jclouds.dynect.v3.parse.ListZonesResponseTest; import org.jclouds.http.HttpRequest; @@ -35,7 +38,6 @@ import org.testng.annotations.Test; */ @Test(groups = "unit", testName = "ZoneApiExpectTest") public class ZoneApiExpectTest extends BaseDynECTApiExpectTest { - HttpRequest get = HttpRequest.builder().method("GET") .endpoint("https://api2.dynect.net/REST/Zone/jclouds.org") .addHeader("API-Version", "3.3.7") @@ -51,8 +53,30 @@ public class ZoneApiExpectTest extends BaseDynECTApiExpectTest { assertEquals(success.getZoneApi().get("jclouds.org").toString(), new GetZoneResponseTest().expected().toString()); } + + HttpRequest create = HttpRequest.builder().method("POST") + .endpoint("https://api2.dynect.net/REST/Zone/jclouds.org") + .addHeader("API-Version", "3.3.7") + .addHeader("Auth-Token", authToken) + .payload(stringPayload("{\"rname\":\"jimmy@jclouds.org\",\"serial_style\":\"increment\",\"ttl\":3600}")) + .build(); - public void testGetWhenResponseError2401() { + public void testCreateWhenResponseIs2xx() { + DynECTApi success = requestsSendResponses(createSession, createSessionResponse, create, getResponse); + assertEquals(success.getZoneApi().create(CreatePrimaryZone.builder() + .fqdn("jclouds.org") + .contact("jimmy@jclouds.org") + .build()).toString(), + new GetZoneResponseTest().expected().toString()); + } + + public void testCreateWithContactWhenResponseIs2xx() { + DynECTApi success = requestsSendResponses(createSession, createSessionResponse, create, getResponse); + assertEquals(success.getZoneApi().createWithContact("jclouds.org", "jimmy@jclouds.org").toString(), + new GetZoneResponseTest().expected().toString()); + } + + public void testGetWhenResponseIs404() { DynECTApi fail = requestsSendResponses(createSession, createSessionResponse, get, notFound); assertNull(fail.getZoneApi().get("jclouds.org")); } @@ -72,4 +96,67 @@ public class ZoneApiExpectTest extends BaseDynECTApiExpectTest { assertEquals(success.getZoneApi().list().toString(), new ListZonesResponseTest().expected().toString()); } + + HttpRequest delete = HttpRequest.builder().method("DELETE") + .endpoint("https://api2.dynect.net/REST/Zone/jclouds.org") + .addHeader("API-Version", "3.3.7") + .addHeader(ACCEPT, APPLICATION_JSON) + .addHeader("Auth-Token", authToken) + .payload(emptyJsonPayload()) + .build(); + + HttpResponse deleteResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType("/delete_zone.json", APPLICATION_JSON)).build(); + + public void testDeleteWhenResponseIs2xx() { + DynECTApi success = requestsSendResponses(createSession, createSessionResponse, delete, deleteResponse); + assertEquals(success.getZoneApi().delete("jclouds.org").toString(), + new DeleteZoneResponseTest().expected().toString()); + } + + public void testDeleteWhenResponseIs404() { + DynECTApi fail = requestsSendResponses(createSession, createSessionResponse, delete, notFound); + assertNull(fail.getZoneApi().delete("jclouds.org")); + } + + HttpRequest publish = HttpRequest.builder().method("PUT") + .endpoint("https://api2.dynect.net/REST/Zone/jclouds.org") + .addHeader("API-Version", "3.3.7") + .addHeader("Auth-Token", authToken) + .payload(stringPayload("{\"publish\":true}")) + .build(); + + public void testPublishWhenResponseIs2xx() { + DynECTApi success = requestsSendResponses(createSession, createSessionResponse, publish, getResponse); + assertEquals(success.getZoneApi().publish("jclouds.org").toString(), + new GetZoneResponseTest().expected().toString()); + } + + HttpRequest freeze = HttpRequest.builder().method("PUT") + .endpoint("https://api2.dynect.net/REST/Zone/jclouds.org") + .addHeader("API-Version", "3.3.7") + .addHeader(ACCEPT, APPLICATION_JSON) + .addHeader("Auth-Token", authToken) + .payload(stringPayload("{\"freeze\":true}")) + .build(); + + public void testFreezeWhenResponseIs2xx() { + DynECTApi success = requestsSendResponses(createSession, createSessionResponse, freeze, deleteResponse); + assertEquals(success.getZoneApi().freeze("jclouds.org").toString(), + new DeleteZoneResponseTest().expected().toString()); + } + + HttpRequest thaw = HttpRequest.builder().method("PUT") + .endpoint("https://api2.dynect.net/REST/Zone/jclouds.org") + .addHeader("API-Version", "3.3.7") + .addHeader(ACCEPT, APPLICATION_JSON) + .addHeader("Auth-Token", authToken) + .payload(stringPayload("{\"thaw\":true}")) + .build(); + + public void testThawWhenResponseIs2xx() { + DynECTApi success = requestsSendResponses(createSession, createSessionResponse, thaw, deleteResponse); + assertEquals(success.getZoneApi().thaw("jclouds.org").toString(), + new DeleteZoneResponseTest().expected().toString()); + } } diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/ZoneApiLiveTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/ZoneApiLiveTest.java index 1b24340cc9..c3b9c3f3c4 100644 --- a/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/ZoneApiLiveTest.java +++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/ZoneApiLiveTest.java @@ -20,10 +20,15 @@ package org.jclouds.dynect.v3.features; import static com.google.common.base.Preconditions.checkNotNull; import static java.util.logging.Logger.getAnonymousLogger; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; +import org.jclouds.JcloudsVersion; +import org.jclouds.dynect.v3.domain.Job; +import org.jclouds.dynect.v3.domain.Job.Status; import org.jclouds.dynect.v3.domain.Zone; import org.jclouds.dynect.v3.internal.BaseDynECTApiLiveTest; +import org.testng.annotations.AfterClass; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; @@ -35,7 +40,7 @@ import com.google.common.collect.ImmutableList; public class ZoneApiLiveTest extends BaseDynECTApiLiveTest { private void checkZone(Zone zone) { - checkNotNull(zone.getName(), "Name cannot be null for a Zone: %s", zone); + checkNotNull(zone.getFQDN(), "FQDN cannot be null for a Zone: %s", zone); checkNotNull(zone.getSerial(), "Serial cannot be null for a Zone: %s", zone); } @@ -44,19 +49,74 @@ public class ZoneApiLiveTest extends BaseDynECTApiLiveTest { ImmutableList zones = api().list().toList(); getAnonymousLogger().info("zones: " + zones.size()); - for (String zoneName : zones) { - Zone zone = api().get(zoneName); - checkNotNull(zone, "zone was null for Zone: %s", zoneName); + for (String fqdn : zones) { + Zone zone = api().get(fqdn); + checkNotNull(zone, "zone was null for Zone: %s", fqdn); checkZone(zone); } } @Test public void testGetZoneWhenNotFound() { - assertNull(api().get("AAAAAAAAAAAAAAAA")); + assertNull(api().get("AAAAAAAAAAAAAAAA.foo.com")); + } + + @Test + public void testDeleteZoneWhenNotFound() { + assertNull(api().delete("AAAAAAAAAAAAAAAA.foo.com")); + } + + String fqdn = System.getProperty("user.name").replace('.', '-') + ".zone.dynecttest.jclouds.org"; + String contact = JcloudsVersion.get() + "@jclouds.org"; + + @Test + public void testCreateZone() { + Zone zone = api().createWithContact(fqdn, contact); + checkNotNull(zone, "unable to create zone %s", fqdn); + getAnonymousLogger().info("created zone: " + zone); + checkZone(zone); + } + + @Test(dependsOnMethods = "testCreateZone") + public void testPublishZone() { + Zone zone = api().publish(fqdn); + checkNotNull(zone, "unable to publish zone %s", fqdn); + getAnonymousLogger().info("published zone: " + zone); + checkZone(zone); + } + + @Test(dependsOnMethods = "testPublishZone") + public void testFreezeZone() { + Job job = api().freeze(fqdn); + assertEquals(job.getStatus(), Status.SUCCESS); + assertEquals(context.getApi().getJob(job.getId()), job); + // TODO: determine how to prove it is frozen + } + + @Test(dependsOnMethods = "testFreezeZone") + public void testThawZone() { + Job job = api().thaw(fqdn); + assertEquals(job.getStatus(), Status.SUCCESS); + assertEquals(context.getApi().getJob(job.getId()), job); + // TODO: determine how to prove it is thawed + } + + @Test(dependsOnMethods = "testThawZone") + public void testDeleteZone() { + Job job = api().delete(fqdn); + assertEquals(job.getStatus(), Status.SUCCESS); + assertEquals(context.getApi().getJob(job.getId()), job); + assertNull(api().get(fqdn), "job " + job + " didn't delete zone" + fqdn); } protected ZoneApi api() { return context.getApi().getZoneApi(); } + + @Override + @AfterClass(groups = "live") + protected void tearDownContext() { + api().delete(fqdn); + super.tearDownContext(); + } } diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/handlers/GetJobRedirectionRetryHandlerExpectTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/handlers/GetJobRedirectionRetryHandlerExpectTest.java new file mode 100644 index 0000000000..17456bcd04 --- /dev/null +++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/handlers/GetJobRedirectionRetryHandlerExpectTest.java @@ -0,0 +1,70 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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 org.jclouds.dynect.v3.handlers; + +import static com.google.common.net.HttpHeaders.ACCEPT; +import static com.google.common.net.HttpHeaders.LOCATION; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +import org.jclouds.dynect.v3.DynECTApi; +import org.jclouds.dynect.v3.internal.BaseDynECTApiExpectTest; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.testng.annotations.Test; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "GetJobRedirectionRetryHandlerExpectTest") +public class GetJobRedirectionRetryHandlerExpectTest extends BaseDynECTApiExpectTest { + + public void testRedirectOnJobLocationSwitchesToGETAndNoPayload() { + + HttpRequest thaw = HttpRequest.builder().method("PUT") + .endpoint("https://api2.dynect.net/REST/Zone/jclouds.org") + .addHeader("API-Version", "3.3.7") + .addHeader(ACCEPT, APPLICATION_JSON) + .addHeader("Auth-Token", authToken) + .payload(stringPayload("{\"thaw\":true}")) + .build(); + + HttpResponse redirectResponse = HttpResponse.builder() + .statusCode(317) + .addHeader(LOCATION, "https://api2.dynect.net/REST/Job/1234") + .build(); + + HttpRequest job = HttpRequest.builder().method("GET") + .endpoint("https://api2.dynect.net/REST/Job/1234") + .addHeader("API-Version", "3.3.7") + .addHeader(ACCEPT, APPLICATION_JSON) + .addHeader("Auth-Token", authToken) + .payload(emptyJsonPayload()) + .build(); + + HttpResponse success = HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType("/delete_zone.json", APPLICATION_JSON)).build(); + + DynECTApi apiThatRedirects = requestsSendResponses(createSession, createSessionResponse, thaw, redirectResponse, + job, success); + + apiThatRedirects.getZoneApi().thaw("jclouds.org"); + + } +} diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTExpectTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTExpectTest.java index 1c50c99937..bafed021ee 100644 --- a/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTExpectTest.java +++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTExpectTest.java @@ -44,6 +44,12 @@ public class BaseDynECTExpectTest extends BaseRestApiExpectTest { return p; } + public static Payload stringPayload(String json) { + Payload p = Payloads.newPayload(json); + p.getContentMetadata().setContentType(APPLICATION_JSON); + return p; + } + @Override protected HttpRequestComparisonType compareHttpRequestAsType(HttpRequest input) { return HttpRequestComparisonType.JSON; diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/parse/DeleteZoneResponseTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/parse/DeleteZoneResponseTest.java new file mode 100644 index 0000000000..c4bd6eeb11 --- /dev/null +++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/parse/DeleteZoneResponseTest.java @@ -0,0 +1,45 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you 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 org.jclouds.dynect.v3.parse; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.dynect.v3.domain.Job; +import org.jclouds.dynect.v3.internal.BaseDynECTParseTest; +import org.testng.annotations.Test; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit") +public class DeleteZoneResponseTest extends BaseDynECTParseTest { + + @Override + public String resource() { + return "/delete_zone.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Job expected() { + return Job.success(262989027l); + } +} diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/parse/GetZoneResponseTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/parse/GetZoneResponseTest.java index ce2de769b6..83731f4854 100644 --- a/labs/dynect/src/test/java/org/jclouds/dynect/v3/parse/GetZoneResponseTest.java +++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/parse/GetZoneResponseTest.java @@ -44,6 +44,6 @@ public class GetZoneResponseTest extends BaseDynECTParseTest { @SelectJson("data") @Consumes(MediaType.APPLICATION_JSON) public Zone expected() { - return Zone.builder().type(PRIMARY).serialStyle(INCREMENT).serial(5).name("jclouds.org").build(); + return Zone.builder().type(PRIMARY).serialStyle(INCREMENT).serial(5).fqdn("jclouds.org").build(); } } \ No newline at end of file diff --git a/labs/dynect/src/test/resources/delete_zone.json b/labs/dynect/src/test/resources/delete_zone.json new file mode 100644 index 0000000000..aca65bef9c --- /dev/null +++ b/labs/dynect/src/test/resources/delete_zone.json @@ -0,0 +1 @@ +{"status": "success", "data": {}, "job_id": 262989027, "msgs": [{"INFO": "remove: Zone removed", "SOURCE": "BLL", "ERR_CD": null, "LVL": "INFO"}]} \ No newline at end of file