diff --git a/heroku-api-integration-tests/pom.xml b/heroku-api-integration-tests/pom.xml index 5c22f1e..6abc7a2 100644 --- a/heroku-api-integration-tests/pom.xml +++ b/heroku-api-integration-tests/pom.xml @@ -5,7 +5,7 @@ heroku-api-parent com.heroku.api - 0.17-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 @@ -66,11 +66,6 @@ heroku-json-jersey-client ${project.parent.version} - - com.heroku.api - heroku-http-play_2.9.1 - ${project.parent.version} - @@ -101,18 +96,24 @@ commons-io commons-io - 1.4 + 2.5 + test + + + commons-codec + commons-codec + 1.10 test - com.googlecode.jmockit + org.jmockit jmockit - 0.999.13 + 1.28 - 11.0.1 + 19.0 true @@ -145,7 +146,6 @@ src/test/resources/testng-asynchttpclient.xml src/test/resources/testng-httpclient-jackson.xml src/test/resources/testng-httpclient-gson.xml - src/test/resources/testng-play.xml src/test/resources/testng-jersey-client.xml diff --git a/heroku-api-integration-tests/src/test/java/com/heroku/api/PlayModule.java b/heroku-api-integration-tests/src/test/java/com/heroku/api/PlayModule.java deleted file mode 100644 index ddf6476..0000000 --- a/heroku-api-integration-tests/src/test/java/com/heroku/api/PlayModule.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.heroku.api; - -import com.google.inject.Provides; -import com.heroku.api.connection.PlayConnection; -import com.heroku.api.connection.PlayWSConnection; -import com.heroku.api.exception.RequestFailedException; - -import java.io.IOException; - -import static com.heroku.api.IntegrationTestConfig.CONFIG; - -public class PlayModule extends ConnectionTestModule { - - @Provides - PlayConnection getConnectionImpl() throws IOException { - - try { - IntegrationTestConfig.TestUser testUser = CONFIG.getDefaultUser(); - return PlayWSConnection.apply(); - } catch (RequestFailedException e) { - System.out.println(e.getMessage()); - System.out.println(e.getStatusCode()); - System.out.println(e.getResponseBody()); - System.out.flush(); - e.printStackTrace(); - throw e; - } - - } -} diff --git a/heroku-api-integration-tests/src/test/java/com/heroku/api/connection/ConnectionIntegrationTest.java b/heroku-api-integration-tests/src/test/java/com/heroku/api/connection/ConnectionIntegrationTest.java deleted file mode 100644 index 399517d..0000000 --- a/heroku-api-integration-tests/src/test/java/com/heroku/api/connection/ConnectionIntegrationTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.heroku.api.connection; - -import com.google.inject.Inject; -import com.heroku.api.HerokuAPI; -import com.heroku.api.IntegrationTestConfig; -import com.heroku.api.LoginVerification; -import com.heroku.api.TestModuleFactory; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.request.login.BasicAuthLogin; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; - -import java.io.IOException; - -import static com.heroku.api.IntegrationTestConfig.CONFIG; -import static org.testng.Assert.assertEquals; - -/** - * Integration tests for authenticating with the Heroku API. - * - * @author Naaman Newbold - */ -@Guice(moduleFactory = TestModuleFactory.class) -public class ConnectionIntegrationTest { - - @Inject - Connection connection; - - @Test(groups = "integration") - public void testObtainingAnApiKey() { - IntegrationTestConfig.TestUser testUser = CONFIG.getDefaultUser(); - String apiKey = HerokuAPI.obtainApiKey(testUser.getUsername(), testUser.getPassword()); - assertEquals(apiKey, testUser.getApiKey()); - } - - @Test(groups = "integration") - public void testValidUsernameAndPassword() throws IOException { - IntegrationTestConfig.TestUser testUser = CONFIG.getDefaultUser(); - LoginVerification verification = connection.execute(new BasicAuthLogin(testUser.getUsername(), testUser.getPassword()), testUser.getApiKey()); - assertEquals(verification.getEmail(), testUser.getUsername()); - } - - @DataProvider - public Object[][] invalidUsernamesAndPasswords() { - return new Object[][]{ - {null, null}, - {"", ""}, - {"rodneyMullen@powell.peralta.bones.brigade", "fakeUsernameAndPassword"} - }; - } - - @Test(groups = "integration", - dataProvider = "invalidUsernamesAndPasswords", - expectedExceptions = RequestFailedException.class) - public void testInvalidUsernameAndPassword(String username, String password) throws IOException { - connection.execute(new BasicAuthLogin(username, password), CONFIG.getDefaultUser().getApiKey()); - } -} diff --git a/heroku-api-integration-tests/src/test/java/com/heroku/api/finagle/FinagleConnectionTest.java b/heroku-api-integration-tests/src/test/java/com/heroku/api/finagle/FinagleConnectionTest.java index 496f2da..83fd916 100644 --- a/heroku-api-integration-tests/src/test/java/com/heroku/api/finagle/FinagleConnectionTest.java +++ b/heroku-api-integration-tests/src/test/java/com/heroku/api/finagle/FinagleConnectionTest.java @@ -6,6 +6,7 @@ import com.heroku.api.User; import com.heroku.api.connection.FinagleConnection; import com.heroku.api.request.user.UserInfo; +import com.twitter.util.Await; import com.twitter.util.Duration; import com.twitter.util.Future; import org.testng.annotations.Guice; @@ -28,7 +29,12 @@ public class FinagleConnectionTest { @SuppressWarnings("unchecked") public void asyncTests() { Future jsonArrayResponseFuture = connection.executeAsync(new UserInfo(), apiKey); - User user = (User) jsonArrayResponseFuture.get(Duration.fromTimeUnit(10L, TimeUnit.SECONDS)).get(); + User user = null; + try { + user = Await.result(jsonArrayResponseFuture, Duration.fromTimeUnit(10L, TimeUnit.SECONDS)); + } catch (Exception e) { + throw new RuntimeException(e); + } assertEquals(user.getEmail(), IntegrationTestConfig.CONFIG.getDefaultUser().getUsername()); } diff --git a/heroku-api-integration-tests/src/test/java/com/heroku/api/httpclient/HttpClientConnectionTest.java b/heroku-api-integration-tests/src/test/java/com/heroku/api/httpclient/HttpClientConnectionTest.java index db12067..0141f43 100644 --- a/heroku-api-integration-tests/src/test/java/com/heroku/api/httpclient/HttpClientConnectionTest.java +++ b/heroku-api-integration-tests/src/test/java/com/heroku/api/httpclient/HttpClientConnectionTest.java @@ -1,29 +1,12 @@ package com.heroku.api.httpclient; import com.heroku.api.App; -import com.heroku.api.Heroku; import com.heroku.api.IntegrationTestConfig; import com.heroku.api.connection.HttpClientConnection; -import com.heroku.api.http.Http; -import com.heroku.api.request.addon.AddonList; import com.heroku.api.request.app.AppList; -import mockit.Instantiation; -import mockit.Mock; -import mockit.MockClass; -import mockit.Mockit; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.HttpVersion; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.AbstractHttpClient; -import org.apache.http.message.BasicHttpResponse; -import org.apache.http.protocol.HttpContext; import org.testng.Assert; import org.testng.annotations.Test; -import java.io.IOException; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -43,66 +26,4 @@ public void asyncTests() throws ExecutionException, TimeoutException, Interrupte List jsonArrayResponse = jsonArrayResponseFuture.get(10L, TimeUnit.SECONDS); Assert.assertTrue(jsonArrayResponse != null); } - - @Test(singleThreaded = true) - public void userAgentShouldContainHerokuJarWithVersionNumber() { - Mockit.setUpMock(AbstractHttpClient.class, new MockAbstractHttpClient(new MockHooks() { - @Override - public void beforeAssertions(HttpUriRequest request, HttpContext context) { - final String expectedUserAgent = "heroku.jar/" + Heroku.JarProperties.getProperty("heroku.jar.version") + " httpclient"; - final String userAgentSent = Http.UserAgent.LATEST.getHeaderValue("httpclient"); - final String userAgentReceived = request.getHeaders(Http.UserAgent.LATEST.getHeaderName())[0].getValue(); - Assert.assertEquals(expectedUserAgent, userAgentSent); - Assert.assertEquals(expectedUserAgent, userAgentReceived); - } - })); - connection.execute(new AddonList(), apiKey); - Mockit.tearDownMocks(AbstractHttpClient.class); - } - - @Test(singleThreaded = true) - public void cookiesShouldBeIgnored() { - // make a call to ensure a Set-Cookie is sent back to the client - Mockit.setUpMock(AbstractHttpClient.class, new MockAbstractHttpClient(new MockHooks() { - @Override - public void responseHook(BasicHttpResponse response) { - response.setHeader("Set-Cookie", "foo=bar; path=/;"); - } - })); - connection.execute(new AddonList(), apiKey); - - Mockit.setUpMock(AbstractHttpClient.class, new MockAbstractHttpClient(new MockHooks() { - @Override - public void beforeAssertions(HttpUriRequest request, HttpContext context) { - Assert.assertEquals(request.getHeaders("Cookie").length, 0, "Cookies should be ignored, but there are cookies present."); - } - })); - // run this twice to ensure the set-cookie was sent from the first request - connection.execute(new AddonList(), apiKey); - Mockit.tearDownMocks(AbstractHttpClient.class); - } - - @MockClass(realClass = AbstractHttpClient.class, instantiation = Instantiation.PerMockSetup) - public static final class MockAbstractHttpClient { - private final MockHooks hooks; - - public MockAbstractHttpClient(MockHooks hooks) { - this.hooks = hooks; - } - - @Mock - public final HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException, ClientProtocolException { - hooks.beforeAssertions(request, context); - BasicHttpResponse basicHttpResponse = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - basicHttpResponse.setEntity(new StringEntity("[]")); - hooks.responseHook(basicHttpResponse); - return basicHttpResponse; - } - - } - - public static abstract class MockHooks { - public void beforeAssertions(HttpUriRequest request, HttpContext context) {} - public void responseHook(BasicHttpResponse response) {} - } } diff --git a/heroku-api-integration-tests/src/test/java/com/heroku/api/parser/JsonParseTest.java b/heroku-api-integration-tests/src/test/java/com/heroku/api/parser/JsonParseTest.java index 16fd972..95ed298 100644 --- a/heroku-api-integration-tests/src/test/java/com/heroku/api/parser/JsonParseTest.java +++ b/heroku-api-integration-tests/src/test/java/com/heroku/api/parser/JsonParseTest.java @@ -1,6 +1,7 @@ package com.heroku.api.parser; import com.heroku.api.App; +import com.heroku.api.Heroku; import com.heroku.api.exception.ParseException; import com.heroku.api.request.app.AppInfo; import org.testng.Assert; @@ -12,7 +13,9 @@ import java.util.List; import static com.heroku.api.parser.Json.parse; +import static com.heroku.api.parser.Json.encode; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; /** * @author mh @@ -30,25 +33,24 @@ Object[][] getParsers() { @Test(dataProvider = "getParsers") public void testParseAppWithDomainList(Parser parser) throws UnsupportedEncodingException { - String appListWithConfiguredDomain = "[{\"name\":\"app\",\"stack\":\"bamboo-ree-1.8.7\",\"slug_size\":2383872,\"requested_stack\":null,\"created_at\":\"2010/06/01 07:48:24 -0700\",\"web_url\":\"http://app.heroku.com/\",\"owner_email\":\"test@heroku.com\",\"create_status\":\"complete\",\"id\":200120," + - "\"domain_name\":{\"created_at\":\"2010/06/03 19:25:08 -0700\",\"updated_at\":\"2010/06/03 19:25:08 -0700\",\"default\":null,\"domain\":\"herokuapp.com\",\"id\":1234,\"app_id\":200120,\"base_domain\":\"herokuapp.com\"}," + - "\"repo_size\":630784,\"git_url\":\"git@heroku.com:dropphotos.git\",\"repo_migrate_status\":\"complete\",\"dynos\":1,\"workers\":0}" + + String appListWithConfiguredDomain = "[{\"name\":\"app\",\"stack\":{\"name\":\"cedar-14\"},\"slug_size\":2383872,\"requested_stack\":null,\"created_at\":\"2010/06/01 07:48:24 -0700\",\"web_url\":\"http://app.heroku.com/\",\"owner_email\":\"test@heroku.com\",\"create_status\":\"complete\",\"id\":200120," + + "\"domain_name\":{\"created_at\":\"2010/06/03 19:25:08 -0700\",\"updated_at\":\"2010/06/03 19:25:08 -0700\",\"status\":\"pending\",\"hostname\":\"herokuapp.com\",\"id\":1234}," + + "\"repo_size\":630784,\"git_url\":\"git@heroku.com:dropphotos.git\",\"maintenance\":\"false\"}" + "]"; final List appList = parser.parse(appListWithConfiguredDomain.getBytes("UTF-8"), APP_LIST_TYPE); Assert.assertEquals(appList.size(), 1); final App app = appList.get(0); Assert.assertEquals(app.getName(), "app"); - Assert.assertEquals(app.getDomain().getDomain(), "herokuapp.com"); - Assert.assertEquals(app.getDomain().getDefault(), null); - Assert.assertEquals(app.getDynos(), 1); + Assert.assertEquals(app.getDomain().getHostname(), "herokuapp.com"); + Assert.assertEquals(app.getDomain().getStatus(), "pending"); } @Test(dataProvider = "getParsers") public void testParseAppWithoutDomainList(Parser parser) throws UnsupportedEncodingException { - String appListWithConfiguredDomain = "[{\"name\":\"app\",\"stack\":\"bamboo-ree-1.8.7\",\"slug_size\":2383872,\"requested_stack\":null,\"created_at\":\"2010/06/01 07:48:24 -0700\",\"web_url\":\"http://app.heroku.com/\",\"owner_email\":\"test@heroku.com\",\"create_status\":\"complete\",\"id\":200120," + + String appListWithConfiguredDomain = "[{\"name\":\"app\",\"stack\":{\"name\":\"cedar-14\"},\"slug_size\":2383872,\"requested_stack\":null,\"created_at\":\"2010/06/01 07:48:24 -0700\",\"web_url\":\"http://app.heroku.com/\",\"owner_email\":\"test@heroku.com\",\"create_status\":\"complete\",\"id\":200120," + "\"domain_name\":null," + - "\"repo_size\":630784,\"git_url\":\"git@heroku.com:dropphotos.git\",\"repo_migrate_status\":\"complete\",\"dynos\":1,\"workers\":0}" + + "\"repo_size\":630784,\"git_url\":\"git@heroku.com:dropphotos.git\",\"maintenance\":\"false\"}" + "]"; final List appList = parser.parse(appListWithConfiguredDomain.getBytes("UTF-8"), APP_LIST_TYPE); @@ -56,7 +58,6 @@ public void testParseAppWithoutDomainList(Parser parser) throws UnsupportedEncod final App app = appList.get(0); Assert.assertEquals(app.getName(), "app"); Assert.assertEquals(app.getDomain(), null); - Assert.assertEquals(app.getDynos(), 1); } @Test(dataProvider = "getParsers") @@ -88,6 +89,14 @@ public void invalidJSONShouldThrowParseException() { parse("{{".getBytes(), AppInfo.class); } + @Test + public void testEncodeApp() { + App a = new App().on(Heroku.Stack.Cedar14); + a.setMaintenance(true); + String json = encode(a); + assertTrue(json.indexOf("true") != 0); + } + public static class SubAppInfo extends AppInfo { public SubAppInfo(String appName) { super(appName); diff --git a/heroku-api-integration-tests/src/test/java/com/heroku/api/play/PlayConnectionTest.java b/heroku-api-integration-tests/src/test/java/com/heroku/api/play/PlayConnectionTest.java deleted file mode 100644 index 65fd0ec..0000000 --- a/heroku-api-integration-tests/src/test/java/com/heroku/api/play/PlayConnectionTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.heroku.api.play; - -import com.google.inject.Inject; -import com.heroku.api.IntegrationTestConfig; -import com.heroku.api.Key; -import com.heroku.api.PlayModule; -import com.heroku.api.connection.PlayWSConnection; -import com.heroku.api.request.key.KeyList; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; -import play.api.libs.concurrent.Promise; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.testng.Assert.assertNotNull; - -@Guice(modules = PlayModule.class) -public class PlayConnectionTest { - - @Inject - PlayWSConnection connection; - - String apiKey = IntegrationTestConfig.CONFIG.getDefaultUser().getApiKey(); - - - @Test - @SuppressWarnings("unchecked") - public void asyncTests() { - Promise> jsonArrayResponseFuture = connection.executeAsync(new KeyList(), apiKey); - List keys = jsonArrayResponseFuture.await(10L, TimeUnit.SECONDS).get(); - assertNotNull(keys); - } - -} - diff --git a/heroku-api-integration-tests/src/test/java/com/heroku/api/request/BaseRequestIntegrationTest.java b/heroku-api-integration-tests/src/test/java/com/heroku/api/request/BaseRequestIntegrationTest.java index f23cc50..1359760 100644 --- a/heroku-api-integration-tests/src/test/java/com/heroku/api/request/BaseRequestIntegrationTest.java +++ b/heroku-api-integration-tests/src/test/java/com/heroku/api/request/BaseRequestIntegrationTest.java @@ -5,10 +5,10 @@ import com.heroku.api.connection.Connection; import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; -import com.heroku.api.request.app.AppClone; import com.heroku.api.request.app.AppCreate; import com.heroku.api.request.app.AppDestroy; -import com.heroku.api.request.config.ConfigAdd; +import com.heroku.api.request.config.ConfigUpdate; +import com.heroku.api.request.key.KeyRemove; import com.heroku.api.request.log.LogStreamResponse; import com.heroku.api.request.sharing.CollabList; import com.heroku.api.response.Unit; @@ -17,7 +17,9 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -51,13 +53,6 @@ public Object[][] newApp() { return new Object[][]{{createApp()}}; } - @DataProvider(parallel = true) - public Object[][] clonedApp() { - return new Object[][]{{ - connection.execute(new AppClone("template-java-spring-hibernate", new App()), apiKey) - }}; - } - public App getApp() { if (apps.size() > 0) return apps.get(0); @@ -67,11 +62,19 @@ public App getApp() { public App createApp() { System.out.println("Creating app..."); - App app = connection.execute(new AppCreate(new App().on(Heroku.Stack.Cedar)), apiKey); + App app = connection.execute(new AppCreate(new App().on(Heroku.Stack.Cedar14)), apiKey); apps.add(app); System.out.format("%s created\n", app.getName()); return app; } + + @BeforeSuite + public void deleteExistingKeys() throws InterruptedException { + for (IntegrationTestConfig.TestUser tu : IntegrationTestConfig.CONFIG.getTestUsers()) { + HerokuAPI api = new HerokuAPI(tu.getApiKey()); + deleteKeys(api.listKeys()); + } + } @BeforeSuite public void deleteExistingApps() throws InterruptedException { @@ -109,16 +112,45 @@ void deleteApps(List appsToDelete) throws InterruptedException { executorService.execute(new Runnable() { @Override public void run() { - System.out.format("Deleting %s\n", res.getName()); + System.out.format("Deleting app %s\n", res.getName()); deleteApp(res.getName()); - System.out.format("Deleted %s\n", res.getName()); + System.out.format("Deleted app %s\n", res.getName()); + } + }); + } + // await termination of all the threads to complete app deletion. + executorService.shutdown(); + executorService.awaitTermination(300L, TimeUnit.SECONDS); + System.out.format("Deleted apps in %dms\n", (System.currentTimeMillis() - start)); + } + + void deleteKeys(List keysToDelete) throws InterruptedException { + long start = System.currentTimeMillis(); + ExecutorService executorService = Executors.newFixedThreadPool(10); + for (final Key res : keysToDelete) { + executorService.execute(new Runnable() { + @Override + public void run() { + System.out.format("Deleting key %s\n", res.getComment()); + deleteKey(res.getId()); + System.out.format("Deleted key %s\n", res.getComment()); } }); } // await termination of all the threads to complete app deletion. executorService.shutdown(); executorService.awaitTermination(300L, TimeUnit.SECONDS); - System.out.format("Deleted apps in %dms", (System.currentTimeMillis() - start)); + System.out.format("Deleted keys in %dms\n", (System.currentTimeMillis() - start)); + } + + public void deleteKey(String sshkey) { + try { + connection.execute(new KeyRemove(sshkey), apiKey); + } catch (RequestFailedException e) { + if (e.getStatusCode() != Http.Status.FORBIDDEN.statusCode) { + throw e; + } + } } public void deleteApp(String appName) { @@ -136,20 +168,12 @@ protected void addConfig(App app, String... nameValuePairs) { throw new RuntimeException("Config must have an equal number of name and value pairs."); } - StringBuffer jsonConfig = new StringBuffer(); - jsonConfig = jsonConfig.append("{"); - String separator = ""; - + Map configVars = new HashMap(); for (int i = 0; i < nameValuePairs.length; i = i + 2) { - jsonConfig = jsonConfig.append( - String.format("%s\"%s\":\"%s\"", separator, nameValuePairs[i], nameValuePairs[i + 1]) - ); - separator = ","; + configVars.put(nameValuePairs[i], nameValuePairs[i + 1]); } - jsonConfig = jsonConfig.append("}"); - - Request req = new ConfigAdd(app.getName(), new String(jsonConfig)); + Request req = new ConfigUpdate(app.getName(), configVars); connection.execute(req, apiKey); } diff --git a/heroku-api-integration-tests/src/test/java/com/heroku/api/request/KeysRequestIntegrationTest.java b/heroku-api-integration-tests/src/test/java/com/heroku/api/request/KeysRequestIntegrationTest.java index cdb4c7b..67e28ac 100644 --- a/heroku-api-integration-tests/src/test/java/com/heroku/api/request/KeysRequestIntegrationTest.java +++ b/heroku-api-integration-tests/src/test/java/com/heroku/api/request/KeysRequestIntegrationTest.java @@ -26,7 +26,6 @@ import java.util.List; import java.util.UUID; -import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; /** @@ -46,7 +45,6 @@ public class KeysRequestIntegrationTest { @AfterClass(alwaysRun = true) public void deleteAllKeys() { -// connection.execute(new KeysRemoveAll(), API_KEY); // delete keys individually to avoid race conditions. // if a key is not found, ignore the failure because it's already been deleted. for (String k : pubKeyComments) { @@ -115,7 +113,7 @@ public boolean keyIsPresent(String comment) { public boolean keyIsPresent(String comment, List keyList) { for (Key k : keyList) { - if (k.getContents().contains(comment)) { + if (comment.equals(k.getComment())) { return true; } } diff --git a/heroku-api-integration-tests/src/test/java/com/heroku/api/request/RequestIntegrationTest.java b/heroku-api-integration-tests/src/test/java/com/heroku/api/request/RequestIntegrationTest.java index a734384..0d91ccf 100644 --- a/heroku-api-integration-tests/src/test/java/com/heroku/api/request/RequestIntegrationTest.java +++ b/heroku-api-integration-tests/src/test/java/com/heroku/api/request/RequestIntegrationTest.java @@ -5,7 +5,6 @@ import com.heroku.api.connection.HttpClientConnection; import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; import com.heroku.api.request.addon.AddonInstall; import com.heroku.api.request.addon.AddonList; import com.heroku.api.request.addon.AppAddonsList; @@ -14,26 +13,17 @@ import com.heroku.api.request.app.AppInfo; import com.heroku.api.request.app.AppList; import com.heroku.api.request.config.ConfigList; -import com.heroku.api.request.config.ConfigRemove; import com.heroku.api.request.domain.DomainAdd; import com.heroku.api.request.domain.DomainList; import com.heroku.api.request.domain.DomainRemove; import com.heroku.api.request.log.Log; -import com.heroku.api.request.log.LogStreamResponse; -import com.heroku.api.request.ps.ProcessList; -import com.heroku.api.request.ps.Restart; -import com.heroku.api.request.ps.Scale; -import com.heroku.api.request.ps.Stop; import com.heroku.api.request.releases.ListReleases; import com.heroku.api.request.releases.ReleaseInfo; import com.heroku.api.request.releases.Rollback; -import com.heroku.api.request.run.Run; -import com.heroku.api.request.run.RunResponse; import com.heroku.api.request.sharing.CollabList; import com.heroku.api.request.sharing.SharingAdd; import com.heroku.api.request.sharing.SharingRemove; import com.heroku.api.request.stack.StackList; -import com.heroku.api.request.stack.StackMigrate; import com.heroku.api.request.user.UserInfo; import com.heroku.api.response.Unit; import org.testng.ITestResult; @@ -46,7 +36,7 @@ import java.util.List; import java.util.Map; -import static com.heroku.api.Heroku.Stack.Cedar; +import static com.heroku.api.Heroku.Stack.Cedar14; import static com.heroku.api.IntegrationTestConfig.CONFIG; import static com.heroku.api.http.Http.Status.*; import static org.testng.Assert.*; @@ -80,62 +70,16 @@ public void testAppExistsCommand(App app) throws IOException { @Test(retryAnalyzer = InternalServerErrorAnalyzer.class) public void testCreateAppCommand() throws IOException { - AppCreate cmd = new AppCreate(new App().on(Cedar)); + AppCreate cmd = new AppCreate(new App().on(Cedar14)); App response = connection.execute(cmd, apiKey); assertNotNull(response.getId()); - assertEquals(response.getStack(), Cedar); - assertEquals(response.getCreateStatus(), "complete"); + assertEquals(response.getStack(), Cedar14); + assertFalse(response.isMaintenance()); assertNull(response.getBuildpackProvidedDescription()); deleteApp(response.getName()); } - @Test(retryAnalyzer = GeneralRetryAnalyzer.class) - public void testCloneAppCommand() throws IOException { - final HerokuAPI api = new HerokuAPI(connection, apiKey); - final String templateName = "template-java-spring-hibernate"; - - App response = api.cloneApp(templateName); - - assertNotNull(response.getId()); - assertNotSame(templateName, response.getName()); - assertEquals(response.getStack(), Cedar); - assertEquals(response.getCreateStatus(), "complete"); - assertEquals(response.getBuildpackProvidedDescription(), "Java"); - deleteApp(response.getName()); - } - - @Test(retryAnalyzer = GeneralRetryAnalyzer.class) - public void testCloneAppCommand_WithRequestedName() throws IOException { - final HerokuAPI api = new HerokuAPI(connection, apiKey); - final String templateName = "template-java-spring-hibernate"; - final String requestedAppName = "test" + System.currentTimeMillis(); - - App response = api.cloneApp(templateName, new App().named(requestedAppName)); - - assertNotNull(response.getId()); - assertNotSame(templateName, response.getName()); - assertEquals(response.getName(), requestedAppName); - assertEquals(response.getStack(), Cedar); - assertEquals(response.getCreateStatus(), "complete"); - assertEquals(response.getBuildpackProvidedDescription(), "Java"); - deleteApp(response.getName()); - } - - @Test(retryAnalyzer = InternalServerErrorAnalyzer.class) - public void testCloneAppCommand_WithNonTemplateApp() throws IOException { - final HerokuAPI api = new HerokuAPI(connection, apiKey); - final String nonTemplateApp = "java"; - - try { - api.cloneApp(nonTemplateApp); - fail(); - } catch (RequestFailedException e) { - assertTrue(e.getMessage().contains("Failed to clone app")); - assertEquals(e.getStatusCode(), FORBIDDEN.statusCode); - } - } - @DataProvider public Object[][] logParameters() { final String appName = getApp().getName(); @@ -170,7 +114,7 @@ public void testListAppsCommand(App app) throws IOException { // don't use the app dataprovider because it'll try to delete an already deleted app @Test(retryAnalyzer = InternalServerErrorAnalyzer.class) public void testDestroyAppCommand() throws IOException { - AppDestroy cmd = new AppDestroy(new HerokuAPI(connection, apiKey).createApp(new App().on(Cedar)).getName()); + AppDestroy cmd = new AppDestroy(new HerokuAPI(connection, apiKey).createApp(new App().on(Cedar14)).getName()); Unit response = connection.execute(cmd, apiKey); assertNotNull(response); } @@ -193,14 +137,16 @@ public void testSharingAddWithBrandNewUserCommand(App app) throws IOException { public void testSharingTransferCommand() throws IOException { assertNotSame(IntegrationTestConfig.CONFIG.getDefaultUser().getUsername(), sharingUser.getUsername()); HerokuAPI api = new HerokuAPI(IntegrationTestConfig.CONFIG.getDefaultUser().getApiKey()); - App app = api.createApp(new App().on(Cedar)); + App app = api.createApp(new App().on(Cedar14)); api.addCollaborator(app.getName(), sharingUser.getUsername()); api.transferApp(app.getName(), sharingUser.getUsername()); HerokuAPI sharedUserAPI = new HerokuAPI(sharingUser.getApiKey()); App transferredApp = sharedUserAPI.getApp(app.getName()); - assertEquals(transferredApp.getOwnerEmail(), sharingUser.getUsername()); - sharedUserAPI.destroyApp(transferredApp.getName()); + + // TODO The transfer must be accepted first + //assertEquals(transferredApp.getOwnerEmail(), sharingUser.getUsername()); + //sharedUserAPI.destroyApp(transferredApp.getName()); } @Test(dataProvider = "newApp", invocationCount = 5, successPercentage = 20, retryAnalyzer = InternalServerErrorAnalyzer.class) @@ -225,7 +171,7 @@ public void testConfigAddCommand() throws IOException { config.put("FOO", "bar"); config.put("BAR", "foo"); config.put("회사", "히로쿠"); - api.addConfig(app.getName(), config); + api.updateConfig(app.getName(), config); Map retrievedConfig = api.listConfig(app.getName()); assertEquals(retrievedConfig.get("FOO"), "bar"); assertEquals(retrievedConfig.get("BAR"), "foo"); @@ -241,47 +187,6 @@ public void testConfigCommand(App app) { assertEquals(response.get("FOO"), "BAR"); } - @Test(dataProvider = "newApp", retryAnalyzer = InternalServerErrorAnalyzer.class) - public void testConfigRemoveCommand(App app) { - addConfig(app, "회사", "히로쿠", "JOHN", "DOE"); - Request> removeRequest = new ConfigRemove(app.getName(), "회사"); - Map response = connection.execute(removeRequest, apiKey); - assertNotNull(response.get("JOHN"), "Config var 'JOHN' should still exist, but it's not there."); - assertNull(response.get("회사")); - } - - @Test(dataProvider = "app", retryAnalyzer = InternalServerErrorAnalyzer.class) - public void testProcessCommand(App app) { - Proc proc = null; - try { - final RunResponse runResponse = connection.execute(new Run(app.getName(), "sleep 60", false), apiKey); - proc = runResponse.getProc(); - - List response = connection.execute(new ProcessList(app.getName()), apiKey); - assertEquals(response.size(), 1); - assertEquals(response.get(0).getProcess(), proc.getProcess()); - assertEquals(response.get(0).getCommand(), proc.getCommand()); - } finally { - if (proc != null) { - connection.execute(new Stop(app.getName(), proc), apiKey); - } - } - } - - @Test(dataProvider = "clonedApp", retryAnalyzer = InternalServerErrorAnalyzer.class) - public void testScaleCommand(App app) { - Request req = new Scale(app.getName(), "web", 1); - Unit response = connection.execute(req, apiKey); - assertNotNull(response); - } - - @Test(dataProvider = "app", retryAnalyzer = InternalServerErrorAnalyzer.class) - public void testRestartCommand(App app) { - Request req = new Restart(app.getName()); - Unit response = connection.execute(req, apiKey); - assertNotNull(response); - } - @Test(retryAnalyzer = GeneralRetryAnalyzer.class) public void testListAddons() { AddonList req = new AddonList(); @@ -291,7 +196,7 @@ public void testListAddons() { @Test(dataProvider = "newApp", retryAnalyzer = GeneralRetryAnalyzer.class) public void testListAppAddons(App app) { - connection.execute(new AddonInstall(app.getName(), "heroku-postgresql:dev"), apiKey); + connection.execute(new AddonInstall(app.getName(), "heroku-postgresql"), apiKey); Request> req = new AppAddonsList(app.getName()); List response = connection.execute(req, apiKey); assertNotNull(response); @@ -301,9 +206,9 @@ public void testListAppAddons(App app) { @Test(dataProvider = "app", retryAnalyzer = GeneralRetryAnalyzer.class) public void testAddAddonToApp(App app) { - AddonInstall req = new AddonInstall(app.getName(), "heroku-postgresql:dev"); + AddonInstall req = new AddonInstall(app.getName(), "heroku-postgresql"); AddonChange response = connection.execute(req, apiKey); - assertEquals(response.getStatus(), "Installed"); + assertEquals(response.getState(), "provisioned"); } @Test(dataProvider = "newApp", retryAnalyzer = InternalServerErrorAnalyzer.class) @@ -314,23 +219,6 @@ public void testCollaboratorList(App app) { assertNotNull(xmlArrayResponse.get(0).getEmail()); } - @Test(dataProvider = "app", retryAnalyzer = InternalServerErrorAnalyzer.class) - public void testRunCommand(App app) throws IOException { - Run run = new Run(app.getName(), "echo helloworld"); - Run runAttached = new Run(app.getName(), "echo helloworld", true); - RunResponse response = connection.execute(run, apiKey); - try { - response.attach(); - fail("Should throw an illegal state exception"); - } catch (IllegalStateException ex) { - //ok - } - RunResponse responseAttach = connection.execute(runAttached, apiKey); - String output = HttpUtil.getUTF8String(HttpUtil.getBytes(responseAttach.attach())); - System.out.println("RUN OUTPUT:" + output); - assertTrue(output.contains("helloworld")); - } - @Test(retryAnalyzer = InternalServerErrorAnalyzer.class) public void testUserInfo() { IntegrationTestConfig.TestUser testUser = CONFIG.getDefaultUser(); @@ -343,7 +231,7 @@ public void testUserInfo() { @Test(dataProvider = "newApp", retryAnalyzer = InternalServerErrorAnalyzer.class) public void testListReleases(App app) { List releases = connection.execute(new ListReleases(app.getName()), apiKey); - assertEquals(releases.get(0).getName(), "v1"); + assertEquals(releases.get(0).getVersion(), 1); assertEquals(releases.get(0).getDescription(), "Initial release"); } @@ -351,17 +239,19 @@ public void testListReleases(App app) { public void testReleaseInfo(App app) { addConfig(app, "releaseTest", "releaseTest"); //ensure a release exists List releases = connection.execute(new ListReleases(app.getName()), apiKey); - Release releaseInfo = connection.execute(new ReleaseInfo(app.getName(), releases.get(0).getName()), apiKey); - assertEquals(releaseInfo.getName(), releases.get(0).getName()); + Release releaseInfo = connection.execute(new ReleaseInfo(app.getName(), releases.get(0).getId()), apiKey); + assertEquals(releaseInfo.getVersion(), releases.get(0).getVersion()); + assertEquals(releaseInfo.getId(), releases.get(0).getId()); } @Test(dataProvider = "newApp", retryAnalyzer = InternalServerErrorAnalyzer.class) public void testRollback(App app) { addConfig(app, "releaseTest", "releaseTest"); List releases = connection.execute(new ListReleases(app.getName()), apiKey); - Release lastRelease = releases.get(releases.size() - 1); - String rollback = connection.execute(new Rollback(app.getName(), lastRelease.getName()), apiKey); - assertEquals(rollback, lastRelease.getName()); + Release previousRelease = releases.get(releases.size() - 2); + Release rollback = connection.execute(new Rollback(app.getName(), previousRelease.getId()), apiKey); + assertNotSame(rollback.getId(), previousRelease.getId()); + assertNotSame(rollback.getVersion(), previousRelease.getVersion()); } @Test(dataProvider = "app", retryAnalyzer = InternalServerErrorAnalyzer.class) @@ -375,19 +265,12 @@ public void testStackList(App app) { } fail("Stack list did not contain the app's stack."); } - - @Test(retryAnalyzer = InternalServerErrorAnalyzer.class) - public void testStackMigrate() { - App app = connection.execute(new AppCreate(new App().on(Heroku.Stack.Bamboo187)), apiKey); - String migrateStatus = connection.execute(new StackMigrate(app.getName(), Heroku.Stack.Bamboo192), apiKey); - assertTrue(migrateStatus.contains("Migration prepared")); - } @Test(dataProvider = "app", retryAnalyzer = InternalServerErrorAnalyzer.class) public void testAddDomain(App app) { String domainName = randomDomain(); Domain addedDomain = connection.execute(new DomainAdd(app.getName(), domainName), apiKey); - assertEquals(addedDomain.getDomain(), domainName); + assertEquals(addedDomain.getHostname(), domainName); } @Test(dataProvider = "app", retryAnalyzer = InternalServerErrorAnalyzer.class) @@ -417,7 +300,7 @@ public void testMaintenanceMode(App app) { private void assertDomainIsPresent(App app, String domainName) { for (Domain d : connection.execute(new DomainList(app.getName()), apiKey)) { - if (d.getDomain().equalsIgnoreCase(domainName)) { + if (d.getHostname().equalsIgnoreCase(domainName)) { return; } } @@ -428,7 +311,7 @@ private void assertDomainIsPresent(App app, String domainName) { private void assertDomainNotPresent(App app, String domainName) { for (Domain d : connection.execute(new DomainList(app.getName()), apiKey)) { - if (d.getDomain().equalsIgnoreCase(domainName)) { + if (d.getHostname().equalsIgnoreCase(domainName)) { throw new AssertionError( "Domain " + domainName + " should not be present, but it was found for app " + app.getName() ); diff --git a/heroku-api-integration-tests/src/test/java/com/heroku/api/request/ps/Stop.java b/heroku-api-integration-tests/src/test/java/com/heroku/api/request/ps/Stop.java deleted file mode 100644 index c3bfb19..0000000 --- a/heroku-api-integration-tests/src/test/java/com/heroku/api/request/ps/Stop.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.heroku.api.request.ps; - -import com.heroku.api.Heroku; -import com.heroku.api.Proc; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; -import com.heroku.api.request.Request; -import com.heroku.api.request.RequestConfig; -import com.heroku.api.response.Unit; - -import java.util.Map; - -/** - * @author Ryan Brainard - */ -// TODO: Currently in test module until fully supported -public class Stop implements Request { - - private final RequestConfig config; - - public Stop(String appName, Proc process) { - config = new RequestConfig().app(appName).with(Heroku.RequestKey.ProcessName, process.getProcess()); - } - - public Stop(String appName, String processType) { - config = new RequestConfig().app(appName).with(Heroku.RequestKey.ProcessType, processType); - } - - @Override - public Http.Method getHttpMethod() { - return Http.Method.POST; - } - - @Override - public String getEndpoint() { - return Heroku.Resource.Stop.format(config.get(Heroku.RequestKey.AppName)); - } - - @Override - public boolean hasBody() { - return true; - } - - @Override - public String getBody() { - return HttpUtil.encodeParameters(config, Heroku.RequestKey.ProcessName, Heroku.RequestKey.ProcessType); - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); - } - - @Override - public Unit getResponse(byte[] bytes, int status) { - if (status == Http.Status.OK.statusCode) { - return Unit.unit; - } else if (status == Http.Status.FORBIDDEN.statusCode) { - throw HttpUtil.insufficientPrivileges(status, bytes); - } else { - throw new RequestFailedException("Error occurred while scaling.", status, bytes); - } - } -} diff --git a/heroku-api/pom.xml b/heroku-api/pom.xml index f0651e6..7a460f4 100644 --- a/heroku-api/pom.xml +++ b/heroku-api/pom.xml @@ -5,7 +5,7 @@ heroku-api-parent com.heroku.api - 0.17-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 diff --git a/heroku-api/src/main/java/com/heroku/api/AddonChange.java b/heroku-api/src/main/java/com/heroku/api/AddonChange.java index 2ee9b88..26f6d9e 100644 --- a/heroku-api/src/main/java/com/heroku/api/AddonChange.java +++ b/heroku-api/src/main/java/com/heroku/api/AddonChange.java @@ -11,32 +11,13 @@ public class AddonChange implements Serializable { private static final long serialVersionUID = 1L; - String status; - String message; - String price; + String state; - public String getStatus() { - return status; + public String getState() { + return state; } - private void setStatus(String status) { - this.status = status; + private void setState(String state) { + this.state = state; } - - public String getMessage() { - return message; - } - - private void setMessage(String message) { - this.message = message; - } - - public String getPrice() { - return price; - } - - private void setPrice(String price) { - this.price = price; - } - } diff --git a/heroku-api/src/main/java/com/heroku/api/App.java b/heroku-api/src/main/java/com/heroku/api/App.java index af1a5cb..5e351ea 100644 --- a/heroku-api/src/main/java/com/heroku/api/App.java +++ b/heroku-api/src/main/java/com/heroku/api/App.java @@ -15,19 +15,16 @@ public class App implements Serializable { String name; Domain domain_name; String created_at; - String create_status; - String owner_email; + App.Owner owner; String web_url; - String stack; + App.Stack stack; String requested_stack; - String repo_migrate_status; String git_url; String buildpack_provided_description; String released_at; int slug_size; - int repo_size; - int dynos; - int workers; + int repo_size; + boolean maintenance; /** * Builder method for specifying the name of an app. @@ -47,10 +44,18 @@ public App named(String name) { */ public App on(Heroku.Stack stack) { App newApp = copy(); - newApp.stack = stack.toString(); + newApp.stack = new App.Stack(stack); return newApp; } + public boolean isMaintenance() { + return maintenance; + } + + public void setMaintenance(boolean maintenance) { + this.maintenance = maintenance; + } + private void setweb_url(String webUrl) { web_url = webUrl; } @@ -71,15 +76,11 @@ private void setCreated_at(String created_at) { this.created_at = created_at; } - private void setCreate_status(String create_status) { - this.create_status = create_status; - } - - private void setOwner_email(String owner_email) { - this.owner_email = owner_email; + private void setOwner(App.Owner owner) { + this.owner = owner; } - private void setStack(String stack) { + private void setStack(App.Stack stack) { this.stack = stack; } @@ -87,10 +88,6 @@ private void setRequested_stack(String requested_stack) { this.requested_stack = requested_stack; } - private void setRepo_migrate_status(String repo_migrate_status) { - this.repo_migrate_status = repo_migrate_status; - } - private void setGit_url(String git_url) { this.git_url = git_url; } @@ -107,14 +104,6 @@ private void setRepo_size(int repo_size) { this.repo_size = repo_size; } - private void setDynos(int dynos) { - this.dynos = dynos; - } - - private void setWorkers(int workers) { - this.workers = workers; - } - public String getId() { return id; } @@ -146,26 +135,18 @@ public String getBuildpackProvidedDescription() { return buildpack_provided_description; } - public String getCreateStatus() { - return create_status; - } - public String getCreatedAt() { return created_at; } public Heroku.Stack getStack() { - return Heroku.Stack.fromString(stack); + return Heroku.Stack.fromString(stack.getName()); } public String getRequestedStack() { return requested_stack; } - public String getRepoMigrateStatus() { - return repo_migrate_status; - } - public int getSlugSize() { return slug_size; } @@ -174,16 +155,8 @@ public int getRepoSize() { return repo_size; } - public int getDynos() { - return dynos; - } - - public int getWorkers() { - return workers; - } - - public String getOwnerEmail() { - return owner_email; + public App.Owner getOwner() { + return owner; } public String getReleasedAt(){ @@ -200,5 +173,55 @@ private App copy() { copy.stack = this.stack; return copy; } - + + public static class Owner implements Serializable { + + private static final long serialVersionUID = 1L; + + String email; + + public Owner() { + + } + + public void setEmail(String email) { + this.email = email; + } + + public String getEmail() { + return email; + } + } + + public static class Stack implements Serializable { + + private static final long serialVersionUID = 1L; + + String id; + String name; + + public Stack() { + + } + + public Stack(Heroku.Stack herokuStack) { + this.name = herokuStack.name(); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } } diff --git a/heroku-api/src/main/java/com/heroku/api/Collaborator.java b/heroku-api/src/main/java/com/heroku/api/Collaborator.java index 9694795..3c844b3 100644 --- a/heroku-api/src/main/java/com/heroku/api/Collaborator.java +++ b/heroku-api/src/main/java/com/heroku/api/Collaborator.java @@ -1,6 +1,8 @@ package com.heroku.api; import java.io.Serializable; +import java.util.List; +import java.util.Map; /** * TODO: Javadoc @@ -11,22 +13,35 @@ public class Collaborator implements Serializable { private static final long serialVersionUID = 1L; - String access; - String email; + List> permissions; + Map user; + String role; - public String getAccess() { - return access; + public List> getPermissions() { + return permissions; } - private void setAccess(String access) { - this.access = access; + public void setPermissions(List> permissions) { + this.permissions = permissions; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; } public String getEmail() { - return email; + return user.get("email"); + } + + public Map getUser() { + return user; } - private void setEmail(String email) { - this.email = email; + public void setUser(Map user) { + this.user = user; } } diff --git a/heroku-api/src/main/java/com/heroku/api/Domain.java b/heroku-api/src/main/java/com/heroku/api/Domain.java index 83bed09..6ebdd5d 100644 --- a/heroku-api/src/main/java/com/heroku/api/Domain.java +++ b/heroku-api/src/main/java/com/heroku/api/Domain.java @@ -16,11 +16,11 @@ public class Domain implements Serializable { String created_at; String updated_at; - String default_name; - String domain; + String hostname; String id; - String app_id; - String base_domain; + String kind; + String status; + String cname; public String getCreatedAt() { return created_at; @@ -30,24 +30,16 @@ public String getUpdatedAt() { return updated_at; } - public String getDefault() { - return default_name; - } - - public String getDomain() { - return domain; + public String getHostname() { + return hostname; } public String getId() { return id; } - public String getAppId() { - return app_id; - } - - public String getBaseDomain() { - return base_domain; + public String getStatus() { + return status; } private void setCreated_at(String created_at) { @@ -58,23 +50,27 @@ private void setUpdated_at(String updated_at) { this.updated_at = updated_at; } - private void setDefault(String default_name) { - this.default_name = default_name; - } - - private void setDomain(String domain) { - this.domain = domain; + private void setHostname(String domain) { + this.hostname = domain; } private void setId(String id) { this.id = id; } - private void setApp_id(String app_id) { - this.app_id = app_id; + private void setKind(String kind) { + this.kind = kind; + } + + private void setStatus(String status) { + this.status = status; + } + + public String getCname() { + return cname; } - private void setBase_domain(String base_domain) { - this.base_domain = base_domain; + public void setCname(String cname) { + this.cname = cname; } } diff --git a/heroku-api/src/main/java/com/heroku/api/Heroku.java b/heroku-api/src/main/java/com/heroku/api/Heroku.java index c434569..123b9da 100644 --- a/heroku-api/src/main/java/com/heroku/api/Heroku.java +++ b/heroku-api/src/main/java/com/heroku/api/Heroku.java @@ -14,6 +14,7 @@ import java.util.Map; import java.util.Properties; + public class Heroku { @@ -145,21 +146,24 @@ public static ResponseKey fromString(String keyName) { } public static enum RequestKey { - Stack("app[stack]"), - CreateAppName("app[name]"), + StackName("name"), + Stack("stack"), + AppMaintenance("maintenance"), Remote("remote"), Timeout("timeout"), Addons("addons"), AddonName("addon"), + AddonPlan("plan"), Attach("attach"), Requested("requested"), Beta("beta"), AppName("name"), - SSHKey("sshkey"), + SSHKey("public_key"), Config("config"), Command("command"), - Collaborator("collaborator[email]"), - TransferOwner("app[transfer_owner]"), + Collaborator("user"), + TransferAppName("app"), + TransferOwner("recipient"), ConfigVars("config_vars"), ConfigVarName("key"), ProcessType("type"), @@ -172,10 +176,8 @@ public static enum RequestKey { LogSource("source"), LogTail("tail"), Release("release"), - Rollback("rollback"), - CreateDomain("domain_name[domain]"), - DeleteDomain("domain_name"), - MaintenanceMode("maintenance_mode"); + CreateDomain("hostname"), + DeleteDomain("hostname"); public final String queryParameter; @@ -186,11 +188,8 @@ public static enum RequestKey { public static enum Stack { - Aspen("aspen-mri-1.8.6"), - Bamboo192("bamboo-mri-1.9.2"), - Bamboo187("bamboo-ree-1.8.7"), - Cedar("cedar"), - Cedar14("cedar-14"); + Cedar14("cedar-14"), + Cedar16("cedar-16"); public final String value; @@ -217,20 +216,18 @@ public static Stack fromString(String stackName) { public static enum Resource { - Login("/login"), Apps("/apps"), App("/apps/%s"), - AppClone(App.value + "/clone"), + AppTransfer("/account/app-transfers"), Addons("/addons"), AppAddons(App.value + "/addons"), AppAddon(AppAddons.value + "/%s"), - User("/user"), + User("/account"), Key(User.value + "/keys/%s"), Keys(User.value + "/keys"), Collaborators(App.value + "/collaborators"), Collaborator(Collaborators.value + "/%s"), - ConfigVars(App.value + "/config_vars"), - ConfigVar(ConfigVars.value + "/%s"), + ConfigVars(App.value + "/config-vars"), Logs(App.value + "/logs?%s"), Process(App.value + "/ps"), Restart(Process.value + "/restart"), @@ -239,10 +236,9 @@ public static enum Resource { Releases(App.value + "/releases"), Release(Releases.value + "/%s"), Status(App.value + "/status"), - AppStack(App.value + "/stack"), + Stacks("/stacks"), Domains(App.value + "/domains"), - Domain(Domains.value + "/%s"), - Maintenance(App.value + "/server/maintenance"); + Domain(Domains.value + "/%s"); public final String value; @@ -255,11 +251,11 @@ public String format(String... values) { } } - public static enum ApiVersion implements Http.Header { + public enum ApiVersion implements Http.Header { v2(2), v3(3); - public static final String HEADER = "X-Heroku-API-Version"; + public static final String HEADER = "Accept"; public final int version; @@ -274,7 +270,7 @@ public String getHeaderName() { @Override public String getHeaderValue() { - return Integer.toString(version); + return "application/vnd.heroku+json; version=" + Integer.toString(version); } } diff --git a/heroku-api/src/main/java/com/heroku/api/HerokuAPI.java b/heroku-api/src/main/java/com/heroku/api/HerokuAPI.java index 38a4019..802b373 100644 --- a/heroku-api/src/main/java/com/heroku/api/HerokuAPI.java +++ b/heroku-api/src/main/java/com/heroku/api/HerokuAPI.java @@ -8,31 +8,21 @@ import com.heroku.api.request.addon.AddonRemove; import com.heroku.api.request.addon.AppAddonsList; import com.heroku.api.request.app.*; -import com.heroku.api.request.config.ConfigAdd; +import com.heroku.api.request.config.ConfigUpdate; import com.heroku.api.request.config.ConfigList; -import com.heroku.api.request.config.ConfigRemove; import com.heroku.api.request.key.KeyAdd; import com.heroku.api.request.key.KeyList; import com.heroku.api.request.key.KeyRemove; import com.heroku.api.request.log.Log; import com.heroku.api.request.log.LogStreamResponse; -import com.heroku.api.request.login.BasicAuthLogin; -import com.heroku.api.request.maintenance.MaintenanceInfo; -import com.heroku.api.request.maintenance.MaintenanceUpdate; -import com.heroku.api.request.ps.ProcessList; -import com.heroku.api.request.ps.Restart; -import com.heroku.api.request.ps.Scale; import com.heroku.api.request.releases.ListReleases; import com.heroku.api.request.releases.ReleaseInfo; import com.heroku.api.request.releases.Rollback; -import com.heroku.api.request.run.Run; -import com.heroku.api.request.run.RunResponse; import com.heroku.api.request.sharing.CollabList; import com.heroku.api.request.sharing.SharingAdd; import com.heroku.api.request.sharing.SharingRemove; import com.heroku.api.request.sharing.SharingTransfer; import com.heroku.api.request.stack.StackList; -import com.heroku.api.request.stack.StackMigrate; import com.heroku.api.request.user.UserInfo; import java.util.List; @@ -59,18 +49,6 @@ public class HerokuAPI { protected final Connection connection; protected final String apiKey; - /** - * Logs into the Heroku API and retrieves an API key for a given username and password using HTTP Basic Authentication. - * @param username Heroku username. - * @param password Heroku password. - * @return An API key that can be used for subsequent API calls. - */ - public static String obtainApiKey(String username, String password) { - Connection tmpConn = ConnectionFactory.get(); - LoginVerification verification = tmpConn.execute(new BasicAuthLogin(username, password), null); - return verification.getApiKey(); - } - /** * Constructs a HerokuAPI with a {@link Connection} based on the first {@link com.heroku.api.connection.ConnectionProvider} * found on the classpath. @@ -166,12 +144,12 @@ public boolean isAppNameAvailable(String name) { } /** - * Create a new app on the {@link Heroku.Stack.Cedar} stack. For more information about the Cedar stack, please see + * Create a new app on the {Heroku.Stack.Cedar14} stack. For more information about the Cedar stack, please see * the Dev Center. * @return */ public App createApp() { - return connection.execute(new AppCreate(new App().on(Heroku.Stack.Cedar)), apiKey); + return connection.execute(new AppCreate(new App().on(Heroku.Stack.Cedar14)), apiKey); } /** @@ -185,32 +163,6 @@ public App createApp(App app) { return connection.execute(new AppCreate(app), apiKey); } - /** - * Clone an existing app that has previously been designated as a template - * into the authenticated user's account with a randomly generated name. - * App cloning is only supported on the {@link Heroku.Stack.Cedar} stack. - * - * @param templateAppName Name of the template app to clone. - * @return details about the cloned app - */ - public App cloneApp(String templateAppName) { - return connection.execute(new AppClone(templateAppName, new App()), apiKey); - } - - /** - * Clone an existing app that has previously been designated as a template - * into the authenticated user's account with details specified in the target app. - * Currently, only specifying the name of the target app is supported. - * App cloning is only supported on the {@link Heroku.Stack.Cedar} stack. - * - * @param templateAppName Name of the template app to clone. - * @param targetApp Details about the target app. - * @return details about the cloned targetApp - */ - public App cloneApp(String templateAppName, App targetApp) { - return connection.execute(new AppClone(templateAppName, targetApp), apiKey); - } - /** * Rename an existing app. * @param appName Existing app name. See {@link #listApps()} for names that can be used. @@ -268,25 +220,6 @@ public AddonChange removeAddon(String appName, String addonName) { // TODO: need addon upgrade/downgrade - /** - * Change the number of processes running for a given process type. - * @param appName App name. See {@link #listApps} for a list of apps that can be used. - * @param processType Process name. See {@link #listProcesses} for a list of processes that can be used. - * @param quantity The number to scale the process to. - */ - public void scaleProcess(String appName, String processType, int quantity) { - connection.execute(new Scale(appName, processType, quantity), apiKey); - } - - /** - * List of processes running for an app. - * @param appName App name. See {@link #listApps} for a list of apps that can be used. - * @return - */ - public List listProcesses(String appName) { - return connection.execute(new ProcessList(appName), apiKey); - } - /** * List of releases for an app. * @param appName App name. See {@link #listApps} for a list of apps that can be used. @@ -299,11 +232,11 @@ public List listReleases(String appName) { /** * Rollback an app to a specific release. * @param appName App name. See {@link #listApps} for a list of apps that can be used. - * @param releaseName Release name. See {@link #listReleases} for a list of the app's releases. + * @param releaseUuid Release UUID. See {@link #listReleases} for a list of the app's releases. * @return */ - public String rollback(String appName, String releaseName) { - return connection.execute(new Rollback(appName, releaseName), apiKey); + public Release rollback(String appName, String releaseUuid) { + return connection.execute(new Rollback(appName, releaseUuid), apiKey); } /** @@ -344,19 +277,12 @@ public void removeCollaborator(String appName, String collaborator) { } /** - * Add environment variables to an app. + * Update environment variables to an app. * @param appName App name. See {@link #listApps} for a list of apps that can be used. * @param config Key/Value pairs of environment variables. */ - public void addConfig(String appName, Map config) { - String jsonConfig = "{"; - String separator = ""; - for (Map.Entry configEntry : config.entrySet()) { - jsonConfig = jsonConfig.concat(String.format("%s \"%s\":\"%s\"", separator, configEntry.getKey(), configEntry.getValue())); - separator = ","; - } - jsonConfig = jsonConfig.concat("}"); - connection.execute(new ConfigAdd(appName, jsonConfig), apiKey); + public void updateConfig(String appName, Map config) { + connection.execute(new ConfigUpdate(appName, config), apiKey); } /** @@ -368,16 +294,6 @@ public Map listConfig(String appName) { return connection.execute(new ConfigList(appName), apiKey); } - /** - * Remove an environment variable from an app. - * @param appName App name. See {@link #listApps} for a list of apps that can be used. - * @param configVarName Name of the environment variable. See {@link #listConfig} for variables that can be removed - * @return - */ - public Map removeConfig(String appName, String configVarName) { - return connection.execute(new ConfigRemove(appName, configVarName), apiKey); - } - /** * Transfer the ownership of an application to another user. * @param appName App name. See {@link #listApps} for a list of apps that can be used. @@ -398,70 +314,13 @@ public LogStreamResponse getLogs(String appName) { /** * Get logs for an app by specifying additional parameters. - * @param logRequest See {@link LogRequestBuilder} + * @param logRequest See {LogRequestBuilder} * @return */ public LogStreamResponse getLogs(Log.LogRequestBuilder logRequest) { return connection.execute(new Log(logRequest), apiKey); } - /** - * Run a one-off process on a Heroku dyno. - * @param appName App name. See {@link #listApps} for a list of apps that can be used. - * @param command Bash command to run inside a dyno. See One-off processes. - */ - public void run(String appName, String command) { - connection.execute(new Run(appName, command), apiKey); - } - - /** - * Run a one-off process on a Heroku dyno in an attached state. Running in an attached state allows for interactive input. Refer to {@link com.heroku.api.request.run.RunResponse#attach()} - * for more information on running an attached process. - * @param appName App name. See {@link #listApps} for a list of apps that can be used. - * @param command Bash command to run inside a dyno. See One-off processes. - * @return - */ - public RunResponse runAttached(String appName, String command) { - return connection.execute(new Run(appName, command, true), apiKey); - } - - /** - * Restart an app. - * @param appName See {@link #listApps} for a list of apps that can be used. - */ - public void restart(String appName) { - connection.execute(new Restart(appName), apiKey); - } - - /** - * Restart a process for an app. - * @param appName See {@link #listApps} for a list of apps that can be used. - * @param type The type of process to restart. e.g. web, worker, etc... - */ - public void restartProcessByType(String appName, String type) { - connection.execute(new Restart.ProcessTypeRestart(appName, type), apiKey); - } - - /** - * Restart a named process. - * @param appName See {@link #listApps} for a list of apps that can be used. - * @param procName Name of process to restart. - */ - public void restartProcessByName(String appName, String procName) { - connection.execute(new Restart.NamedProcessRestart(appName, procName), apiKey); - } - - /** - * Migrates an app from its current stack to the specified stack. Stacks must be compatible with one another. e.g. an app can be migrated from - * {@link com.heroku.api.Heroku.Stack.Bamboo187} to {@link com.heroku.api.Heroku.Stack.Bamboo192}, but not to {@link com.heroku.api.Heroku.Stack.Cedar}. - * @param appName See {@link #listApps} for a list of apps that can be used. - * @param migrateTo Stack to migrate the app to. - * @return A message about the migration. - */ - public String migrateStack(String appName, Heroku.Stack migrateTo) { - return connection.execute(new StackMigrate(appName, migrateTo), apiKey); - } - /** * Gets a list of stacks available. * @param appName See {@link #listApps} for a list of apps that can be used. @@ -478,7 +337,8 @@ public List listAppStacks(String appName) { * @return true if maintenance mode is enabled */ public boolean isMaintenanceModeEnabled(String appName) { - return connection.execute(new MaintenanceInfo(appName), apiKey); + App app = connection.execute(new AppInfo(appName), apiKey); + return app.isMaintenance(); } /** @@ -488,6 +348,6 @@ public boolean isMaintenanceModeEnabled(String appName) { * @param enable true to enable; false to disable */ public void setMaintenanceMode(String appName, boolean enable) { - connection.execute(new MaintenanceUpdate(appName, enable), apiKey); + connection.execute(new AppUpdate(appName, enable), apiKey); } } diff --git a/heroku-api/src/main/java/com/heroku/api/Key.java b/heroku-api/src/main/java/com/heroku/api/Key.java index cf5dbb7..23c217d 100644 --- a/heroku-api/src/main/java/com/heroku/api/Key.java +++ b/heroku-api/src/main/java/com/heroku/api/Key.java @@ -12,7 +12,25 @@ public class Key implements Serializable { private static final long serialVersionUID = 1L; String email; - String contents; + String comment; + String id; + String fingerprint; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFingerprint() { + return fingerprint; + } + + public void setFingerprint(String fingerprint) { + this.fingerprint = fingerprint; + } public String getEmail() { return email; @@ -22,11 +40,11 @@ private void setEmail(String email) { this.email = email; } - public String getContents() { - return contents; + public String getComment() { + return comment; } private void setContents(String contents) { - this.contents = contents; + this.comment = comment; } } diff --git a/heroku-api/src/main/java/com/heroku/api/Release.java b/heroku-api/src/main/java/com/heroku/api/Release.java index 90dd102..abe8636 100644 --- a/heroku-api/src/main/java/com/heroku/api/Release.java +++ b/heroku-api/src/main/java/com/heroku/api/Release.java @@ -13,73 +13,67 @@ public class Release implements Serializable { private static final long serialVersionUID = 1L; - String name, descr,user, commit; + String id; + int version; + String status; + Map user; + String description; String created_at; - Map env; - List addons; - Map pstable; + List addon_plan_names; - public String getName() { - return name; - } - - private void setName(String name) { - this.name = name; - } - - public String getDescription() { - return descr; + public Map getUser() { + return user; } - private void setDescr(String descr) { - this.descr = descr; + public void setUser(Map user) { + this.user = user; } - public String getUser() { - return user; + public String getStatus() { + return status; } - private void setUser(String user) { - this.user = user; + public void setStatus(String status) { + this.status = status; } - public String getCommit() { - return commit; + public int getVersion() { + return version; } - private void setCommit(String commit) { - this.commit = commit; + public void setVersion(int version) { + this.version = version; } - public String getCreatedAt() { - return created_at; + public String getId() { + return id; } - private void setCreated_at(String created_at) { - this.created_at = created_at; + public void setId(String id) { + this.id = id; } - public Map getEnv() { - return env; + public String getDescription() { + return description; } - private void setEnv(Map env) { - this.env = env; + private void setDescription(String description) { + this.description = description; } - public List getAddons() { - return addons; + public String getCreatedAt() { + return created_at; } - private void setAddons(List addons) { - this.addons = addons; + private void setCreated_at(String created_at) { + this.created_at = created_at; } - public Map getPSTable() { - return pstable; + public List getAddon_plan_names() { + return addon_plan_names; } - private void setPstable(Map pstable) { - this.pstable = pstable; + private void setAddon_plan_names(List addon_plan_names) { + this.addon_plan_names = addon_plan_names; } } diff --git a/heroku-api/src/main/java/com/heroku/api/StackInfo.java b/heroku-api/src/main/java/com/heroku/api/StackInfo.java index 68d5cfa..7353b7c 100644 --- a/heroku-api/src/main/java/com/heroku/api/StackInfo.java +++ b/heroku-api/src/main/java/com/heroku/api/StackInfo.java @@ -9,33 +9,24 @@ public class StackInfo implements Serializable { private static final long serialVersionUID = 1L; - String requested; - boolean beta; - boolean current; + String id; String name; + String state; - public String getRequested() { - return requested; + public String getId() { + return id; } - private void setRequested(String requested) { - this.requested = requested; + private void setId(String id) { + this.id = id; } - public boolean isBeta() { - return beta; + public String getState() { + return state; } - private void setBeta(boolean beta) { - this.beta = beta; - } - - public boolean isCurrent() { - return current; - } - - private void setCurrent(boolean current) { - this.current = current; + private void setState(String state) { + this.state = state; } public String getName() { diff --git a/heroku-api/src/main/java/com/heroku/api/http/Http.java b/heroku-api/src/main/java/com/heroku/api/http/Http.java index 517afda..8be4560 100644 --- a/heroku-api/src/main/java/com/heroku/api/http/Http.java +++ b/heroku-api/src/main/java/com/heroku/api/http/Http.java @@ -39,6 +39,7 @@ public String getHeaderValue() { * HTTP Content-Type header model. */ public static enum ContentType implements Header { + JSON("application/json"), FORM_URLENCODED("application/x-www-form-urlencoded"), SSH_AUTHKEY("text/ssh-authkey"); @@ -126,7 +127,7 @@ public static Map setHeaders(Header... headers) { /** * HTTP Methods. Not all are implemented. Only those used by the Heroku API. */ - public static enum Method {GET, PUT, POST, DELETE} + public static enum Method {GET, PUT, POST, DELETE, PATCH} /** * HTTP Status codes. Not all are implemented. Only those used by the Heroku API. diff --git a/heroku-api/src/main/java/com/heroku/api/parser/Json.java b/heroku-api/src/main/java/com/heroku/api/parser/Json.java index 5dc0b12..ff0c299 100644 --- a/heroku-api/src/main/java/com/heroku/api/parser/Json.java +++ b/heroku-api/src/main/java/com/heroku/api/parser/Json.java @@ -27,6 +27,17 @@ static class Holder { } } + /** + * Proxy method for getting the Parser and calling encode(). + * @param object JSON byte array to be parsed. + * @param type Deserialized type for the JSON data + * @param Deserialzed object type + * @return The JSON data deserialized to T + */ + public static String encode(Object object) { + return Holder.parser.encode(object); + } + /** * Proxy method for getting the Parser and calling parse(). * @param data JSON byte array to be parsed. diff --git a/heroku-api/src/main/java/com/heroku/api/parser/Parser.java b/heroku-api/src/main/java/com/heroku/api/parser/Parser.java index 31d9176..39cb332 100644 --- a/heroku-api/src/main/java/com/heroku/api/parser/Parser.java +++ b/heroku-api/src/main/java/com/heroku/api/parser/Parser.java @@ -6,4 +6,5 @@ public interface Parser { T parse(byte[] data, Type type); + String encode(Object object); } diff --git a/heroku-api/src/main/java/com/heroku/api/request/Request.java b/heroku-api/src/main/java/com/heroku/api/request/Request.java index 22723cc..7fe14bf 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/Request.java +++ b/heroku-api/src/main/java/com/heroku/api/request/Request.java @@ -39,6 +39,13 @@ public interface Request { */ String getBody(); + /** + * Value of the request body as a Map. + * @return Body + * @throws UnsupportedOperationException Generally thrown if {@link #hasBody()} returns false + */ + Map getBodyAsMap(); + /** * HTTP Accept header. * @return The Accept header to be used in the request. Typically "application/json" or "text/xml" diff --git a/heroku-api/src/main/java/com/heroku/api/request/RequestConfig.java b/heroku-api/src/main/java/com/heroku/api/request/RequestConfig.java index 1099d98..2b6d067 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/RequestConfig.java +++ b/heroku-api/src/main/java/com/heroku/api/request/RequestConfig.java @@ -1,8 +1,10 @@ package com.heroku.api.request; import com.heroku.api.Heroku; +import com.heroku.api.parser.Json; import java.util.EnumMap; +import java.util.HashMap; import java.util.Map; /** @@ -12,24 +14,34 @@ * @author Naaman Newbold */ public class RequestConfig { - private final Map config = new EnumMap(Heroku.RequestKey.class); + + private String appName; + + private final Map config = new EnumMap(Heroku.RequestKey.class); /** - * Sets the {@link Heroku.RequestKey.Stack} parameter. + * Sets the {Heroku.RequestKey.Stack} parameter. * @param stack A {@link com.heroku.api.Heroku.Stack} to specify in the config. * @return A new {@link RequestConfig} */ public RequestConfig onStack(Heroku.Stack stack) { - return with(Heroku.RequestKey.Stack, stack.value); + Map stackMap = new EnumMap(Heroku.RequestKey.class); + stackMap.put(Heroku.RequestKey.StackName, new Either(stack.value)); + return with(Heroku.RequestKey.Stack, stackMap); } /** - * Sets the {@link Heroku.RequestKey.AppName} parameter. + * Sets the {Heroku.RequestKey.AppName} parameter. * @param appName Name of the app to specify in the config. * @return A new {@link RequestConfig} */ public RequestConfig app(String appName) { - return with(Heroku.RequestKey.AppName, appName); + this.appName = appName; + return this; + } + + public String getAppName() { + return this.appName; } /** @@ -39,22 +51,99 @@ public RequestConfig app(String appName) { * @return A new {@link RequestConfig} */ public RequestConfig with(Heroku.RequestKey key, String value) { - RequestConfig newConfig = new RequestConfig(); - newConfig.config.putAll(config); - newConfig.config.put(key, value); + RequestConfig newConfig = copy(); + newConfig.config.put(key, new Either(value)); + return newConfig; + } + + /** + * Sets a {@link com.heroku.api.Heroku.RequestKey} parameter. + * @param key + * @param value + * @return A new {@link RequestConfig} + */ + public RequestConfig with(Heroku.RequestKey key, Map value) { + RequestConfig newConfig = copy(); + newConfig.config.put(key, new Either(value)); return newConfig; } public String get(Heroku.RequestKey key) { - return config.get(key); + return config.get(key).string(); } - - public Map asMap() { - return config; + + public Map getMap(Heroku.RequestKey key) { + return config.get(key).map(); } public boolean has(Heroku.RequestKey key){ return config.containsKey(key); } + public String asJson() { + return Json.encode(asMap()); + } + + public Map asMap() { + return stringifyMap(config); + } + + private Map stringifyMap(Map map) { + Map jsonMap = new HashMap(); + for (Heroku.RequestKey key : map.keySet()) { + RequestConfig.Either either = map.get(key); + if (either.isString()) { + jsonMap.put(key.queryParameter, either.string()); + } else { + jsonMap.put(key.queryParameter, stringifyMap(either.map())); + } + } + return jsonMap; + } + + private RequestConfig copy() { + RequestConfig newConfig = new RequestConfig(); + newConfig.app(this.appName); + newConfig.config.putAll(config); + return newConfig; + } + + public static class Either { + + private Class type; + + private String string; + + private Map map; + + public Either(String value) { + this.type = String.class; + this.string = value; + } + + public Either(Map value) { + this.type = Map.class; + this.map = value; + } + + public String string() { + return string; + } + + public Map map() { + return map; + } + + public Object value() { + return (isString()) ? string : map; + } + + public Boolean isMap() { + return Map.class.equals(type); + } + + public Boolean isString() { + return String.class.equals(type); + } + } } diff --git a/heroku-api/src/main/java/com/heroku/api/request/RequestTransformation.java b/heroku-api/src/main/java/com/heroku/api/request/RequestTransformation.java index 9c2795f..63d5d46 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/RequestTransformation.java +++ b/heroku-api/src/main/java/com/heroku/api/request/RequestTransformation.java @@ -40,6 +40,11 @@ public String getBody() { return a.getBody(); } + @Override + public Map getBodyAsMap() { + return a.getBodyAsMap(); + } + @Override public Http.Accept getResponseType() { return a.getResponseType(); diff --git a/heroku-api/src/main/java/com/heroku/api/request/addon/AddonInstall.java b/heroku-api/src/main/java/com/heroku/api/request/addon/AddonInstall.java index 3a1ddbc..a8b49e4 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/addon/AddonInstall.java +++ b/heroku-api/src/main/java/com/heroku/api/request/addon/AddonInstall.java @@ -4,11 +4,11 @@ import com.heroku.api.Heroku; import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; import com.heroku.api.parser.Json; import com.heroku.api.request.Request; import com.heroku.api.request.RequestConfig; +import java.util.Collections; import java.util.Map; /** @@ -20,8 +20,8 @@ public class AddonInstall implements Request { private final RequestConfig config; - public AddonInstall(String appName, String addonName) { - config = new RequestConfig().app(appName).with(Heroku.RequestKey.AddonName, addonName); + public AddonInstall(String appName, String addonPlan) { + config = new RequestConfig().app(appName).with(Heroku.RequestKey.AddonPlan, addonPlan); } @Override @@ -31,17 +31,22 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.AppAddon.format(config.get(Heroku.RequestKey.AppName), config.get(Heroku.RequestKey.AddonName)); + return Heroku.Resource.AppAddons.format(config.getAppName()); } @Override public boolean hasBody() { - return false; + return true; } @Override public String getBody() { - throw HttpUtil.noBody(); + return config.asJson(); + } + + @Override + public Map getBodyAsMap() { + return config.asMap(); } @Override @@ -51,15 +56,15 @@ public Http.Accept getResponseType() { @Override public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); + return Collections.emptyMap(); } @Override public AddonChange getResponse(byte[] bytes, int status) { - if (status == Http.Status.OK.statusCode) { + if (status == Http.Status.CREATED.statusCode) { return Json.parse(bytes, this.getClass()); } else { - throw new RequestFailedException("Unable to add addon " + config.get(Heroku.RequestKey.AddonName), status, bytes); + throw new RequestFailedException("Unable to add addon " + config.get(Heroku.RequestKey.AddonPlan), status, bytes); } } } diff --git a/heroku-api/src/main/java/com/heroku/api/request/addon/AddonList.java b/heroku-api/src/main/java/com/heroku/api/request/addon/AddonList.java index c9ac816..d4c807f 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/addon/AddonList.java +++ b/heroku-api/src/main/java/com/heroku/api/request/addon/AddonList.java @@ -4,13 +4,13 @@ import com.heroku.api.Heroku; import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; import com.heroku.api.request.Request; -import java.util.HashMap; +import java.util.Collections; import java.util.List; import java.util.Map; +import static com.heroku.api.http.HttpUtil.noBody; import static com.heroku.api.parser.Json.parse; /** @@ -37,7 +37,12 @@ public boolean hasBody() { @Override public String getBody() { - throw HttpUtil.noBody(); + throw noBody(); + } + + @Override + public Map getBodyAsMap() { + throw noBody(); } @Override @@ -47,7 +52,7 @@ public Http.Accept getResponseType() { @Override public Map getHeaders() { - return new HashMap(); + return Collections.emptyMap(); } @Override diff --git a/heroku-api/src/main/java/com/heroku/api/request/addon/AddonRemove.java b/heroku-api/src/main/java/com/heroku/api/request/addon/AddonRemove.java index 5b95776..d8699ba 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/addon/AddonRemove.java +++ b/heroku-api/src/main/java/com/heroku/api/request/addon/AddonRemove.java @@ -45,6 +45,11 @@ public String getBody() { throw noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/addon/AppAddonsList.java b/heroku-api/src/main/java/com/heroku/api/request/addon/AppAddonsList.java index 331c4cc..c7e038b 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/addon/AppAddonsList.java +++ b/heroku-api/src/main/java/com/heroku/api/request/addon/AppAddonsList.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; +import static com.heroku.api.http.HttpUtil.noBody; import static com.heroku.api.parser.Json.parse; /** @@ -34,7 +35,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.AppAddons.format(config.get(Heroku.RequestKey.AppName)); + return Heroku.Resource.AppAddons.format(config.getAppName()); } @Override @@ -47,6 +48,11 @@ public String getBody() { throw HttpUtil.noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; @@ -63,6 +69,6 @@ public List getResponse(byte[] bytes, int status) { return parse(bytes, getClass()); } throw new RequestFailedException( - "Unable to get addons for " + config.get(Heroku.RequestKey.AppName), status, bytes); + "Unable to get addons for " + config.getAppName(), status, bytes); } } diff --git a/heroku-api/src/main/java/com/heroku/api/request/app/AppClone.java b/heroku-api/src/main/java/com/heroku/api/request/app/AppClone.java deleted file mode 100644 index 0584de7..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/app/AppClone.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.heroku.api.request.app; - -import com.heroku.api.App; -import com.heroku.api.Heroku; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; -import com.heroku.api.request.Request; -import com.heroku.api.request.RequestConfig; - -import java.util.Map; - -import static com.heroku.api.parser.Json.parse; - -/** - * @author Ryan Brainard - */ -public class AppClone implements Request { - - private final RequestConfig config; - - public AppClone(String templateAppName, App targetApp) { - RequestConfig builder = new RequestConfig(); - builder = builder.with(Heroku.RequestKey.AppName, templateAppName); - builder = (targetApp.getName() != null) ? builder.with(Heroku.RequestKey.CreateAppName, targetApp.getName()) : builder; - config = builder; - } - - @Override - public Http.Method getHttpMethod() { - return Http.Method.POST; - } - - @Override - public String getEndpoint() { - return Heroku.Resource.AppClone.format(config.get(Heroku.RequestKey.AppName)); - } - - @Override - public boolean hasBody() { - return true; - } - - @Override - public String getBody() { - return HttpUtil.encodeParameters(config, Heroku.RequestKey.CreateAppName); - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); - } - - @Override - public App getResponse(byte[] in, int status) { - if (status != Http.Status.OK.statusCode) { - throw new RequestFailedException("Failed to clone app", status, in); - } - - return parse(in, getClass()); - } -} diff --git a/heroku-api/src/main/java/com/heroku/api/request/app/AppCreate.java b/heroku-api/src/main/java/com/heroku/api/request/app/AppCreate.java index 295f254..95a67cf 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/app/AppCreate.java +++ b/heroku-api/src/main/java/com/heroku/api/request/app/AppCreate.java @@ -4,10 +4,10 @@ import com.heroku.api.Heroku; import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; import com.heroku.api.request.Request; import com.heroku.api.request.RequestConfig; +import java.util.Collections; import java.util.Map; import static com.heroku.api.parser.Json.parse; @@ -23,7 +23,7 @@ public class AppCreate implements Request { public AppCreate(App app) { RequestConfig builder = new RequestConfig(); - builder = (app.getName() != null) ? builder.with(Heroku.RequestKey.CreateAppName, app.getName()) : builder; + builder = (app.getName() != null) ? builder.with(Heroku.RequestKey.AppName, app.getName()) : builder; builder = (app.getStack() != null) ? builder.onStack(app.getStack()) : builder; config = builder; } @@ -45,7 +45,12 @@ public boolean hasBody() { @Override public String getBody() { - return HttpUtil.encodeParameters(config, Heroku.RequestKey.Stack, Heroku.RequestKey.CreateAppName); + return config.asJson(); + } + + @Override + public Map getBodyAsMap() { + return config.asMap(); } @Override @@ -55,12 +60,14 @@ public Http.Accept getResponseType() { @Override public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); + return Collections.emptyMap(); } @Override public App getResponse(byte[] in, int code) { - if (code == Http.Status.ACCEPTED.statusCode) + if (code == Http.Status.CREATED.statusCode) + return parse(in, getClass()); + else if (code == Http.Status.ACCEPTED.statusCode) return parse(in, getClass()); else throw new RequestFailedException("Failed to create app", code, in); diff --git a/heroku-api/src/main/java/com/heroku/api/request/app/AppDestroy.java b/heroku-api/src/main/java/com/heroku/api/request/app/AppDestroy.java index 160b9ac..0063fdf 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/app/AppDestroy.java +++ b/heroku-api/src/main/java/com/heroku/api/request/app/AppDestroy.java @@ -11,6 +11,8 @@ import java.util.Collections; import java.util.Map; +import static com.heroku.api.http.HttpUtil.noBody; + /** * TODO: Javadoc * @@ -31,7 +33,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.App.format(config.get(Heroku.RequestKey.AppName)); + return Heroku.Resource.App.format(config.getAppName()); } @Override @@ -44,6 +46,11 @@ public String getBody() { throw HttpUtil.noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/app/AppInfo.java b/heroku-api/src/main/java/com/heroku/api/request/app/AppInfo.java index c56fee2..9b4acb9 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/app/AppInfo.java +++ b/heroku-api/src/main/java/com/heroku/api/request/app/AppInfo.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.Map; +import static com.heroku.api.http.HttpUtil.noBody; import static com.heroku.api.parser.Json.parse; /** @@ -33,7 +34,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.App.format(config.get(Heroku.RequestKey.AppName)); + return Heroku.Resource.App.format(config.getAppName()); } @Override @@ -46,6 +47,11 @@ public String getBody() { throw HttpUtil.noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/app/AppList.java b/heroku-api/src/main/java/com/heroku/api/request/app/AppList.java index 2000f8f..281d55d 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/app/AppList.java +++ b/heroku-api/src/main/java/com/heroku/api/request/app/AppList.java @@ -12,6 +12,8 @@ import java.util.List; import java.util.Map; +import static com.heroku.api.http.HttpUtil.noBody; + /** * TODO: Javadoc * @@ -39,6 +41,11 @@ public String getBody() { throw HttpUtil.noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/app/AppRename.java b/heroku-api/src/main/java/com/heroku/api/request/app/AppRename.java index 5eaaed7..522f24a 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/app/AppRename.java +++ b/heroku-api/src/main/java/com/heroku/api/request/app/AppRename.java @@ -7,9 +7,9 @@ import com.heroku.api.request.Request; import com.heroku.api.request.RequestConfig; +import java.util.Collections; import java.util.Map; -import static com.heroku.api.http.HttpUtil.encodeParameters; import static com.heroku.api.parser.Json.parse; /** @@ -21,7 +21,7 @@ public class AppRename implements Request { private final RequestConfig config; public AppRename(String appName, String newName) { - this.config = new RequestConfig().app(appName).with(Heroku.RequestKey.CreateAppName, newName); + this.config = new RequestConfig().app(appName).with(Heroku.RequestKey.AppName, newName); } @Override @@ -31,7 +31,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.App.format(config.get(Heroku.RequestKey.AppName)); + return Heroku.Resource.App.format(config.getAppName()); } @Override @@ -41,7 +41,12 @@ public boolean hasBody() { @Override public String getBody() { - return encodeParameters(config, Heroku.RequestKey.CreateAppName); + return config.asJson(); + } + + @Override + public Map getBodyAsMap() { + return config.asMap(); } @Override @@ -51,7 +56,7 @@ public Http.Accept getResponseType() { @Override public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); + return Collections.emptyMap(); } @Override diff --git a/heroku-api/src/main/java/com/heroku/api/request/app/AppUpdate.java b/heroku-api/src/main/java/com/heroku/api/request/app/AppUpdate.java new file mode 100644 index 0000000..53b4d8c --- /dev/null +++ b/heroku-api/src/main/java/com/heroku/api/request/app/AppUpdate.java @@ -0,0 +1,70 @@ +package com.heroku.api.request.app; + +import com.heroku.api.App; +import com.heroku.api.Heroku; +import com.heroku.api.exception.RequestFailedException; +import com.heroku.api.http.Http; +import com.heroku.api.request.Request; +import com.heroku.api.request.RequestConfig; + +import java.util.Collections; +import java.util.Map; + +import static com.heroku.api.parser.Json.parse; + +/** + * TODO: Javadoc + * + * @author Joe Kutner + */ +public class AppUpdate implements Request { + + private final RequestConfig config; + + public AppUpdate(String appName, Boolean maintenance) { + this.config = new RequestConfig().app(appName).with(Heroku.RequestKey.AppMaintenance, maintenance.toString()); + } + + @Override + public Http.Method getHttpMethod() { + return Http.Method.PATCH; + } + + @Override + public String getEndpoint() { + return Heroku.Resource.App.format(config.getAppName()); + } + + @Override + public boolean hasBody() { + return true; + } + + @Override + public String getBody() { + return config.asJson(); + } + + @Override + public Map getBodyAsMap() { + return config.asMap(); + } + + @Override + public Http.Accept getResponseType() { + return Http.Accept.JSON; + } + + @Override + public Map getHeaders() { + return Collections.emptyMap(); + } + + @Override + public App getResponse(byte[] in, int code) { + if (code == Http.Status.OK.statusCode) + return parse(in, getClass()); + else + throw new RequestFailedException("App update failed", code, in); + } +} \ No newline at end of file diff --git a/heroku-api/src/main/java/com/heroku/api/request/config/ConfigList.java b/heroku-api/src/main/java/com/heroku/api/request/config/ConfigList.java index 279ef47..e8b2c0d 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/config/ConfigList.java +++ b/heroku-api/src/main/java/com/heroku/api/request/config/ConfigList.java @@ -3,13 +3,13 @@ import com.heroku.api.Heroku; import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; import com.heroku.api.request.Request; import com.heroku.api.request.RequestConfig; import java.util.Collections; import java.util.Map; +import static com.heroku.api.http.HttpUtil.noBody; import static com.heroku.api.parser.Json.parse; /** @@ -32,7 +32,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.ConfigVars.format(config.get(Heroku.RequestKey.AppName)); + return Heroku.Resource.ConfigVars.format(config.getAppName()); } @Override @@ -42,7 +42,12 @@ public boolean hasBody() { @Override public String getBody() { - throw HttpUtil.noBody(); + throw noBody(); + } + + @Override + public Map getBodyAsMap() { + throw noBody(); } @Override diff --git a/heroku-api/src/main/java/com/heroku/api/request/config/ConfigRemove.java b/heroku-api/src/main/java/com/heroku/api/request/config/ConfigRemove.java deleted file mode 100644 index 2e5a5e4..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/config/ConfigRemove.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.heroku.api.request.config; - -import com.heroku.api.Heroku; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; -import com.heroku.api.request.Request; -import com.heroku.api.request.RequestConfig; - -import java.util.Collections; -import java.util.Map; - -import static com.heroku.api.http.HttpUtil.encodeIncludingSpecialCharacters; -import static com.heroku.api.parser.Json.parse; - -/** - * TODO: Javadoc - * - * @author Naaman Newbold - */ -public class ConfigRemove implements Request> { - - private final RequestConfig config; - - public ConfigRemove(String appName, String configVarName) { - config = new RequestConfig().app(appName).with(Heroku.RequestKey.ConfigVarName, configVarName); - } - - @Override - public Http.Method getHttpMethod() { - return Http.Method.DELETE; - } - - @Override - public String getEndpoint() { - return Heroku.Resource.ConfigVar.format(config.get(Heroku.RequestKey.AppName), encodeIncludingSpecialCharacters(config.get(Heroku.RequestKey.ConfigVarName))); - } - - @Override - public boolean hasBody() { - return false; - } - - @Override - public String getBody() { - throw HttpUtil.noBody(); - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - return Collections.emptyMap(); - } - - @Override - public Map getResponse(byte[] bytes, int status) { - if (status == Http.Status.OK.statusCode) { - return parse(bytes, getClass()); - } else { - throw new RequestFailedException("Config removal failed.", status, bytes); - } - } -} diff --git a/heroku-api/src/main/java/com/heroku/api/request/config/ConfigAdd.java b/heroku-api/src/main/java/com/heroku/api/request/config/ConfigUpdate.java similarity index 67% rename from heroku-api/src/main/java/com/heroku/api/request/config/ConfigAdd.java rename to heroku-api/src/main/java/com/heroku/api/request/config/ConfigUpdate.java index b850181..e691787 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/config/ConfigAdd.java +++ b/heroku-api/src/main/java/com/heroku/api/request/config/ConfigUpdate.java @@ -3,6 +3,7 @@ import com.heroku.api.Heroku; import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; +import com.heroku.api.parser.Json; import com.heroku.api.request.Request; import com.heroku.api.request.RequestConfig; import com.heroku.api.response.Unit; @@ -15,22 +16,25 @@ * * @author James Ward */ -public class ConfigAdd implements Request { +public class ConfigUpdate implements Request { private final RequestConfig config; - public ConfigAdd(String appName, String jsonConfigVars) { - this.config = new RequestConfig().app(appName).with(Heroku.RequestKey.ConfigVars, jsonConfigVars); + private final Map configVars; + + public ConfigUpdate(String appName, Map configVars) { + this.configVars = configVars; + this.config = new RequestConfig().app(appName); } @Override public Http.Method getHttpMethod() { - return Http.Method.PUT; + return Http.Method.PATCH; } @Override public String getEndpoint() { - return Heroku.Resource.ConfigVars.format(config.get(Heroku.RequestKey.AppName)); + return Heroku.Resource.ConfigVars.format(config.getAppName()); } @Override @@ -40,7 +44,12 @@ public boolean hasBody() { @Override public String getBody() { - return config.get(Heroku.RequestKey.ConfigVars); + return Json.encode(configVars); + } + + @Override + public Map getBodyAsMap() { + return configVars; } @Override diff --git a/heroku-api/src/main/java/com/heroku/api/request/domain/DomainAdd.java b/heroku-api/src/main/java/com/heroku/api/request/domain/DomainAdd.java index 524a1e4..303f9b4 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/domain/DomainAdd.java +++ b/heroku-api/src/main/java/com/heroku/api/request/domain/DomainAdd.java @@ -3,17 +3,14 @@ import com.heroku.api.Domain; import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; import com.heroku.api.request.Request; import com.heroku.api.request.RequestConfig; +import java.util.Collections; import java.util.Map; -import static com.heroku.api.Heroku.RequestKey.AppName; import static com.heroku.api.Heroku.RequestKey.CreateDomain; import static com.heroku.api.Heroku.Resource.Domains; -import static com.heroku.api.http.Http.ContentType.FORM_URLENCODED; -import static com.heroku.api.http.Http.Header; import static com.heroku.api.parser.Json.parse; /** @@ -22,10 +19,12 @@ * @author Naaman Newbold */ public class DomainAdd implements Request { + private final RequestConfig config; public DomainAdd(String appName, String domainName) { - this(new RequestConfig().app(appName).with(CreateDomain, domainName)); + this(new RequestConfig().app(appName). + with(CreateDomain, domainName)); } public DomainAdd(RequestConfig config) { @@ -39,7 +38,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Domains.format(config.get(AppName)); + return Domains.format(config.getAppName()); } @Override @@ -49,7 +48,12 @@ public boolean hasBody() { @Override public String getBody() { - return HttpUtil.encodeParameters(config, AppName, CreateDomain); + return config.asJson(); + } + + @Override + public Map getBodyAsMap() { + return config.asMap(); } @Override @@ -59,7 +63,7 @@ public Http.Accept getResponseType() { @Override public Map getHeaders() { - return Header.Util.setHeaders(FORM_URLENCODED); + return Collections.emptyMap(); } @Override diff --git a/heroku-api/src/main/java/com/heroku/api/request/domain/DomainList.java b/heroku-api/src/main/java/com/heroku/api/request/domain/DomainList.java index f692371..f24af1b 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/domain/DomainList.java +++ b/heroku-api/src/main/java/com/heroku/api/request/domain/DomainList.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Map; -import static com.heroku.api.Heroku.RequestKey.AppName; import static com.heroku.api.Heroku.Resource.Domains; import static com.heroku.api.http.HttpUtil.noBody; import static com.heroku.api.parser.Json.parse; @@ -38,7 +37,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Domains.format(config.get(AppName)); + return Domains.format(config.getAppName()); } @Override @@ -51,6 +50,11 @@ public String getBody() { throw noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/domain/DomainRemove.java b/heroku-api/src/main/java/com/heroku/api/request/domain/DomainRemove.java index 8090082..b374074 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/domain/DomainRemove.java +++ b/heroku-api/src/main/java/com/heroku/api/request/domain/DomainRemove.java @@ -9,7 +9,6 @@ import java.util.Collections; import java.util.Map; -import static com.heroku.api.Heroku.RequestKey.AppName; import static com.heroku.api.Heroku.RequestKey.DeleteDomain; import static com.heroku.api.Heroku.Resource.Domain; import static com.heroku.api.http.HttpUtil.noBody; @@ -36,7 +35,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Domain.format(config.get(AppName), config.get(DeleteDomain)); + return Domain.format(config.getAppName(), config.get(DeleteDomain)); } @Override @@ -49,6 +48,11 @@ public String getBody() { throw noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/key/KeyAdd.java b/heroku-api/src/main/java/com/heroku/api/request/key/KeyAdd.java index 46fc988..1d9629f 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/key/KeyAdd.java +++ b/heroku-api/src/main/java/com/heroku/api/request/key/KeyAdd.java @@ -7,6 +7,7 @@ import com.heroku.api.request.RequestConfig; import com.heroku.api.response.Unit; +import java.util.Collections; import java.util.Map; /** @@ -16,8 +17,6 @@ */ public class KeyAdd implements Request { - // post("/user/keys", key, { 'Content-Type' => 'text/ssh-authkey' }).to_s - private final RequestConfig config; public KeyAdd(String sshkey) { @@ -41,7 +40,12 @@ public boolean hasBody() { @Override public String getBody() { - return config.get(Heroku.RequestKey.SSHKey); + return config.asJson(); + } + + @Override + public Map getBodyAsMap() { + return config.asMap(); } @Override @@ -51,11 +55,11 @@ public Http.Accept getResponseType() { @Override public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.SSH_AUTHKEY); + return Collections.emptyMap(); } public Unit getResponse(byte[] in, int code) { - if (code == Http.Status.OK.statusCode) + if (code == Http.Status.CREATED.statusCode) return Unit.unit; else throw new RequestFailedException("KeysAdd failed", code, in); diff --git a/heroku-api/src/main/java/com/heroku/api/request/key/KeyList.java b/heroku-api/src/main/java/com/heroku/api/request/key/KeyList.java index 5c12b80..442979e 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/key/KeyList.java +++ b/heroku-api/src/main/java/com/heroku/api/request/key/KeyList.java @@ -41,6 +41,11 @@ public String getBody() { throw noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/key/KeyRemove.java b/heroku-api/src/main/java/com/heroku/api/request/key/KeyRemove.java index 0edca85..2421e5a 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/key/KeyRemove.java +++ b/heroku-api/src/main/java/com/heroku/api/request/key/KeyRemove.java @@ -12,6 +12,7 @@ import java.util.Map; import static com.heroku.api.http.HttpUtil.encodeIncludingSpecialCharacters; +import static com.heroku.api.http.HttpUtil.noBody; /** * TODO: Javadoc @@ -24,8 +25,8 @@ public class KeyRemove implements Request { private final RequestConfig config; - public KeyRemove(String keyName) { - this.config = new RequestConfig().with(Heroku.RequestKey.SSHKey, keyName); + public KeyRemove(String keyIdOrFingerprint) { + this.config = new RequestConfig().with(Heroku.RequestKey.SSHKey, keyIdOrFingerprint); } @Override @@ -48,6 +49,11 @@ public String getBody() { throw HttpUtil.noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/key/KeysRemoveAll.java b/heroku-api/src/main/java/com/heroku/api/request/key/KeysRemoveAll.java deleted file mode 100644 index 20427bb..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/key/KeysRemoveAll.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.heroku.api.request.key; - -import com.heroku.api.Heroku; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.request.Request; -import com.heroku.api.response.Unit; - -import java.util.Collections; -import java.util.Map; - -import static com.heroku.api.http.HttpUtil.noBody; - -/** - * Deletes all SSH keys. - * - * @author Naaman Newbold - */ -public class KeysRemoveAll implements Request { - - @Override - public Http.Method getHttpMethod() { - return Http.Method.DELETE; - } - - @Override - public String getEndpoint() { - return Heroku.Resource.Keys.format(); - } - - @Override - public boolean hasBody() { - return false; - } - - @Override - public String getBody() { - throw noBody(); - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - return Collections.emptyMap(); - } - - @Override - public Unit getResponse(byte[] bytes, int status) { - if (Http.Status.OK.equals(status)) { - return Unit.unit; - } else { - throw new RequestFailedException("Unable to delete all keys.", status, bytes); - } - } -} diff --git a/heroku-api/src/main/java/com/heroku/api/request/log/Log.java b/heroku-api/src/main/java/com/heroku/api/request/log/Log.java index 8fee05a..ba33fed 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/log/Log.java +++ b/heroku-api/src/main/java/com/heroku/api/request/log/Log.java @@ -13,6 +13,7 @@ import java.util.Map; import static com.heroku.api.Heroku.RequestKey.*; +import static com.heroku.api.http.HttpUtil.noBody; public class Log implements Request { @@ -137,6 +138,11 @@ public String getBody() { return null; } + @Override + public Map getBodyAsMap() { + return null; + } + @Override public Http.Accept getResponseType() { return Http.Accept.TEXT; diff --git a/heroku-api/src/main/java/com/heroku/api/request/login/BasicAuthLogin.java b/heroku-api/src/main/java/com/heroku/api/request/login/BasicAuthLogin.java deleted file mode 100644 index 2ef8cd9..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/login/BasicAuthLogin.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.heroku.api.request.login; - -import com.heroku.api.Heroku; -import com.heroku.api.LoginVerification; -import com.heroku.api.exception.LoginFailedException; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; -import com.heroku.api.parser.Json; -import com.heroku.api.request.Request; -import com.heroku.api.request.RequestConfig; - -import java.util.Map; - - -public class BasicAuthLogin implements Request { - - private String username; - private String password; - - public BasicAuthLogin(String username, String password) { - this.username = username; - this.password = password; - } - - @Override - public Http.Method getHttpMethod() { - return Http.Method.POST; - } - - @Override - public String getEndpoint() { - return Heroku.Resource.Login.value; - } - - @Override - public boolean hasBody() { - return true; - } - - @Override - public String getBody() { - return HttpUtil.encodeParameters( - new RequestConfig().with(Heroku.RequestKey.Username, username) - .with(Heroku.RequestKey.Password, password), - Heroku.RequestKey.Username, Heroku.RequestKey.Password - ); - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); - } - - - @Override - public LoginVerification getResponse(byte[] in, int code) { - if (code == 200) { - return Json.parse(in, this.getClass()); - } else if (code == 404) { - throw new LoginFailedException("Invalid username and password combination.", code, in); - } else { - throw new RequestFailedException("Unknown error occurred while connecting to Heroku.", code, in); - } - } -} - diff --git a/heroku-api/src/main/java/com/heroku/api/request/maintenance/MaintenanceInfo.java b/heroku-api/src/main/java/com/heroku/api/request/maintenance/MaintenanceInfo.java deleted file mode 100644 index 740366f..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/maintenance/MaintenanceInfo.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.heroku.api.request.maintenance; - -import com.heroku.api.Heroku; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; -import com.heroku.api.parser.Json; -import com.heroku.api.parser.TypeReference; -import com.heroku.api.request.Request; -import com.heroku.api.request.RequestConfig; - -import java.util.Collections; -import java.util.Map; - -/** - * @author Ryan Brainard - */ -public class MaintenanceInfo implements Request { - - private final RequestConfig config; - - public MaintenanceInfo(String appName) { - this.config = new RequestConfig().app(appName); - } - - @Override - public Http.Method getHttpMethod() { - return Http.Method.GET; - } - - @Override - public String getEndpoint() { - return Heroku.Resource.Maintenance.format(config.get(Heroku.RequestKey.AppName)); - } - - @Override - public boolean hasBody() { - return false; - } - - @Override - public String getBody() { - throw HttpUtil.noBody(); - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - return Collections.emptyMap(); - } - - @Override - public Boolean getResponse(byte[] in, int code) { - if (Http.Status.OK.equals(code)) { - final Map parsed = Json.parse(in, new TypeReference>() {}.getType()); - return Boolean.valueOf(parsed.get("maintenance")); - } - else - throw new RequestFailedException("MaintenanceInfo failed", code, in); - } -} diff --git a/heroku-api/src/main/java/com/heroku/api/request/maintenance/MaintenanceUpdate.java b/heroku-api/src/main/java/com/heroku/api/request/maintenance/MaintenanceUpdate.java deleted file mode 100644 index 92ede0d..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/maintenance/MaintenanceUpdate.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.heroku.api.request.maintenance; - -import com.heroku.api.Heroku; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; -import com.heroku.api.request.Request; -import com.heroku.api.request.RequestConfig; -import com.heroku.api.response.Unit; - -import java.util.Map; - -/** - * @author Ryan Brainard - */ -public class MaintenanceUpdate implements Request { - - private final RequestConfig config; - - public MaintenanceUpdate(String appName, boolean enable) { - this.config = new RequestConfig().app(appName).with(Heroku.RequestKey.MaintenanceMode, enable ? "1" : "0"); - } - - @Override - public Http.Method getHttpMethod() { - return Http.Method.POST; - } - - @Override - public String getEndpoint() { - return Heroku.Resource.Maintenance.format(config.get(Heroku.RequestKey.AppName)); - } - - @Override - public boolean hasBody() { - return true; - } - - @Override - public String getBody() { - return HttpUtil.encodeParameters(config, Heroku.RequestKey.MaintenanceMode); - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); - } - - @Override - public Unit getResponse(byte[] in, int code) { - if (Http.Status.OK.equals(code)) - return Unit.unit; - else - throw new RequestFailedException("MaintenanceUpdate failed", code, in); - } -} diff --git a/heroku-api/src/main/java/com/heroku/api/request/ps/ProcessList.java b/heroku-api/src/main/java/com/heroku/api/request/ps/ProcessList.java deleted file mode 100644 index a924d59..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/ps/ProcessList.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.heroku.api.request.ps; - -import com.heroku.api.Heroku; -import com.heroku.api.Proc; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; -import com.heroku.api.request.Request; -import com.heroku.api.request.RequestConfig; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static com.heroku.api.parser.Json.parse; - -/** - * TODO: Javadoc - * - * @author Naaman Newbold - */ -public class ProcessList implements Request> { - - private final RequestConfig config; - - public ProcessList(String appName) { - config = new RequestConfig().app(appName); - } - - @Override - public Http.Method getHttpMethod() { - return Http.Method.GET; - } - - @Override - public String getEndpoint() { - return Heroku.Resource.Process.format(config.get(Heroku.RequestKey.AppName)); - } - - @Override - public boolean hasBody() { - return false; - } - - @Override - public String getBody() { - throw HttpUtil.noBody(); - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - return Collections.emptyMap(); - } - - @Override - public List getResponse(byte[] bytes, int status) { - if (status == Http.Status.OK.statusCode) { - return parse(bytes, getClass()); - } else { - throw new RequestFailedException("Process request failed.", status, bytes); - } - } -} diff --git a/heroku-api/src/main/java/com/heroku/api/request/ps/Restart.java b/heroku-api/src/main/java/com/heroku/api/request/ps/Restart.java deleted file mode 100644 index 81aaac9..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/ps/Restart.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.heroku.api.request.ps; - -import com.heroku.api.Heroku; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; -import com.heroku.api.request.Request; -import com.heroku.api.request.RequestConfig; -import com.heroku.api.response.Unit; - -import java.util.Map; - -/** - * TODO: Javadoc - * - * @author Naaman Newbold - */ -public class Restart implements Request { - - private final RequestConfig config; - - public Restart(String appName) { - this(new RequestConfig().app(appName)); - } - - Restart(RequestConfig config) { - this.config = config; - } - - @Override - public Http.Method getHttpMethod() { - return Http.Method.POST; - } - - @Override - public String getEndpoint() { - return Heroku.Resource.Restart.format(config.get(Heroku.RequestKey.AppName)); - } - - @Override - public boolean hasBody() { - return true; - } - - @Override - public String getBody() { - return HttpUtil.encodeParameters(config, Heroku.RequestKey.ProcessName, Heroku.RequestKey.ProcessType); - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); - } - - @Override - public Unit getResponse(byte[] bytes, int status) { - if (status == Http.Status.OK.statusCode) { - return Unit.unit; - } else { - throw new RequestFailedException("Unable to restart the process.", status, bytes); - } - } - - public static class NamedProcessRestart extends Restart { - public NamedProcessRestart(String appName, String processName) { - super(new RequestConfig().app(appName).with(Heroku.RequestKey.ProcessName, processName)); - } - } - - public static class ProcessTypeRestart extends Restart { - public ProcessTypeRestart(String appName, String processType) { - super(new RequestConfig().app(appName).with(Heroku.RequestKey.ProcessType, processType)); - } - } -} diff --git a/heroku-api/src/main/java/com/heroku/api/request/ps/Scale.java b/heroku-api/src/main/java/com/heroku/api/request/ps/Scale.java deleted file mode 100644 index 411ab6f..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/ps/Scale.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.heroku.api.request.ps; - -import com.heroku.api.Heroku; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; -import com.heroku.api.request.Request; -import com.heroku.api.request.RequestConfig; -import com.heroku.api.response.Unit; - -import java.util.Map; - -/** - * TODO: Javadoc - * - * @author Naaman Newbold - */ -public class Scale implements Request { - - private final RequestConfig config; - - public Scale(String appName, String processType, int quantity) { - config = new RequestConfig().app(appName).with(Heroku.RequestKey.ProcessType, processType).with(Heroku.RequestKey.Quantity, String.valueOf(quantity)); - } - - @Override - public Http.Method getHttpMethod() { - return Http.Method.POST; - } - - @Override - public String getEndpoint() { - return Heroku.Resource.Scale.format(config.get(Heroku.RequestKey.AppName)); - } - - @Override - public boolean hasBody() { - return true; - } - - @Override - public String getBody() { - return HttpUtil.encodeParameters(config, Heroku.RequestKey.ProcessType, Heroku.RequestKey.Quantity); - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); - } - - @Override - public Unit getResponse(byte[] bytes, int status) { - if (status == Http.Status.OK.statusCode) { - return Unit.unit; - } else if (status == Http.Status.FORBIDDEN.statusCode) { - throw HttpUtil.insufficientPrivileges(status, bytes); - } else if (status == Http.Status.UNPROCESSABLE_ENTITY.statusCode) { - throw new RequestFailedException("Invalid process type", status, bytes); - } else if (status == Http.Status.PAYMENT_REQUIRED.statusCode) { - throw new RequestFailedException("Payment is required for scaling this process. " + - "Please go to https://api.heroku.com and check your account details.", status, bytes); - } else { - throw new RequestFailedException("Error occurred while scaling.", status, bytes); - } - } -} diff --git a/heroku-api/src/main/java/com/heroku/api/request/releases/ListReleases.java b/heroku-api/src/main/java/com/heroku/api/request/releases/ListReleases.java index 288cee5..f654e4f 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/releases/ListReleases.java +++ b/heroku-api/src/main/java/com/heroku/api/request/releases/ListReleases.java @@ -34,7 +34,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.Releases.format(config.get(Heroku.RequestKey.AppName)); + return Heroku.Resource.Releases.format(config.getAppName()); } @Override @@ -47,6 +47,11 @@ public String getBody() { throw noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/releases/ReleaseInfo.java b/heroku-api/src/main/java/com/heroku/api/request/releases/ReleaseInfo.java index 8783128..55d8a2b 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/releases/ReleaseInfo.java +++ b/heroku-api/src/main/java/com/heroku/api/request/releases/ReleaseInfo.java @@ -33,7 +33,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.Release.format(config.get(Heroku.RequestKey.AppName), config.get(Heroku.RequestKey.Release)); + return Heroku.Resource.Release.format(config.getAppName(), config.get(Heroku.RequestKey.Release)); } @Override @@ -46,6 +46,11 @@ public String getBody() { throw noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/releases/Rollback.java b/heroku-api/src/main/java/com/heroku/api/request/releases/Rollback.java index 470fe23..938b8be 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/releases/Rollback.java +++ b/heroku-api/src/main/java/com/heroku/api/request/releases/Rollback.java @@ -1,24 +1,27 @@ package com.heroku.api.request.releases; import com.heroku.api.Heroku; +import com.heroku.api.Release; import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; import com.heroku.api.request.Request; import com.heroku.api.request.RequestConfig; +import java.util.Collections; import java.util.Map; +import static com.heroku.api.parser.Json.parse; + /** * TODO: Javadoc * * @author Naaman Newbold */ -public class Rollback implements Request { +public class Rollback implements Request { private final RequestConfig config; public Rollback(String appName, String releaseName) { - this.config = new RequestConfig().app(appName).with(Heroku.RequestKey.Rollback, releaseName); + this.config = new RequestConfig().app(appName).with(Heroku.RequestKey.Release, releaseName); } @Override @@ -28,7 +31,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.Releases.format(config.get(Heroku.RequestKey.AppName), config.get(Heroku.RequestKey.Rollback)); + return Heroku.Resource.Releases.format(config.getAppName(), config.get(Heroku.RequestKey.Release)); } @Override @@ -38,7 +41,12 @@ public boolean hasBody() { @Override public String getBody() { - return HttpUtil.encodeParameters(config, Heroku.RequestKey.Rollback); + return config.asJson(); + } + + @Override + public Map getBodyAsMap() { + return config.asMap(); } @Override @@ -48,13 +56,13 @@ public Http.Accept getResponseType() { @Override public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); + return Collections.emptyMap(); } @Override - public String getResponse(byte[] bytes, int status) { - if (status == Http.Status.OK.statusCode) { - return new String(bytes); + public Release getResponse(byte[] bytes, int status) { + if (status == Http.Status.CREATED.statusCode) { + return parse(bytes, getClass()); } throw new RequestFailedException("Unable to rollback.", status, bytes); } diff --git a/heroku-api/src/main/java/com/heroku/api/request/run/Run.java b/heroku-api/src/main/java/com/heroku/api/request/run/Run.java deleted file mode 100644 index 0e82288..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/run/Run.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.heroku.api.request.run; - - -import com.heroku.api.Heroku; -import com.heroku.api.Proc; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; -import com.heroku.api.parser.Json; -import com.heroku.api.request.Request; -import com.heroku.api.request.RequestConfig; - -import java.util.Map; - -public class Run implements Request { - - private RequestConfig config; - - - public Run(String app, String command) { - config = new RequestConfig().app(app).with(Heroku.RequestKey.Command, command); - } - - - public Run(String app, String command, boolean attach) { - this(app,command); - if(attach){ - config = config.with(Heroku.RequestKey.Attach, "true"); - } - } - - - @Override - public Http.Method getHttpMethod() { - return Http.Method.POST; - } - - - @Override - public String getEndpoint() { - return Heroku.Resource.Process.format(config.get(Heroku.RequestKey.AppName)); - } - - @Override - public boolean hasBody() { - return true; - } - - @Override - public String getBody() { - if (config.has(Heroku.RequestKey.Attach)) { - return HttpUtil.encodeParameters(config, Heroku.RequestKey.AppName, Heroku.RequestKey.Command, Heroku.RequestKey.Attach); - } else { - return HttpUtil.encodeParameters(config, Heroku.RequestKey.AppName, Heroku.RequestKey.Command); - } - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - Map map = Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); - //workaround should be removed when core is updated to not require this - map.put("User-Agent", "heroku-gem/2.4"); - return map; - } - - @Override - public RunResponse getResponse(byte[] bytes, int status) { - if (status == 200) { - Proc parsedProc = Json.parse(bytes, Proc.class); - return new RunResponse(parsedProc); - } else { - throw new RequestFailedException("Run Request Failed,", status, bytes); - } - } -} - \ No newline at end of file diff --git a/heroku-api/src/main/java/com/heroku/api/request/run/RunResponse.java b/heroku-api/src/main/java/com/heroku/api/request/run/RunResponse.java deleted file mode 100644 index 0ceb6e4..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/run/RunResponse.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.heroku.api.request.run; - - -import com.heroku.api.Proc; -import com.heroku.api.exception.HerokuAPIException; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLSocketFactory; -import java.io.*; -import java.net.Socket; - -public class RunResponse { - - private final Proc proc; - - public RunResponse(Proc proc) { - this.proc = proc; - } - - public Proc getProc() { - return proc; - } - - /** - * Should only be called if you started the process with attach enabled - * - * @return an InputStream that contains the output of your process. - */ - public InputStream attach() { - if(!proc.isAttached()) { - throw new IllegalStateException("The process was not started as attached, and has probably already executed. Check your app logs"); - } - try { - String host = proc.getRendezvousUrl().getHost(); - int port = proc.getRendezvousUrl().getPort(); - String secret = proc.getRendezvousUrl().getPath().substring(1); - SocketFactory socketFactory = SSLSocketFactory.getDefault(); - final Socket socket = socketFactory.createSocket(host, port); - // Create streams to securely send and receive data to the server - final InputStream in = socket.getInputStream(); - final OutputStream out = socket.getOutputStream(); - /*Write the secret to the server*/ - OutputStreamWriter owriter = new OutputStreamWriter(out); - final BufferedWriter writer = new BufferedWriter(owriter); - writer.write(secret); - writer.flush(); - InputStreamReader ireader = new InputStreamReader(in); - BufferedReader reader = new BufferedReader(ireader); - /*this line reads the string "rendezvous" */ - reader.readLine(); - return in; - } catch (IOException e) { - throw new HerokuAPIException("IOException while running process", e); - } - } - - -} diff --git a/heroku-api/src/main/java/com/heroku/api/request/sharing/CollabList.java b/heroku-api/src/main/java/com/heroku/api/request/sharing/CollabList.java index f730ac6..f8921ed 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/sharing/CollabList.java +++ b/heroku-api/src/main/java/com/heroku/api/request/sharing/CollabList.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; +import static com.heroku.api.http.HttpUtil.noBody; import static com.heroku.api.parser.Json.parse; /** @@ -34,7 +35,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.Collaborators.format(config.get(Heroku.RequestKey.AppName)); + return Heroku.Resource.Collaborators.format(config.getAppName()); } @Override @@ -47,6 +48,11 @@ public String getBody() { throw HttpUtil.noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingAdd.java b/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingAdd.java index 5f8d511..be56e04 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingAdd.java +++ b/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingAdd.java @@ -3,11 +3,11 @@ import com.heroku.api.Heroku; import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; import com.heroku.api.request.Request; import com.heroku.api.request.RequestConfig; import com.heroku.api.response.Unit; +import java.util.Collections; import java.util.Map; /** @@ -32,7 +32,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.Collaborators.format(config.get(Heroku.RequestKey.AppName)); + return Heroku.Resource.Collaborators.format(config.getAppName()); } @Override @@ -42,17 +42,22 @@ public boolean hasBody() { @Override public String getBody() { - return HttpUtil.encodeParameters(config, Heroku.RequestKey.Collaborator); + return config.asJson(); + } + + @Override + public Map getBodyAsMap() { + return config.asMap(); } @Override public Http.Accept getResponseType() { - return Http.Accept.TEXT; + return Http.Accept.JSON; } @Override public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); + return Collections.emptyMap(); } @Override diff --git a/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingRemove.java b/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingRemove.java index cfa3fc9..c0e7246 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingRemove.java +++ b/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingRemove.java @@ -11,6 +11,8 @@ import java.util.Collections; import java.util.Map; +import static com.heroku.api.http.HttpUtil.noBody; + /** * TODO: Javadoc * @@ -23,7 +25,9 @@ public class SharingRemove implements Request { private final RequestConfig config; public SharingRemove(String appName, String collaboratorEmail) { - this.config = new RequestConfig().app(appName).with(Heroku.RequestKey.Collaborator, collaboratorEmail); + this.config = new RequestConfig() + .with(Heroku.RequestKey.TransferAppName, appName) + .with(Heroku.RequestKey.Collaborator, collaboratorEmail); } @Override @@ -33,7 +37,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.Collaborator.format(config.get(Heroku.RequestKey.AppName), + return Heroku.Resource.Collaborator.format(config.get(Heroku.RequestKey.TransferAppName), HttpUtil.urlencode(config.get(Heroku.RequestKey.Collaborator), "Unable to encode the endpoint")); } @@ -44,12 +48,17 @@ public boolean hasBody() { @Override public String getBody() { - throw HttpUtil.noBody(); + throw noBody(); + } + + @Override + public Map getBodyAsMap() { + throw noBody(); } @Override public Http.Accept getResponseType() { - return Http.Accept.TEXT; + return Http.Accept.JSON; } @Override diff --git a/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingTransfer.java b/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingTransfer.java index 4611514..8b072f2 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingTransfer.java +++ b/heroku-api/src/main/java/com/heroku/api/request/sharing/SharingTransfer.java @@ -3,11 +3,11 @@ import com.heroku.api.Heroku; import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; import com.heroku.api.request.Request; import com.heroku.api.request.RequestConfig; import com.heroku.api.response.Unit; +import java.util.Collections; import java.util.Map; /** @@ -17,24 +17,22 @@ */ public class SharingTransfer implements Request { - // heroku.update(app, :transfer_owner => email) - // put("/apps/#{name}", :app => attributes).to_s - // http request body = app[transfer_owner]=jw%2Bdemo%40heroku.com - private final RequestConfig config; public SharingTransfer(String appName, String newOwnerEmail) { - this.config = new RequestConfig().app(appName).with(Heroku.RequestKey.TransferOwner, newOwnerEmail); + this.config = new RequestConfig(). + with(Heroku.RequestKey.TransferAppName, appName). + with(Heroku.RequestKey.TransferOwner, newOwnerEmail); } @Override public Http.Method getHttpMethod() { - return Http.Method.PUT; + return Http.Method.POST; } @Override public String getEndpoint() { - return Heroku.Resource.App.format(config.get(Heroku.RequestKey.AppName)); + return Heroku.Resource.AppTransfer.value; } @Override @@ -44,7 +42,12 @@ public boolean hasBody() { @Override public String getBody() { - return HttpUtil.encodeParameters(config, Heroku.RequestKey.TransferOwner); + return config.asJson(); + } + + @Override + public Map getBodyAsMap() { + return config.asMap(); } @Override @@ -54,12 +57,12 @@ public Http.Accept getResponseType() { @Override public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); + return Collections.emptyMap(); } @Override public Unit getResponse(byte[] in, int code) { - if (code == Http.Status.OK.statusCode) + if (code == Http.Status.CREATED.statusCode) return Unit.unit; else throw new RequestFailedException("SharingTransfer failed", code, in); diff --git a/heroku-api/src/main/java/com/heroku/api/request/stack/StackList.java b/heroku-api/src/main/java/com/heroku/api/request/stack/StackList.java index 32ac32e..ae59081 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/stack/StackList.java +++ b/heroku-api/src/main/java/com/heroku/api/request/stack/StackList.java @@ -31,7 +31,7 @@ public Http.Method getHttpMethod() { @Override public String getEndpoint() { - return Heroku.Resource.AppStack.format(config.get(Heroku.RequestKey.AppName)); + return Heroku.Resource.Stacks.value; } @Override @@ -44,6 +44,11 @@ public String getBody() { throw noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-api/src/main/java/com/heroku/api/request/stack/StackMigrate.java b/heroku-api/src/main/java/com/heroku/api/request/stack/StackMigrate.java deleted file mode 100644 index ae2d731..0000000 --- a/heroku-api/src/main/java/com/heroku/api/request/stack/StackMigrate.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.heroku.api.request.stack; - -import com.heroku.api.Heroku; -import com.heroku.api.exception.RequestFailedException; -import com.heroku.api.http.Http; -import com.heroku.api.http.HttpUtil; -import com.heroku.api.request.Request; -import com.heroku.api.request.RequestConfig; - -import java.util.Map; - -/** - * @author Naaman Newbold - */ -public class StackMigrate implements Request { - private final RequestConfig config; - - public StackMigrate(String appName, Heroku.Stack stack) { - this.config = new RequestConfig().app(appName).onStack(stack); - } - - @Override - public Http.Method getHttpMethod() { - return Http.Method.PUT; - } - - @Override - public String getEndpoint() { - return Heroku.Resource.AppStack.format(config.get(Heroku.RequestKey.AppName)); - } - - @Override - public boolean hasBody() { - return true; - } - - @Override - public String getBody() { - return config.get(Heroku.RequestKey.Stack); - } - - @Override - public Http.Accept getResponseType() { - return Http.Accept.JSON; - } - - @Override - public Map getHeaders() { - return Http.Header.Util.setHeaders(Http.ContentType.FORM_URLENCODED); - } - - @Override - public String getResponse(byte[] bytes, int status) { - if (Http.Status.OK.equals(status)) { - return HttpUtil.getUTF8String(bytes); - } else { - throw new RequestFailedException("Unable to migrate stacks.", status, bytes); - } - } -} diff --git a/heroku-api/src/main/java/com/heroku/api/request/user/UserInfo.java b/heroku-api/src/main/java/com/heroku/api/request/user/UserInfo.java index 207d204..ce1530f 100644 --- a/heroku-api/src/main/java/com/heroku/api/request/user/UserInfo.java +++ b/heroku-api/src/main/java/com/heroku/api/request/user/UserInfo.java @@ -46,6 +46,11 @@ public String getBody() { throw noBody(); } + @Override + public Map getBodyAsMap() { + throw noBody(); + } + @Override public Http.Accept getResponseType() { return Http.Accept.JSON; diff --git a/heroku-http-apache/pom.xml b/heroku-http-apache/pom.xml index 1f6920f..6696118 100644 --- a/heroku-http-apache/pom.xml +++ b/heroku-http-apache/pom.xml @@ -5,7 +5,7 @@ heroku-api-parent com.heroku.api - 0.17-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 @@ -27,6 +27,11 @@ httpcore ${apache.commons-httpclient.version} + + commons-codec + commons-codec + 1.10 + diff --git a/heroku-http-apache/src/main/java/com/heroku/api/connection/HttpClientConnection.java b/heroku-http-apache/src/main/java/com/heroku/api/connection/HttpClientConnection.java index 4bb4f77..2c0a851 100644 --- a/heroku-http-apache/src/main/java/com/heroku/api/connection/HttpClientConnection.java +++ b/heroku-http-apache/src/main/java/com/heroku/api/connection/HttpClientConnection.java @@ -4,29 +4,28 @@ import com.heroku.api.http.Http; import com.heroku.api.http.HttpUtil; import com.heroku.api.request.Request; +import org.apache.commons.codec.binary.Base64; import org.apache.http.*; -import org.apache.http.auth.*; -import org.apache.http.auth.params.AuthPNames; +import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.AuthState; +import org.apache.http.auth.Credentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.*; -import org.apache.http.client.params.AuthPolicy; -import org.apache.http.client.params.ClientPNames; -import org.apache.http.client.params.CookiePolicy; import org.apache.http.client.protocol.ClientContext; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; -import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.concurrent.*; @@ -37,7 +36,7 @@ public class HttpClientConnection implements FutureConnection { private URL endpoint = HttpUtil.toURL(ENDPOINT.value); - private DefaultHttpClient httpClient = getHttpClient(); + private CloseableHttpClient httpClient = createClient(); private volatile ExecutorService executorService; private Object lock = new Object(); @@ -71,8 +70,8 @@ public T execute(Request request, String key) { public T execute(Request request, Map exraHeaders, String key) { try { HttpRequestBase message = getHttpRequestBase(request.getHttpMethod(), ENDPOINT.value + request.getEndpoint()); - message.setHeader(Heroku.ApiVersion.HEADER, String.valueOf(Heroku.ApiVersion.v2.version)); - message.setHeader(request.getResponseType().getHeaderName(), request.getResponseType().getHeaderValue()); + message.setHeader(Heroku.ApiVersion.v3.getHeaderName(), Heroku.ApiVersion.v3.getHeaderValue()); + message.setHeader(Http.ContentType.JSON.getHeaderName(), Http.ContentType.JSON.getHeaderValue()); message.setHeader(Http.UserAgent.LATEST.getHeaderName(), Http.UserAgent.LATEST.getHeaderValue("httpclient")); for (Map.Entry header : exraHeaders.entrySet()) { @@ -87,15 +86,13 @@ public T execute(Request request, Map exraHeaders, String ((HttpEntityEnclosingRequestBase) message).setEntity(new StringEntity(request.getBody(), "UTF-8")); } - httpClient.getCredentialsProvider().setCredentials( - new AuthScope(endpoint.getHost(), endpoint.getPort()), - new UsernamePasswordCredentials("", key) - ); + if (key != null) { + message.setHeader("Authorization", "Basic " + Base64.encodeBase64String((":" + key).getBytes())); + } BasicHttpContext ctx = new BasicHttpContext(); ctx.setAttribute("preemptive-auth", new BasicScheme()); - httpClient.addRequestInterceptor(new PreemptiveAuth(), 0); HttpResponse httpResponse = httpClient.execute(message, ctx); return request.getResponse(HttpUtil.getBytes(httpResponse.getEntity().getContent()), httpResponse.getStatusLine().getStatusCode()); @@ -114,6 +111,8 @@ private HttpRequestBase getHttpRequestBase(Http.Method httpMethod, String endpoi return new HttpPost(endpoint); case DELETE: return new HttpDelete(endpoint); + case PATCH: + return new HttpPatch(endpoint); default: throw new UnsupportedOperationException(httpMethod + " is not a supported request type."); } @@ -141,18 +140,34 @@ public Thread newThread(Runnable runnable) { }); } - protected DefaultHttpClient getHttpClient() { - SSLSocketFactory ssf = new SSLSocketFactory(Heroku.herokuSSLContext()); - ThreadSafeClientConnManager ccm = new ThreadSafeClientConnManager(); - if (!Heroku.Config.ENDPOINT.isDefault()) { - ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); - SchemeRegistry sr = ccm.getSchemeRegistry(); - sr.register(new Scheme("https", ssf, 443)); + protected static CloseableHttpClient createClientWithProxy(String proxyStr) throws URISyntaxException { + URI proxyUri = new URI(proxyStr); + HttpHost proxy = new HttpHost(proxyUri.getHost(), proxyUri.getPort(), proxyUri.getScheme()); + DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); + return HttpClients.custom() + .setRoutePlanner(routePlanner) + .build(); + } + + protected static CloseableHttpClient createClient() { + String httpProxy = System.getenv("HTTP_PROXY"); + String httpsProxy = System.getenv("HTTPS_PROXY"); + + if (httpsProxy != null) { + try { + return createClientWithProxy(httpsProxy); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("HTTPS_PROXY is not valid!" , e); + } + } else if (httpProxy != null) { + try { + return createClientWithProxy(httpProxy); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("HTTP_PROXY is not valid!" , e); + } + } else { + return HttpClients.createDefault(); } - DefaultHttpClient defaultHttpClient = new DefaultHttpClient(ccm); - defaultHttpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES); - defaultHttpClient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF, Arrays.asList(AuthPolicy.BASIC)); - return defaultHttpClient; } diff --git a/heroku-http-finagle/pom.xml b/heroku-http-finagle/pom.xml index 250788c..f9a2aa8 100644 --- a/heroku-http-finagle/pom.xml +++ b/heroku-http-finagle/pom.xml @@ -5,7 +5,7 @@ heroku-api-parent com.heroku.api - 0.17-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 @@ -20,36 +20,36 @@ org.scala-lang scala-library - 2.9.1 + 2.11.8 compile com.twitter - finagle-core_2.9.1 + finagle-core_2.11 ${finagle.version} compile com.twitter - finagle-http_2.9.1 + finagle-http_2.11 ${finagle.version} compile com.twitter - util-core_2.9.1 + util-core_2.11 ${twitter.util.version} compile com.twitter - util-codec_2.9.1 + util-codec_2.11 ${twitter.util.version} compile io.netty - netty + netty-all ${netty.version} compile @@ -85,20 +85,20 @@ - 2.9.1 + 2.11.8 org.scala-lang scala-library - 2.9.1 + 2.11.8 org.scala-lang scala-compiler - 2.9.1 + 2.11.8 diff --git a/heroku-http-finagle/src/main/scala/com/heroku/api/connection/FinagleConnection.scala b/heroku-http-finagle/src/main/scala/com/heroku/api/connection/FinagleConnection.scala index cc5c4f3..68e0056 100644 --- a/heroku-http-finagle/src/main/scala/com/heroku/api/connection/FinagleConnection.scala +++ b/heroku-http-finagle/src/main/scala/com/heroku/api/connection/FinagleConnection.scala @@ -1,21 +1,19 @@ package com.heroku.api.connection -import java.lang.String import com.heroku.api.http._ -import com.twitter.finagle.Service -import org.jboss.netty.handler.codec.http._ +import com.twitter.finagle.{http, Service} import com.heroku.api.request.Request +import org.jboss.netty.buffer.ChannelBuffers import collection.JavaConversions._ import java.net.{InetSocketAddress, URL} -import com.twitter.finagle.http.Http -import org.jboss.netty.buffer.ChannelBuffers +import com.twitter.finagle.http.{Response, Http} import java.nio.charset.Charset import com.twitter.finagle.builder.ClientBuilder import com.heroku.api.http.Http.Method import com.heroku.api.http.Http.UserAgent import com.heroku.api.Heroku.ApiVersion import com.heroku.api.Heroku -import com.twitter.util.{Base64StringEncoder, Future} +import com.twitter.util.{Await, Base64StringEncoder, Future} import com.twitter.conversions.time._ import java.util @@ -36,7 +34,7 @@ class FinagleConnection(val host: String) extends TwitterFutureConnection { this(Heroku.Config.ENDPOINT.value) } - type HttpService = Service[HttpRequest, HttpResponse] + type HttpService = Service[http.Request, Response] val timeout = 60.seconds @volatile var client = newClient() @@ -44,51 +42,51 @@ class FinagleConnection(val host: String) extends TwitterFutureConnection { val hostHeader = getHostHeader - def execute[T](request: Request[T], key: String): T = executeAsync(request, key).get(timeout).get() + def execute[T](request: Request[T], key: String): T = Await.result(executeAsync(request, key), timeout) def executeAsync[T](request: Request[T], apiKey: String): Future[T] = executeAsync(request, mapAsJavaMap(Map.empty), apiKey) - def execute[T](request: Request[T], extraHeaders: util.Map[String, String], apiKey: String): T = executeAsync(request, extraHeaders, apiKey).get(timeout).get() + def execute[T](request: Request[T], extraHeaders: util.Map[String, String], apiKey: String): T = Await.result(executeAsync(request, extraHeaders, apiKey), timeout) def executeAsync[T](command: Request[T], extraHeaders:util.Map[String,String], key: String): Future[T] = { if (!client.isAvailable) { - client.release() + client.close() client = newClient() } client(toReq(command, extraHeaders, key)).map { resp => - command.getResponse(resp.getContent.array(), resp.getStatus.getCode) + command.getResponse(resp.contentString.getBytes("UTF-8"), resp.status.code) } } - def toReq(cmd: Request[_], extraHeaders: util.Map[String, String], key: String): HttpRequest = { + def toReq(cmd: Request[_], extraHeaders: util.Map[String, String], key: String): com.twitter.finagle.http.Request = { val method = cmd.getHttpMethod match { - case Method.GET => HttpMethod.GET - case Method.PUT => HttpMethod.PUT - case Method.POST => HttpMethod.POST - case Method.DELETE => HttpMethod.DELETE + case Method.GET => http.Method.Get + case Method.PUT => http.Method.Put + case Method.POST => http.Method.Post + case Method.DELETE => http.Method.Delete + case Method.PATCH => http.Method.Patch } - val req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, method, cmd.getEndpoint) - req.addHeader(cmd.getResponseType.getHeaderName, cmd.getResponseType.getHeaderValue) - req.addHeader(ApiVersion.HEADER, ApiVersion.v2.getHeaderValue) - req.addHeader(HttpHeaders.Names.HOST, hostHeader) - req.addHeader(UserAgent.LATEST.getHeaderName, UserAgent.LATEST.getHeaderValue("finagle")) + val req = http.Request(method, cmd.getEndpoint) + req.accept = cmd.getResponseType.getHeaderValue + req.headerMap.add(ApiVersion.HEADER, ApiVersion.v3.getHeaderValue) + req.host = hostHeader + req.headerMap.add(UserAgent.LATEST.getHeaderName, UserAgent.LATEST.getHeaderValue("finagle")) if (key != null) { - req.addHeader(HttpHeaders.Names.AUTHORIZATION, "Basic " + Base64StringEncoder.encode((":" + key).getBytes("UTF-8"))) + req.authorization = "Basic " + Base64StringEncoder.encode((":" + key).getBytes("UTF-8")) } (cmd.getHeaders ++ extraHeaders) foreach { _ match { - case (k, v) => req.addHeader(k, v) + case (k, v) => req.headerMap.add(k, v) } } if (cmd.hasBody) { val body = ChannelBuffers.copiedBuffer(cmd.getBody, Charset.forName("UTF-8")) - req.setContent(body) - req.setHeader(HttpHeaders.Names.CONTENT_LENGTH, body.readableBytes().toString) + req.write(body) + req.contentLength = body.readableBytes() } - req } @@ -105,7 +103,7 @@ class FinagleConnection(val host: String) extends TwitterFutureConnection { hhost + port } - def newClient(): HttpService = { + def newClient(): Service[http.Request, Response] = { val endpoint = HttpUtil.toURL(host) var builder = ClientBuilder() .codec(Http()) @@ -122,7 +120,7 @@ class FinagleConnection(val host: String) extends TwitterFutureConnection { } def close() { - client.release() + client.close() } diff --git a/heroku-http-jersey-client/pom.xml b/heroku-http-jersey-client/pom.xml index 12eb165..2303801 100644 --- a/heroku-http-jersey-client/pom.xml +++ b/heroku-http-jersey-client/pom.xml @@ -5,7 +5,7 @@ heroku-api-parent com.heroku.api - 0.17-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 @@ -18,9 +18,14 @@ ${project.parent.version} - com.sun.jersey + org.glassfish.jersey.core jersey-client - ${com.sun.jersey.version} + ${jersey.version} + + + org.glassfish.jersey.connectors + jersey-apache-connector + ${jersey.version} \ No newline at end of file diff --git a/heroku-http-jersey-client/src/main/java/com/heroku/api/connection/JerseyClientAsyncConnection.java b/heroku-http-jersey-client/src/main/java/com/heroku/api/connection/JerseyClientAsyncConnection.java index b0b0be5..f88f2b4 100644 --- a/heroku-http-jersey-client/src/main/java/com/heroku/api/connection/JerseyClientAsyncConnection.java +++ b/heroku-http-jersey-client/src/main/java/com/heroku/api/connection/JerseyClientAsyncConnection.java @@ -4,15 +4,13 @@ import com.heroku.api.exception.HerokuAPIException; import com.heroku.api.http.Http; import com.heroku.api.request.Request; -import com.sun.jersey.api.client.AsyncWebResource; -import com.sun.jersey.api.client.Client; -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.config.ClientConfig; -import com.sun.jersey.api.client.config.DefaultClientConfig; -import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; -import com.sun.jersey.api.client.filter.LoggingFilter; -import com.sun.jersey.core.util.FeaturesAndProperties; +import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.spi.ConnectorProvider; +import org.glassfish.jersey.internal.util.Base64; +import javax.ws.rs.client.*; +import javax.ws.rs.core.Response; import java.util.Collections; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -27,7 +25,7 @@ public class JerseyClientAsyncConnection implements AsyncConnection> { private final Client client; public JerseyClientAsyncConnection() { - this(Client.create()); + this(ClientBuilder.newClient(createClientConfig())); } public JerseyClientAsyncConnection(Client client) { @@ -36,25 +34,24 @@ public JerseyClientAsyncConnection(Client client) { @Override public Future executeAsync(final Request request, final Map extraHeaders, final String apiKey) { - final AsyncWebResource resource = client.asyncResource(ENDPOINT.value + request.getEndpoint()); - resource.addFilter(new HTTPBasicAuthFilter("", apiKey)); - - final AsyncWebResource.Builder builder = resource.getRequestBuilder(); + final Invocation.Builder builder = client.target(ENDPOINT.value + request.getEndpoint()).request(); + builder.header("Authorization", Base64.encodeAsString((":" + apiKey).getBytes())); builder.header(request.getResponseType().getHeaderName(), request.getResponseType().getHeaderValue()); - builder.header(Heroku.ApiVersion.HEADER, String.valueOf(Heroku.ApiVersion.v2.version)); + builder.header(Heroku.ApiVersion.v3.getHeaderName(), Heroku.ApiVersion.v3.getHeaderValue()); + builder.header(Http.ContentType.JSON.getHeaderName(), Http.ContentType.JSON.getHeaderValue()); builder.header(Http.UserAgent.LATEST.getHeaderName(), Http.UserAgent.LATEST.getHeaderValue("jersey-client")); + for (Map.Entry header : request.getHeaders().entrySet()) { builder.header(header.getKey(), header.getValue()); } - if (request.hasBody()) { - builder.entity(request.getBody()); - } + final AsyncInvoker invoker = builder.async(); - final Future futureResponse = builder.method(request.getHttpMethod().name(), ClientResponse.class); + final Future futureResponse = request.hasBody() ? + invoker.method(request.getHttpMethod().name(), Entity.json(request.getBodyAsMap())) : + invoker.method(request.getHttpMethod().name()); - // transform Future to Future return new Future() { public boolean cancel(boolean mayInterruptIfRunning) { return futureResponse.cancel(mayInterruptIfRunning); @@ -76,8 +73,8 @@ public T get(long timeout, TimeUnit unit) throws InterruptedException, Execution return handleResponse(futureResponse.get(timeout, unit)); } - private T handleResponse(ClientResponse r) { - return request.getResponse(r.getEntity(byte[].class), r.getStatus()); + private T handleResponse(Response r) { + return request.getResponse(r.readEntity(byte[].class), r.getStatus()); } }; } @@ -105,7 +102,27 @@ public T execute(Request request, Map extraHeaders, Strin @Override public void close() { - client.destroy(); + client.close(); + } + + private Future invoke(final Request request, final Invocation.Builder builder) { + if (Http.Method.PATCH.equals(request.getHttpMethod())) { + final AsyncInvoker invoker = builder.async(); + builder.property("X-HTTP-Method-Override", "PATCH"); + return invoker.method("POST", Entity.json(request.getBodyAsMap())); + } else { + final AsyncInvoker invoker = builder.async(); + return request.hasBody() ? + invoker.method(request.getHttpMethod().name(), Entity.json(request.getBodyAsMap())) : + invoker.method(request.getHttpMethod().name()); + } + } + + private static ClientConfig createClientConfig() { + ClientConfig clientConfig = new ClientConfig(); + ConnectorProvider connectorProvider = new ApacheConnectorProvider(); + clientConfig.connectorProvider(connectorProvider); + return clientConfig; } public static class Provider implements ConnectionProvider { diff --git a/heroku-http-ning-async/pom.xml b/heroku-http-ning-async/pom.xml index 623b296..48b6562 100644 --- a/heroku-http-ning-async/pom.xml +++ b/heroku-http-ning-async/pom.xml @@ -5,7 +5,7 @@ heroku-api-parent com.heroku.api - 0.17-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 @@ -19,7 +19,7 @@ ${project.parent.version} - com.ning + org.asynchttpclient async-http-client ${ning.async.version} diff --git a/heroku-http-ning-async/src/main/java/com/heroku/api/connection/AsyncHttpClientConnection.java b/heroku-http-ning-async/src/main/java/com/heroku/api/connection/AsyncHttpClientConnection.java index d35d215..f405de7 100644 --- a/heroku-http-ning-async/src/main/java/com/heroku/api/connection/AsyncHttpClientConnection.java +++ b/heroku-http-ning-async/src/main/java/com/heroku/api/connection/AsyncHttpClientConnection.java @@ -5,8 +5,8 @@ import com.heroku.api.exception.RequestFailedException; import com.heroku.api.http.Http; import com.heroku.api.request.Request; -import com.ning.http.client.*; -import com.ning.http.util.Base64; +import org.asynchttpclient.*; +import org.asynchttpclient.util.Base64; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -16,28 +16,14 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; - - - public class AsyncHttpClientConnection implements ListenableFutureConnection { - private AsyncHttpClient httpClient = getHttpClient(); - - - protected AsyncHttpClient getHttpClient() { - AsyncHttpClientConfig.Builder builder = new AsyncHttpClientConfig.Builder(); - // TODO: fix the handling of hostname verification - if (!Heroku.Config.ENDPOINT.isDefault()) { - builder.setSSLContext(Heroku.herokuSSLContext()); - builder.setHostnameVerifier(Heroku.herokuHostnameVerifier()); - } - return new AsyncHttpClient(builder.build()); - } + private AsyncHttpClient httpClient = new DefaultAsyncHttpClient(); - private com.ning.http.client.Request buildRequest(Request req, Map extraHeaders, String key) { - AsyncHttpClient.BoundRequestBuilder builder = prepareRequest(req); - builder.setHeader(Heroku.ApiVersion.HEADER, String.valueOf(Heroku.ApiVersion.v2.version)); - builder.setHeader(req.getResponseType().getHeaderName(), req.getResponseType().getHeaderValue()); + private org.asynchttpclient.Request buildRequest(Request req, Map extraHeaders, String key) { + BoundRequestBuilder builder = prepareRequest(req); + builder.setHeader(Heroku.ApiVersion.v3.getHeaderName(), String.valueOf(Heroku.ApiVersion.v3.getHeaderValue())); + builder.setHeader(Http.ContentType.JSON.getHeaderName(), Http.ContentType.JSON.getHeaderValue()); builder.setHeader(Http.UserAgent.LATEST.getHeaderName(), Http.UserAgent.LATEST.getHeaderValue("asynchttpclient")); for (Map.Entry entry : extraHeaders.entrySet()) { builder.setHeader(entry.getKey(), entry.getValue()); @@ -59,7 +45,7 @@ private com.ning.http.client.Request buildRequest(Request req, Map request) { + private BoundRequestBuilder prepareRequest(Request request) { String requestEndpoint = Heroku.Config.ENDPOINT.value + request.getEndpoint(); switch (request.getHttpMethod()) { case GET: @@ -70,6 +56,8 @@ private AsyncHttpClient.BoundRequestBuilder prepareRequest(Request request) { return httpClient.preparePut(requestEndpoint); case DELETE: return httpClient.prepareDelete(requestEndpoint); + case PATCH: + return httpClient.preparePatch(requestEndpoint); default: throw new UnsupportedOperationException(request.getHttpMethod().name() + " is not supported"); } @@ -78,18 +66,14 @@ private AsyncHttpClient.BoundRequestBuilder prepareRequest(Request request) { @Override public ListenableFuture executeAsync(final Request request, final Map extraHeaders, String key) { - com.ning.http.client.Request asyncRequest = buildRequest(request, extraHeaders, key); + org.asynchttpclient.Request asyncRequest = buildRequest(request, extraHeaders, key); AsyncCompletionHandler handler = new AsyncCompletionHandler() { @Override public T onCompleted(Response response) throws Exception { - return request.getResponse(response.getResponseBody("UTF-8").getBytes(), response.getStatusCode()); + return request.getResponse(response.getResponseBody().getBytes(), response.getStatusCode()); } }; - try { - return httpClient.executeRequest(asyncRequest, handler); - } catch (IOException e) { - throw new HerokuAPIException("IOException while executing request", e); - } + return httpClient.executeRequest(asyncRequest, handler); } @Override @@ -109,8 +93,12 @@ public T execute(Request req, Map extraHeaders, String key } catch (InterruptedException e) { throw new HerokuAPIException("request interrupted", e); } catch (ExecutionException e) { - if (e.getCause() != null && e.getCause().getCause() instanceof RequestFailedException) { + if (e.getCause() instanceof RequestFailedException) { + throw (RequestFailedException) e.getCause(); + } else if (e.getCause() != null && e.getCause().getCause() instanceof RequestFailedException) { throw (RequestFailedException) e.getCause().getCause(); + } else if (e.getCause().getCause() != null && e.getCause().getCause().getCause() instanceof RequestFailedException) { + throw (RequestFailedException) e.getCause().getCause().getCause(); } throw new HerokuAPIException("execution exception", e.getCause()); } catch (TimeoutException e) { @@ -121,7 +109,11 @@ public T execute(Request req, Map extraHeaders, String key @Override public void close() { - httpClient.close(); + try { + httpClient.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } } diff --git a/heroku-http-ning-async/src/main/java/com/heroku/api/connection/ListenableFutureConnection.java b/heroku-http-ning-async/src/main/java/com/heroku/api/connection/ListenableFutureConnection.java index 699b523..a3555e9 100644 --- a/heroku-http-ning-async/src/main/java/com/heroku/api/connection/ListenableFutureConnection.java +++ b/heroku-http-ning-async/src/main/java/com/heroku/api/connection/ListenableFutureConnection.java @@ -1,7 +1,7 @@ package com.heroku.api.connection; import com.heroku.api.request.Request; -import com.ning.http.client.ListenableFuture; +import org.asynchttpclient.ListenableFuture; public interface ListenableFutureConnection extends AsyncConnection> { diff --git a/heroku-http-play_2.10/pom.xml b/heroku-http-play_2.10/pom.xml deleted file mode 100644 index 592745a..0000000 --- a/heroku-http-play_2.10/pom.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - heroku-api-parent - com.heroku.api - 0.17-SNAPSHOT - - 4.0.0 - - heroku-http-play_2.10 - - - - typesafe - http://repo.typesafe.com/typesafe/maven-releases/ - - - - - - com.heroku.api - heroku-api - ${project.parent.version} - - - play - play_2.10 - 2.1.0 - - - org.scala-lang - scala-library - 2.10.0 - - - - - - - - net.alchim31.maven - scala-maven-plugin - - - scala-compile-first - process-resources - - add-source - compile - - - - scala-test-compile - process-test-resources - - testCompile - - - - - - 2.10.0 - - - - - org.scala-lang - scala-library - 2.10.0 - - - - org.scala-lang - scala-compiler - 2.10.0 - - - - - - - org.springframework.build.aws - org.springframework.build.aws.maven - 3.0.0.RELEASE - - - - - - diff --git a/heroku-http-play_2.10/src/main/resources/META-INF/services/com.heroku.api.connection.ConnectionProvider b/heroku-http-play_2.10/src/main/resources/META-INF/services/com.heroku.api.connection.ConnectionProvider deleted file mode 100644 index 3a3a97a..0000000 --- a/heroku-http-play_2.10/src/main/resources/META-INF/services/com.heroku.api.connection.ConnectionProvider +++ /dev/null @@ -1 +0,0 @@ -com.heroku.api.connection.PlayWSConnection$Provider \ No newline at end of file diff --git a/heroku-http-play_2.10/src/main/scala/com/heroku/api/connection/PlayWSConnection.scala b/heroku-http-play_2.10/src/main/scala/com/heroku/api/connection/PlayWSConnection.scala deleted file mode 100644 index d64d0ad..0000000 --- a/heroku-http-play_2.10/src/main/scala/com/heroku/api/connection/PlayWSConnection.scala +++ /dev/null @@ -1,76 +0,0 @@ -package com.heroku.api.connection - -import com.heroku.api.Heroku -import com.heroku.api.http.Http -import play.api.libs.ws.{Response, WS} -import collection.JavaConverters._ -import com.ning.http.client.Realm -import com.heroku.api.request.Request -import concurrent.{Await, Future} -import play.api.libs.concurrent.Execution.Implicits._ - -import scala.concurrent.duration._ -import java.util.{Map => JMap, Collections} - -trait PlayConnection extends AsyncConnection[Future[_]] { - def executeAsync[T](request: Request[T], apiKey: String): Future[T] - - def execute[T](request: Request[T], apiKey: String): T - - def executeAsync[T](request: Request[T], extraHeaders: JMap[String, String], apiKey: String): Future[T] - - def execute[T](request: Request[T], extraHeaders: JMap[String, String], apiKey: String): T -} - -class PlayWSConnection(val host: String) extends PlayConnection { - - def this() { - this(Heroku.Config.ENDPOINT.value) - } - - def executeAsync[T](request: Request[T], extraHeaders:JMap[String,String], key: String): Future[T] = { - - val path = request.getEndpoint - var url = WS.url(host + path) - .withHeaders(Heroku.ApiVersion.HEADER -> Heroku.ApiVersion.v2.getHeaderValue) - .withHeaders(Http.UserAgent.LATEST.getHeaderName -> Http.UserAgent.LATEST.getHeaderValue("playws")) - .withHeaders(request.getResponseType.getHeaderName -> request.getResponseType.getHeaderValue) - .withHeaders(request.getHeaders.asScala.toArray: _*).withHeaders(extraHeaders.asScala.toArray:_*) - - if (key != null) { - url = url.withAuth("", key, Realm.AuthScheme.BASIC) - } - - val body = if (request.hasBody) request.getBody else "" - - request.getHttpMethod match { - case Http.Method.GET => toResponse(request, url.get()) - case Http.Method.PUT => toResponse(request, url.put(body)) - case Http.Method.POST => toResponse(request, url.post(body)) - case Http.Method.DELETE => toResponse(request, url.delete()) - } - - } - - - def executeAsync[T](request: Request[T], apiKey: String): Future[T] = executeAsync(request, Map.empty[String,String].asJava, apiKey) - - def execute[T](request: Request[T], apiKey: String): T = execute(request, Map.empty[String,String].asJava, apiKey) - - def toResponse[T](req: Request[T], p: Future[Response]): Future[T] = p.map(res => req.getResponse(res.body.getBytes, res.status)) - - def execute[T](request: Request[T], extraHeaders: JMap[String, String], key: String): T = Await.result(executeAsync(request, extraHeaders, key), Duration(60, SECONDS)) - - def close() {} -} - -object PlayWSConnection { - - def apply(): PlayWSConnection = new PlayWSConnection() - - class Provider extends ConnectionProvider { - - def getConnection(): Connection = PlayWSConnection() - } - -} diff --git a/heroku-http-play_2.9.1/pom.xml b/heroku-http-play_2.9.1/pom.xml deleted file mode 100644 index 62ab6eb..0000000 --- a/heroku-http-play_2.9.1/pom.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - heroku-api-parent - com.heroku.api - 0.17-SNAPSHOT - - 4.0.0 - - heroku-http-play_2.9.1 - - - - typesafe - http://repo.typesafe.com/typesafe/maven-releases/ - - - - - - com.heroku.api - heroku-api - ${project.parent.version} - - - play - play_2.9.1 - 2.0 - - - org.scala-lang - scala-library - 2.9.1 - - - - - - - - net.alchim31.maven - scala-maven-plugin - - - scala-compile-first - process-resources - - add-source - compile - - - - scala-test-compile - process-test-resources - - testCompile - - - - - - 2.9.1 - - - - - org.scala-lang - scala-library - 2.9.1 - - - - org.scala-lang - scala-compiler - 2.9.1 - - - - - - - org.springframework.build.aws - org.springframework.build.aws.maven - 3.0.0.RELEASE - - - - - - diff --git a/heroku-http-play_2.9.1/src/main/resources/META-INF/services/com.heroku.api.connection.ConnectionProvider b/heroku-http-play_2.9.1/src/main/resources/META-INF/services/com.heroku.api.connection.ConnectionProvider deleted file mode 100644 index 3a3a97a..0000000 --- a/heroku-http-play_2.9.1/src/main/resources/META-INF/services/com.heroku.api.connection.ConnectionProvider +++ /dev/null @@ -1 +0,0 @@ -com.heroku.api.connection.PlayWSConnection$Provider \ No newline at end of file diff --git a/heroku-http-play_2.9.1/src/main/scala/com/heroku/api/connection/PlayWSConnection.scala b/heroku-http-play_2.9.1/src/main/scala/com/heroku/api/connection/PlayWSConnection.scala deleted file mode 100644 index 37b22f5..0000000 --- a/heroku-http-play_2.9.1/src/main/scala/com/heroku/api/connection/PlayWSConnection.scala +++ /dev/null @@ -1,73 +0,0 @@ -package com.heroku.api.connection - -import play.api.libs.concurrent.Promise -import com.heroku.api.Heroku -import com.heroku.api.http.Http -import play.api.libs.ws.{Response, WS} -import collection.JavaConverters._ -import com.ning.http.client.Realm -import com.heroku.api.request.Request -import java.util.concurrent.TimeUnit -import java.util.{Map =>JMap, Collections} - -trait PlayConnection extends AsyncConnection[Promise[_]] { - def executeAsync[T](request: Request[T], apiKey: String): Promise[T] - - def execute[T](request: Request[T], apiKey: String): T - - def executeAsync[T](request: Request[T], extraHeaders: JMap[String, String], apiKey: String): Promise[T] - - def execute[T](request: Request[T], extraHeaders: JMap[String, String], apiKey: String): T -} - -class PlayWSConnection(val host: String) extends PlayConnection { - - def this() { - this(Heroku.Config.ENDPOINT.value) - } - - def executeAsync[T](request: Request[T], extraHeaders: JMap[String,String], key: String): Promise[T] = { - - val path = request.getEndpoint - var url = WS.url(host + path) - .withHeaders(Heroku.ApiVersion.HEADER -> Heroku.ApiVersion.v2.getHeaderValue) - .withHeaders(Http.UserAgent.LATEST.getHeaderName -> Http.UserAgent.LATEST.getHeaderValue("playws")) - .withHeaders(request.getResponseType.getHeaderName -> request.getResponseType.getHeaderValue) - .withHeaders(request.getHeaders.asScala.toArray: _*).withHeaders(extraHeaders.asScala.toArray:_*) - - if (key != null) { - url = url.withAuth("", key, Realm.AuthScheme.BASIC) - } - - val body = if (request.hasBody) request.getBody else "" - - request.getHttpMethod match { - case Http.Method.GET => toResponse(request, url.get()) - case Http.Method.PUT => toResponse(request, url.put(body)) - case Http.Method.POST => toResponse(request, url.post(body)) - case Http.Method.DELETE => toResponse(request, url.delete()) - } - - } - - def executeAsync[T](request: Request[T], apiKey: String): Promise[T] = executeAsync(request, Collections.emptyMap[String,String], apiKey) - - def execute[T](request: Request[T], apiKey: String): T = execute(request, Collections.emptyMap[String,String], apiKey) - - def toResponse[T](req: Request[T], p: Promise[Response]): Promise[T] = p.map(res => req.getResponse(res.body.getBytes, res.status)) - - def execute[T](request: Request[T], extraHeaders: JMap[String,String], key: String): T = executeAsync(request, key).await(60, TimeUnit.SECONDS).get - - def close() {} -} - -object PlayWSConnection { - - def apply(): PlayWSConnection = new PlayWSConnection() - - class Provider extends ConnectionProvider { - - def getConnection(): Connection = PlayWSConnection() - } - -} diff --git a/heroku-json-gson/pom.xml b/heroku-json-gson/pom.xml index 43adc10..70c22a1 100644 --- a/heroku-json-gson/pom.xml +++ b/heroku-json-gson/pom.xml @@ -5,7 +5,7 @@ heroku-api-parent com.heroku.api - 0.17-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 diff --git a/heroku-json-gson/src/main/java/com/heroku/api/parser/GsonParser.java b/heroku-json-gson/src/main/java/com/heroku/api/parser/GsonParser.java index 39f1d4f..3506296 100644 --- a/heroku-json-gson/src/main/java/com/heroku/api/parser/GsonParser.java +++ b/heroku-json-gson/src/main/java/com/heroku/api/parser/GsonParser.java @@ -2,6 +2,7 @@ import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.stream.JsonReader; import java.io.ByteArrayInputStream; @@ -12,6 +13,14 @@ public class GsonParser implements Parser { + @Override + public String encode(Object object) { + GsonBuilder builder = new GsonBuilder(); + + Gson gson = builder.create(); + return gson.toJson(object); + } + @Override public T parse(byte[] data, final Type type) { Reader dataReader = getReader(data); diff --git a/heroku-json-jackson/pom.xml b/heroku-json-jackson/pom.xml index f7ea7dc..ccaa8c4 100644 --- a/heroku-json-jackson/pom.xml +++ b/heroku-json-jackson/pom.xml @@ -5,7 +5,7 @@ heroku-api-parent com.heroku.api - 0.17-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 @@ -28,6 +28,14 @@ jackson-core-asl ${jackson.version} + + + + org.testng + testng + ${testng.version} + test + \ No newline at end of file diff --git a/heroku-json-jackson/src/main/java/com/heroku/api/parser/JacksonParser.java b/heroku-json-jackson/src/main/java/com/heroku/api/parser/JacksonParser.java index de9dc8a..b87a339 100644 --- a/heroku-json-jackson/src/main/java/com/heroku/api/parser/JacksonParser.java +++ b/heroku-json-jackson/src/main/java/com/heroku/api/parser/JacksonParser.java @@ -1,6 +1,7 @@ package com.heroku.api.parser; import com.heroku.api.exception.ParseException; +import org.codehaus.jackson.annotate.JsonAutoDetect; import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.type.JavaType; @@ -9,11 +10,25 @@ import java.lang.reflect.Type; /** - * TODO: Javadoc * * @author Naaman Newbold */ public class JacksonParser implements Parser { + @Override + public String encode(Object object) { + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibilityChecker(mapper.getSerializationConfig().getDefaultVisibilityChecker() + .withFieldVisibility(JsonAutoDetect.Visibility.NONE) + .withGetterVisibility(JsonAutoDetect.Visibility.ANY) + .withSetterVisibility(JsonAutoDetect.Visibility.NONE) + .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)); + try { + return mapper.writeValueAsString(object); + } catch (IOException e) { + throw new ParseException("Unable to encode object.", e); + } + } + @Override public T parse(byte[] data, Type type) { ObjectMapper mapper = new ObjectMapper(); @@ -25,5 +40,4 @@ public T parse(byte[] data, Type type) { throw new ParseException("Unable to parse data.", e); } } - } diff --git a/heroku-json-jackson/src/test/java/com/heroku/api/parser/JsonTest.java b/heroku-json-jackson/src/test/java/com/heroku/api/parser/JsonTest.java new file mode 100644 index 0000000..42cceb3 --- /dev/null +++ b/heroku-json-jackson/src/test/java/com/heroku/api/parser/JsonTest.java @@ -0,0 +1,23 @@ +package com.heroku.api.parser; + +import com.heroku.api.App; +import com.heroku.api.Heroku; +import com.heroku.api.request.app.AppCreate; +import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; + +/** + * @author Joe Kutner + */ +public class JsonTest { + @Test + public void parseRequest() { + App app = new App().named("foobar").on(Heroku.Stack.Cedar14); + AppCreate request = new AppCreate(app); + + String json = request.getBody(); + App deserializedApp = Json.parse(json.getBytes(), App.class); + + assertEquals(deserializedApp.getName(), "foobar"); + } +} \ No newline at end of file diff --git a/heroku-json-jersey-client/pom.xml b/heroku-json-jersey-client/pom.xml index 8e879db..5882971 100644 --- a/heroku-json-jersey-client/pom.xml +++ b/heroku-json-jersey-client/pom.xml @@ -5,7 +5,7 @@ heroku-api-parent com.heroku.api - 0.17-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 @@ -18,9 +18,19 @@ ${project.parent.version} - com.sun.jersey - jersey-json - ${com.sun.jersey.version} + org.glassfish.jersey.media + jersey-media-json-jackson + ${jersey.version} + + + org.codehaus.jackson + jackson-mapper-asl + ${jackson.version} + + + org.codehaus.jackson + jackson-core-asl + ${jackson.version} diff --git a/heroku-json-jersey-client/src/main/java/com/heroku/api/parser/JerseyClientJsonParser.java b/heroku-json-jersey-client/src/main/java/com/heroku/api/parser/JerseyClientJsonParser.java index 1b64b76..4485a8e 100644 --- a/heroku-json-jersey-client/src/main/java/com/heroku/api/parser/JerseyClientJsonParser.java +++ b/heroku-json-jersey-client/src/main/java/com/heroku/api/parser/JerseyClientJsonParser.java @@ -1,9 +1,9 @@ package com.heroku.api.parser; import com.heroku.api.exception.ParseException; +import org.codehaus.jackson.annotate.JsonAutoDetect; import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.type.TypeFactory; import org.codehaus.jackson.type.JavaType; import java.io.IOException; @@ -13,13 +13,29 @@ * @author Ryan Brainard */ public class JerseyClientJsonParser implements Parser { + + @Override + public String encode(Object object) { + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibilityChecker(mapper.getSerializationConfig().getDefaultVisibilityChecker() + .withFieldVisibility(JsonAutoDetect.Visibility.NONE) + .withGetterVisibility(JsonAutoDetect.Visibility.ANY) + .withSetterVisibility(JsonAutoDetect.Visibility.NONE) + .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)); + try { + return mapper.writeValueAsString(object); + } catch (IOException e) { + throw new ParseException("Unable to encode object.", e); + } + } + @Override public T parse(byte[] data, Type type) { ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); - JavaType javaType = TypeFactory.type(type); + JavaType javaType = mapper.constructType(type); try { - return mapper.readValue(data, 0, data.length, javaType); + return mapper.readValue(data, javaType); } catch (IOException e) { throw new ParseException("Unable to parse data.", e); } diff --git a/pom.xml b/pom.xml index 1ef3524..c76082f 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.heroku.api heroku-api-parent pom - 0.17-SNAPSHOT + 3.0.0-SNAPSHOT heroku.jar Heroku API Java Client @@ -52,6 +52,11 @@ James Ward jw@heroku.com + + codefinger + Joe Kutner + joe@heroku.com + @@ -76,8 +81,6 @@ heroku-http-finagle heroku-http-ning-async heroku-api-integration-tests - heroku-http-play_2.9.1 - heroku-http-play_2.10 heroku-http-jersey-client heroku-json-jersey-client @@ -85,12 +88,12 @@ ${project.version} 6.0 - 3.0 - 4.1.2 - 1.11.1 - 1.12.13 - 1.7.0 - 3.3.1.Final + 4.1.0 + 4.4 + 6.39.0 + 6.38.0 + 2.0.20 + 4.1.6.Final 1.9.10 1.7.1 1.6 @@ -98,7 +101,7 @@ 1.2 2.7 3.1.0 - 1.8 + 2.24