Merge pull request #1460 from rackspace/rax-pagination

PaginatedCollection for Rackspace style pagination.
This commit is contained in:
Adrian Cole 2013-03-25 12:45:11 -07:00
commit b943a8c19f
6 changed files with 389 additions and 2 deletions

View File

@ -0,0 +1,89 @@
/*
* 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.rackspace.cloudidentity.v2_0.domain;
import static org.jclouds.http.utils.Queries.queryParser;
import java.util.Iterator;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.openstack.v2_0.domain.Link;
import org.jclouds.rackspace.cloudidentity.v2_0.options.PaginationOptions;
import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
/**
* Base class for a paginated collection in Rackspace.
*
* @author Everett Toews
*/
@Beta
public class PaginatedCollection<T> extends IterableWithMarker<T> {
private Iterable<T> resources;
private Iterable<Link> links;
private int totalEntries;
protected PaginatedCollection(@Nullable Iterable<T> resources, @Nullable Iterable<Link> links, int totalEntries) {
this.resources = resources != null ? resources : ImmutableSet.<T> of();
this.links = links != null ? links : ImmutableSet.<Link> of();
this.totalEntries = totalEntries;
}
@Override
public Iterator<T> iterator() {
return resources.iterator();
}
/**
* links that relate to this collection
*/
public Iterable<Link> getLinks() {
return links;
}
public int getTotalEntries() {
return totalEntries;
}
public PaginationOptions nextPaginationOptions() {
return PaginationOptions.class.cast(nextMarker().get());
}
@Override
public Optional<Object> nextMarker() {
for (Link link: getLinks()) {
if (Link.Relation.NEXT == link.getRelation()) {
return Optional.<Object> of(toPaginationOptions(link));
}
}
return Optional.absent();
}
private Object toPaginationOptions(Link link) {
Multimap<String, String> queryParams = queryParser().apply(link.getHref().getRawQuery());
PaginationOptions paginationOptions = PaginationOptions.Builder.queryParameters(queryParams);
return paginationOptions;
}
}

View File

@ -0,0 +1,56 @@
/**
* 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.rackspace.cloudidentity.v2_0.functions;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Date;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.date.DateService;
import com.google.common.base.Function;
/**
* Takes a Date and return a yyyy-MM-dd String.
*
* @author Everett Toews
*/
@Singleton
public class DateParser implements Function<Object, String> {
private final DateService dateService;
@Inject
DateParser(DateService dateService) {
this.dateService = dateService;
}
@Override
public String apply(Object input) {
checkArgument(checkNotNull(input, "input") instanceof Date, "This function is only valid for Dates!");
Date date = Date.class.cast(input);
return dateService.iso8601SecondsDateFormat(date);
}
}

View File

@ -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.rackspace.cloudidentity.v2_0.options;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.http.options.BaseHttpRequestOptions;
import com.google.common.collect.Multimap;
/**
* Options used to control paginated results (aka list commands).
*
* @author Everett Toews
*/
public class PaginationOptions extends BaseHttpRequestOptions {
public PaginationOptions queryParameters(Multimap<String, String> queryParams) {
checkNotNull(queryParams, "queryParams");
queryParameters.putAll(queryParams);
return this;
}
/**
* Offset is the starting point for the return data. Offset must be a multiple of the limit (or zero).
*/
public PaginationOptions offset(int offset) {
checkState(offset >= 0, "offset must be >= 0");
queryParameters.put("offset", String.valueOf(offset));
return this;
}
/**
* Limit is the restriction on the maximum number of items for that type that can be returned.
* <p/>
* Note that list operations never return itemNotFound (404) faults.
*/
public PaginationOptions limit(int limit) {
checkState(limit >= 0, "limit must be >= 0");
checkState(limit <= 10000, "limit must be <= 10000");
queryParameters.put("limit", Integer.toString(limit));
return this;
}
/**
* Name is a filter on the result set.
*/
public PaginationOptions name(String nameFilter) {
queryParameters.put("name", nameFilter);
return this;
}
public static class Builder {
public static PaginationOptions queryParameters(Multimap<String, String> queryParams) {
PaginationOptions options = new PaginationOptions();
return options.queryParameters(queryParams);
}
/**
* @see PaginationOptions#offset(int)
*/
public static PaginationOptions offset(int offset) {
PaginationOptions options = new PaginationOptions();
return options.offset(offset);
}
/**
* @see PaginationOptions#limit(int)
*/
public static PaginationOptions limit(int limit) {
PaginationOptions options = new PaginationOptions();
return options.limit(limit);
}
/**
* @see PaginationOptions#name(String)
*/
public static PaginationOptions name(String name) {
PaginationOptions options = new PaginationOptions();
return options.name(name);
}
}
}

