From 4c6e48fc47a50cfc9cde990f5bea56e078886333 Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Sat, 29 Sep 2012 16:27:32 +0200 Subject: [PATCH] Added SearchOptions to all search methods Added a SearchOptions parameter to all search methods in ChefApi. This way every search can be customized and limited. Fixes #1. This is still a WIP until the appropriate live tests are added. --- .../main/java/org/jclouds/chef/ChefApi.java | 40 ++++++-- .../java/org/jclouds/chef/ChefAsyncApi.java | 33 ++++++- .../chef/internal/BaseChefService.java | 15 ++- .../jclouds/chef/options/SearchOptions.java | 99 +++++++++++++++++++ .../chef/test/TransientChefAsyncApi.java | 26 ++++- .../org/jclouds/chef/ChefAsyncApiTest.java | 64 +++++++++++- 6 files changed, 252 insertions(+), 25 deletions(-) create mode 100644 apis/chef/src/main/java/org/jclouds/chef/options/SearchOptions.java diff --git a/apis/chef/src/main/java/org/jclouds/chef/ChefApi.java b/apis/chef/src/main/java/org/jclouds/chef/ChefApi.java index eccebf6c76..023c19861f 100644 --- a/apis/chef/src/main/java/org/jclouds/chef/ChefApi.java +++ b/apis/chef/src/main/java/org/jclouds/chef/ChefApi.java @@ -34,6 +34,7 @@ import org.jclouds.chef.domain.Sandbox; import org.jclouds.chef.domain.SearchResult; import org.jclouds.chef.domain.UploadSandbox; import org.jclouds.chef.options.CreateClientOptions; +import org.jclouds.chef.options.SearchOptions; import org.jclouds.concurrent.Timeout; import org.jclouds.http.HttpResponseException; import org.jclouds.io.Payload; @@ -530,9 +531,18 @@ public interface ChefApi { * and the rows themselves. */ SearchResult searchRoles(); + + /** + * search all roles that match the given options. + * + * @return The response contains the total number of rows that matched your + * request, the position this result set returns (useful for paging) + * and the rows themselves. + */ + SearchResult searchRoles(SearchOptions options); /** - * search all apis. + * search all clients. *

* Note that without any request parameters this will return all of the data * within the index. @@ -542,6 +552,15 @@ public interface ChefApi { * and the rows themselves. */ SearchResult searchClients(); + + /** + * search all clients that match the given options. + * + * @return The response contains the total number of rows that matched your + * request, the position this result set returns (useful for paging) + * and the rows themselves. + */ + SearchResult searchClients(SearchOptions options); /** * search all nodes. @@ -554,19 +573,15 @@ public interface ChefApi { * and the rows themselves. */ SearchResult searchNodes(); - - + /** - * search all nodes that match the query parameter. - *

- * Note that without any request parameters this will return all of the data - * within the index. + * search all nodes that match the given options. * * @return The response contains the total number of rows that matched your * request, the position this result set returns (useful for paging) * and the rows themselves. */ - SearchResult searchNodes(String qeury); + SearchResult searchNodes(SearchOptions options); /** * search all items in a databag. @@ -579,6 +594,15 @@ public interface ChefApi { * and the rows themselves. */ SearchResult searchDatabag(String databagName); + + /** + * search all items in a databag that match the given options. + * + * @return The response contains the total number of rows that matched your + * request, the position this result set returns (useful for paging) + * and the rows themselves. + */ + SearchResult searchDatabag(String databagName, SearchOptions options); /** * Get the contents of the given resource. diff --git a/apis/chef/src/main/java/org/jclouds/chef/ChefAsyncApi.java b/apis/chef/src/main/java/org/jclouds/chef/ChefAsyncApi.java index e62e0fb367..6d63133ca0 100644 --- a/apis/chef/src/main/java/org/jclouds/chef/ChefAsyncApi.java +++ b/apis/chef/src/main/java/org/jclouds/chef/ChefAsyncApi.java @@ -29,7 +29,6 @@ import javax.ws.rs.GET; import javax.ws.rs.HEAD; import javax.ws.rs.POST; import javax.ws.rs.PUT; -import javax.ws.rs.QueryParam; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -63,6 +62,7 @@ import org.jclouds.chef.functions.ParseSearchNodesFromJson; import org.jclouds.chef.functions.ParseSearchRolesFromJson; import org.jclouds.chef.functions.UriForResource; import org.jclouds.chef.options.CreateClientOptions; +import org.jclouds.chef.options.SearchOptions; import org.jclouds.io.Payload; import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.EndpointParam; @@ -416,6 +416,14 @@ public interface ChefAsyncApi { @Path("/search/role") @ResponseParser(ParseSearchRolesFromJson.class) ListenableFuture> searchRoles(); + + /** + * @see ChefApi#searchRoles(SearchOptions) + */ + @GET + @Path("/search/role") + @ResponseParser(ParseSearchRolesFromJson.class) + ListenableFuture> searchRoles(SearchOptions options); /** * @see ChefApi#searchClients() @@ -424,6 +432,14 @@ public interface ChefAsyncApi { @Path("/search/client") @ResponseParser(ParseSearchClientsFromJson.class) ListenableFuture> searchClients(); + + /** + * @see ChefApi#searchClients(SearchOptions) + */ + @GET + @Path("/search/client") + @ResponseParser(ParseSearchClientsFromJson.class) + ListenableFuture> searchClients(SearchOptions options); /** * @see ChefApi#searchNodes() @@ -432,14 +448,14 @@ public interface ChefAsyncApi { @Path("/search/node") @ResponseParser(ParseSearchNodesFromJson.class) ListenableFuture> searchNodes(); - + /** - * @see ChefApi#searchNodes(String) + * @see ChefApi#searchNodes(SearchOptions) */ @GET @Path("/search/node") @ResponseParser(ParseSearchNodesFromJson.class) - ListenableFuture> searchNodes(@QueryParam("q") String query); + ListenableFuture> searchNodes(SearchOptions options); /** * @see ChefApi#searchDatabag(String) @@ -450,6 +466,15 @@ public interface ChefAsyncApi { ListenableFuture> searchDatabag( @PathParam("databagName") String databagName); + /** + * @see ChefApi#searchDatabag(String, SearchOptions) + */ + @GET + @Path("/search/{databagName}") + @ResponseParser(ParseSearchDatabagFromJson.class) + ListenableFuture> searchDatabag( + @PathParam("databagName") String databagName, SearchOptions options); + /** * @see ChefApi#getResourceContents(Resource) */ diff --git a/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java b/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java index ca5f27d039..072260e432 100644 --- a/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java +++ b/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java @@ -199,11 +199,16 @@ public class BaseChefService implements ChefService { } catch (IllegalStateException e) { } - chefContext.getApi().updateDatabagItem( - databag, - new DatabagItem(group, chefContext.utils().json().toJson( - ImmutableMap.> of("run_list", Lists.newArrayList(runList)), - RunListForGroup.RUN_LIST_TYPE))); + + DatabagItem runlist = new DatabagItem(group, chefContext.utils().json().toJson( + ImmutableMap.> of("run_list", Lists.newArrayList(runList)), + RunListForGroup.RUN_LIST_TYPE)); + + if (chefContext.getApi().getDatabagItem(databag, group) == null) { + chefContext.getApi().createDatabagItem(databag, runlist); + } else { + chefContext.getApi().updateDatabagItem(databag, runlist); + } } @Override diff --git a/apis/chef/src/main/java/org/jclouds/chef/options/SearchOptions.java b/apis/chef/src/main/java/org/jclouds/chef/options/SearchOptions.java new file mode 100644 index 0000000000..78dbf3502b --- /dev/null +++ b/apis/chef/src/main/java/org/jclouds/chef/options/SearchOptions.java @@ -0,0 +1,99 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.chef.options; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.http.options.BaseHttpRequestOptions; + +/** + * Options for the search api. + * + * @author Ignasi Barrera + */ +public class SearchOptions extends BaseHttpRequestOptions { + + /** + * A valid search string. + */ + public SearchOptions query(String query) { + this.queryParameters.put("q", checkNotNull(query, "query")); + return this; + } + + /** + * A sort string, such as 'name DESC'. + */ + public SearchOptions sort(String sort) { + this.queryParameters.put("sort", checkNotNull(sort, "sort")); + return this; + } + + /** + * The number of rows to return. + */ + public SearchOptions rows(int rows) { + this.queryParameters.put("rows", String.valueOf(rows)); + return this; + } + + /** + * The result number to start from. + */ + public SearchOptions start(int start) { + this.queryParameters.put("start", String.valueOf(start)); + return this; + } + + public static class Builder { + + /** + * @see SearchOptions#query(String) + */ + public static SearchOptions query(String query) { + SearchOptions options = new SearchOptions(); + return options.query(query); + } + + /** + * @see SearchOptions#sort(String) + */ + public static SearchOptions start(String start) { + SearchOptions options = new SearchOptions(); + return options.sort(start); + } + + /** + * @see SearchOptions#rows(int) + */ + public static SearchOptions rows(int rows) { + SearchOptions options = new SearchOptions(); + return options.rows(rows); + } + + /** + * @see SearchOptions#start(int) + */ + public static SearchOptions start(int start) { + SearchOptions options = new SearchOptions(); + return options.start(start); + } + } + +} diff --git a/apis/chef/src/main/java/org/jclouds/chef/test/TransientChefAsyncApi.java b/apis/chef/src/main/java/org/jclouds/chef/test/TransientChefAsyncApi.java index e5aaaecc72..0d33ea1f5a 100644 --- a/apis/chef/src/main/java/org/jclouds/chef/test/TransientChefAsyncApi.java +++ b/apis/chef/src/main/java/org/jclouds/chef/test/TransientChefAsyncApi.java @@ -51,6 +51,7 @@ import org.jclouds.chef.domain.Sandbox; import org.jclouds.chef.domain.SearchResult; import org.jclouds.chef.domain.UploadSandbox; import org.jclouds.chef.options.CreateClientOptions; +import org.jclouds.chef.options.SearchOptions; import org.jclouds.io.Payload; import org.jclouds.util.Strings2; @@ -286,11 +287,23 @@ public class TransientChefAsyncApi implements ChefAsyncApi { public ListenableFuture> searchClients() { throw new UnsupportedOperationException(); } + + @Override + public ListenableFuture> searchClients( + SearchOptions options) { + throw new UnsupportedOperationException(); + } @Override public ListenableFuture> searchDatabag(String databagName) { throw new UnsupportedOperationException(); } + + @Override + public ListenableFuture> searchDatabag( + String databagName, SearchOptions options) { + throw new UnsupportedOperationException(); + } @Override public ListenableFuture> searchNodes() { @@ -298,14 +311,21 @@ public class TransientChefAsyncApi implements ChefAsyncApi { } @Override - public ListenableFuture> searchNodes(String query) { - throw new UnsupportedOperationException(); - } + public ListenableFuture> searchNodes( + SearchOptions options) { + throw new UnsupportedOperationException(); + } @Override public ListenableFuture> searchRoles() { throw new UnsupportedOperationException(); } + + @Override + public ListenableFuture> searchRoles( + SearchOptions options) { + throw new UnsupportedOperationException(); + } @Override public ListenableFuture updateCookbook(String cookbookName, String version, CookbookVersion cookbook) { diff --git a/apis/chef/src/test/java/org/jclouds/chef/ChefAsyncApiTest.java b/apis/chef/src/test/java/org/jclouds/chef/ChefAsyncApiTest.java index 1ba29d020f..04490dec68 100644 --- a/apis/chef/src/test/java/org/jclouds/chef/ChefAsyncApiTest.java +++ b/apis/chef/src/test/java/org/jclouds/chef/ChefAsyncApiTest.java @@ -44,6 +44,7 @@ import org.jclouds.chef.functions.ParseSearchDatabagFromJson; import org.jclouds.chef.functions.ParseSearchNodesFromJson; import org.jclouds.chef.functions.ParseSearchRolesFromJson; import org.jclouds.chef.options.CreateClientOptions; +import org.jclouds.chef.options.SearchOptions; import org.jclouds.crypto.CryptoStreams; import org.jclouds.date.TimeStamp; import org.jclouds.http.HttpRequest; @@ -715,6 +716,23 @@ public class ChefAsyncApiTest extends BaseAsyncApiTest { checkFilters(httpRequest); } + + public void testSearchRolesWithOptions() throws SecurityException, NoSuchMethodException, IOException { + Method method = ChefAsyncApi.class.getMethod("searchRoles", SearchOptions.class); + GeneratedHttpRequest httpRequest = processor.createRequest(method, + SearchOptions.Builder.query("text")); + + assertRequestLineEquals(httpRequest, "GET http://localhost:4000/search/role?q=text HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: " + ChefAsyncApi.VERSION + "-test\n"); + assertPayloadEquals(httpRequest, null, null, false); + + assertResponseParserClassEquals(method, httpRequest, ParseSearchRolesFromJson.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, MapHttp4xxCodesToExceptions.class); + + checkFilters(httpRequest); + + } public void testSearchClients() throws SecurityException, NoSuchMethodException, IOException { Method method = ChefAsyncApi.class.getMethod("searchClients"); @@ -731,6 +749,23 @@ public class ChefAsyncApiTest extends BaseAsyncApiTest { checkFilters(httpRequest); } + + public void testSearchClientsWithOptions() throws SecurityException, NoSuchMethodException, IOException { + Method method = ChefAsyncApi.class.getMethod("searchClients", SearchOptions.class); + GeneratedHttpRequest httpRequest = processor.createRequest(method, + SearchOptions.Builder.query("text").rows(5)); + + assertRequestLineEquals(httpRequest, "GET http://localhost:4000/search/client?q=text&rows=5 HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: " + ChefAsyncApi.VERSION + "-test\n"); + assertPayloadEquals(httpRequest, null, null, false); + + assertResponseParserClassEquals(method, httpRequest, ParseSearchClientsFromJson.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, MapHttp4xxCodesToExceptions.class); + + checkFilters(httpRequest); + + } public void testSearchNodes() throws SecurityException, NoSuchMethodException, IOException { Method method = ChefAsyncApi.class.getMethod("searchNodes"); @@ -747,12 +782,13 @@ public class ChefAsyncApiTest extends BaseAsyncApiTest { checkFilters(httpRequest); } + + public void testSearchNodesWithOptions() throws SecurityException, NoSuchMethodException, IOException { + Method method = ChefAsyncApi.class.getMethod("searchNodes", SearchOptions.class); + GeneratedHttpRequest httpRequest = processor.createRequest(method, + SearchOptions.Builder.query("foo:foo").start(3)); - public void testSearchNodesQuery() throws SecurityException, NoSuchMethodException, IOException { - Method method = ChefAsyncApi.class.getMethod("searchNodes", String.class); - GeneratedHttpRequest httpRequest = processor.createRequest(method, "foo:foo"); - - assertRequestLineEquals(httpRequest, "GET http://localhost:4000/search/node?q=foo%3Afoo HTTP/1.1"); + assertRequestLineEquals(httpRequest, "GET http://localhost:4000/search/node?q=foo%3Afoo&start=3 HTTP/1.1"); assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: " + ChefAsyncApi.VERSION + "-test\n"); assertPayloadEquals(httpRequest, null, null, false); @@ -764,6 +800,7 @@ public class ChefAsyncApiTest extends BaseAsyncApiTest { } + public void testSearchDatabag() throws SecurityException, NoSuchMethodException, IOException { Method method = ChefAsyncApi.class.getMethod("searchDatabag", String.class); GeneratedHttpRequest httpRequest = processor.createRequest(method, "foo"); @@ -780,6 +817,23 @@ public class ChefAsyncApiTest extends BaseAsyncApiTest { } + public void testSearchDatabagWithOptions() throws SecurityException, NoSuchMethodException, IOException { + Method method = ChefAsyncApi.class.getMethod("searchDatabag", String.class, SearchOptions.class); + GeneratedHttpRequest httpRequest = processor.createRequest(method, "foo", + SearchOptions.Builder.query("bar").sort("name DESC")); + + assertRequestLineEquals(httpRequest, "GET http://localhost:4000/search/foo?q=bar&sort=name%20DESC HTTP/1.1"); + assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: " + ChefAsyncApi.VERSION + "-test\n"); + assertPayloadEquals(httpRequest, null, null, false); + + assertResponseParserClassEquals(method, httpRequest, ParseSearchDatabagFromJson.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, MapHttp4xxCodesToExceptions.class); + + checkFilters(httpRequest); + + } + public void testGetResourceContents() throws SecurityException, NoSuchMethodException, IOException { Method method = ChefAsyncApi.class.getMethod("getResourceContents", Resource.class); GeneratedHttpRequest httpRequest = processor.createRequest(method,