View File

@ -0,0 +1,50 @@
/**
* 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.rackspace.cloudidentity.v2_0;
import static com.google.common.util.concurrent.Futures.immediateFuture;
import static org.jclouds.Fallbacks.valOnNotFoundOr404;
import org.jclouds.Fallback;
import org.jclouds.openstack.v2_0.domain.Link;
import org.jclouds.rackspace.cloudidentity.v2_0.domain.PaginatedCollection;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ListenableFuture;
public final class CloudIdentityFallbacks {
private CloudIdentityFallbacks() {
}
public static final class EmptyPaginatedCollectionOnNotFoundOr404 implements Fallback<PaginatedCollection<Object>> {
private static final PaginatedCollection<Object> EMPTY = new PaginatedCollection<Object>(
ImmutableSet.<Object> of(), ImmutableSet.<Link> of(), 0) {
};
@Override
public ListenableFuture<PaginatedCollection<Object>> create(Throwable t) throws Exception {
return immediateFuture(createOrPropagate(t));
}
@Override
public PaginatedCollection<Object> createOrPropagate(Throwable t) throws Exception {
return valOnNotFoundOr404(EMPTY, t);
}
}
}

View File

@ -0,0 +1,91 @@
/**
* 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.rackspace.cloudidentity.v2_0.options;
import static org.jclouds.rackspace.cloudidentity.v2_0.options.PaginationOptions.Builder.limit;
import static org.jclouds.rackspace.cloudidentity.v2_0.options.PaginationOptions.Builder.name;
import static org.jclouds.rackspace.cloudidentity.v2_0.options.PaginationOptions.Builder.offset;
import static org.jclouds.rackspace.cloudidentity.v2_0.options.PaginationOptions.Builder.queryParameters;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSetMultimap;
/**
* @author Everett Toews
*/
@Test(groups = "unit", testName = "PaginationOptionsTest")
public class PaginationOptionsTest {
public void testQueryParameters() {
ImmutableSetMultimap<String, String> queryParameters = ImmutableSetMultimap.<String, String> of(
"limit", "1",
"offset", "1",
"name", "foo");
PaginationOptions options = new PaginationOptions().queryParameters(queryParameters);
assertEquals(queryParameters, options.buildQueryParameters());
}
public void testMaxResults() {
int limit = 1;
PaginationOptions options = new PaginationOptions().limit(limit);
assertEquals(ImmutableList.of("1"), options.buildQueryParameters().get("limit"));
}
public void testOffset() {
int offset = 1;
PaginationOptions options = new PaginationOptions().offset(offset);
assertEquals(ImmutableList.of("1"), options.buildQueryParameters().get("offset"));
}
public void testNameFilter() {
String nameFilter = "foo";
PaginationOptions options = new PaginationOptions().name(nameFilter);
assertEquals(ImmutableList.of("foo"), options.buildQueryParameters().get("name"));
}
public void testMaxResultsStatic() {
int limit = 1;
PaginationOptions options = limit(limit);
assertEquals(ImmutableList.of("1"), options.buildQueryParameters().get("limit"));
}
public void testOffsetStatic() {
int offset = 1;
PaginationOptions options = offset(offset);
assertEquals(ImmutableList.of("1"), options.buildQueryParameters().get("offset"));
}
public void testNameFilterStatic() {
String nameFilter = "foo";
PaginationOptions options = name(nameFilter);
assertEquals(ImmutableList.of("foo"), options.buildQueryParameters().get("name"));
}
public void testQueryParametersStatic() {
ImmutableSetMultimap<String, String> queryParameters = ImmutableSetMultimap.<String, String> of(
"limit", "1",
"offset", "1",
"name", "foo");
PaginationOptions options = queryParameters(queryParameters);
assertEquals(queryParameters, options.buildQueryParameters());
}
}

View File

@ -20,6 +20,7 @@ package org.jclouds.http.options;
import java.util.Collection;
import com.google.common.base.Objects;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
@ -134,8 +135,9 @@ public class BaseHttpRequestOptions implements HttpRequestOptions {
@Override
public String toString() {
return "[formParameters=" + formParameters + ", headers=" + headers + ", pathSuffix=" + pathSuffix + ", payload="
+ payload + ", queryParameters=" + queryParameters + "]";
return Objects.toStringHelper(this).omitNullValues().add("formParameters", formParameters)
.add("headers", headers).add("pathSuffix", pathSuffix).add("payload", payload)
.add("queryParameters", queryParameters).toString();
}
}