mirror of https://github.com/apache/jclouds.git
add ApiMetadata and ProviderMetadata
- add skeleton for PacketApi with ProjectApi only - add XAuthTokenToRequest filter - add HttpApiModule and ParserModule - add ProjectApi feature with Mock and Live Tests - add pagination to Project API - add test pagination
This commit is contained in:
parent
1620a19787
commit
34d013f7db
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
import org.jclouds.packet.features.ProjectApi;
|
||||
import org.jclouds.rest.annotations.Delegate;
|
||||
|
||||
/**
|
||||
* The Packet API is a REST API for managing your services and deployments.
|
||||
* <p>
|
||||
*
|
||||
* @see <a href="https://www.packet.net/help/api/" >doc</a>
|
||||
*/
|
||||
public interface PacketApi extends Closeable {
|
||||
|
||||
/**
|
||||
* The Packet API includes operations for managing project.
|
||||
*
|
||||
* @see <a href="https://www.packet.net/help/api/#page:projects,header:projects-projects">docs</a>
|
||||
*/
|
||||
@Delegate
|
||||
ProjectApi projectApi();
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
import org.jclouds.packet.config.PacketComputeParserModule;
|
||||
import org.jclouds.packet.config.PacketHttpApiModule;
|
||||
import org.jclouds.rest.internal.BaseHttpApiMetadata;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Module;
|
||||
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ApiMetadata} for Packet API
|
||||
*/
|
||||
public class PacketApiMetadata extends BaseHttpApiMetadata<PacketApi> {
|
||||
|
||||
@Override
|
||||
public Builder toBuilder() {
|
||||
return new Builder().fromApiMetadata(this);
|
||||
}
|
||||
|
||||
public PacketApiMetadata() {
|
||||
this(new Builder());
|
||||
}
|
||||
|
||||
protected PacketApiMetadata(Builder builder) {
|
||||
super(builder);
|
||||
}
|
||||
|
||||
public static Properties defaultProperties() {
|
||||
Properties properties = BaseHttpApiMetadata.defaultProperties();
|
||||
properties.put(TEMPLATE, "osFamily=UBUNTU,os64Bit=true,osVersionMatches=16.*");
|
||||
properties.put(TIMEOUT_NODE_RUNNING, 300000); // 5 mins
|
||||
return properties;
|
||||
}
|
||||
|
||||
public static class Builder extends BaseHttpApiMetadata.Builder<PacketApi, Builder> {
|
||||
|
||||
protected Builder() {
|
||||
id("packet")
|
||||
.name("Packet API")
|
||||
.identityName("Packet Project Id")
|
||||
.credentialName("Must be Packet Token")
|
||||
.documentation(URI.create("https://www.packet.net/help/api/#"))
|
||||
.defaultEndpoint("https://api.packet.net")
|
||||
.defaultProperties(PacketApiMetadata.defaultProperties())
|
||||
//.view(typeToken(ComputeServiceContext.class))
|
||||
.defaultModules(ImmutableSet.<Class<? extends Module>>builder()
|
||||
.add(PacketHttpApiModule.class)
|
||||
.add(PacketComputeParserModule.class)
|
||||
//.add(PacketComputeServiceContextModule.class)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketApiMetadata build() {
|
||||
return new PacketApiMetadata(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Builder self() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jclouds.providers.ProviderMetadata;
|
||||
import org.jclouds.providers.internal.BaseProviderMetadata;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
|
||||
@AutoService(ProviderMetadata.class)
|
||||
public class PacketProviderMetadata extends BaseProviderMetadata {
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder toBuilder() {
|
||||
return builder().fromProviderMetadata(this);
|
||||
}
|
||||
|
||||
public PacketProviderMetadata() {
|
||||
super(builder());
|
||||
}
|
||||
|
||||
public PacketProviderMetadata(Builder builder) {
|
||||
super(builder);
|
||||
}
|
||||
|
||||
public static Properties defaultProperties() {
|
||||
final Properties properties = PacketApiMetadata.defaultProperties();
|
||||
return properties;
|
||||
}
|
||||
|
||||
public static class Builder extends BaseProviderMetadata.Builder {
|
||||
|
||||
protected Builder() {
|
||||
id("packet")
|
||||
.name("Packet Compute Services")
|
||||
.apiMetadata(new PacketApiMetadata())
|
||||
.homepage(URI.create("https://www.packet.net/"))
|
||||
.console(URI.create("https://app.packet.net/portal"))
|
||||
.endpoint("https://api.packet.net")
|
||||
.iso3166Codes("US-CA", "US-NJ", "NL", "JP")
|
||||
.defaultProperties(PacketProviderMetadata.defaultProperties());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketProviderMetadata build() {
|
||||
return new PacketProviderMetadata(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder fromProviderMetadata(ProviderMetadata in) {
|
||||
super.fromProviderMetadata(in);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.config;
|
||||
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
public class PacketComputeParserModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(GsonModule.DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.config;
|
||||
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.annotation.ClientError;
|
||||
import org.jclouds.http.annotation.Redirection;
|
||||
import org.jclouds.http.annotation.ServerError;
|
||||
import org.jclouds.location.suppliers.ImplicitLocationSupplier;
|
||||
import org.jclouds.location.suppliers.implicit.FirstRegion;
|
||||
import org.jclouds.packet.PacketApi;
|
||||
import org.jclouds.packet.domain.Href;
|
||||
import org.jclouds.packet.domain.options.ListOptions;
|
||||
import org.jclouds.packet.functions.LinkToListOptions;
|
||||
import org.jclouds.packet.handlers.PacketErrorHandler;
|
||||
import org.jclouds.rest.ConfiguresHttpApi;
|
||||
import org.jclouds.rest.config.HttpApiModule;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.inject.Scopes;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
@ConfiguresHttpApi
|
||||
public class PacketHttpApiModule extends HttpApiModule<PacketApi> {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
bind(ImplicitLocationSupplier.class).to(FirstRegion.class).in(Scopes.SINGLETON);
|
||||
bind(new TypeLiteral<Function<Href, ListOptions>>() {
|
||||
}).to(LinkToListOptions.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindErrorHandlers() {
|
||||
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(PacketErrorHandler.class);
|
||||
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(PacketErrorHandler.class);
|
||||
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(PacketErrorHandler.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.domain.internal;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.jclouds.collect.IterableWithMarker;
|
||||
import org.jclouds.javax.annotation.Nullable;
|
||||
import org.jclouds.json.SerializedNames;
|
||||
import org.jclouds.packet.domain.Href;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* Base class for all collections that return paginated results.
|
||||
*/
|
||||
public abstract class PaginatedCollection<T> extends IterableWithMarker<T> {
|
||||
|
||||
@AutoValue
|
||||
public abstract static class Meta {
|
||||
public abstract long total();
|
||||
@Nullable public abstract Href first();
|
||||
@Nullable public abstract Href previous();
|
||||
@Nullable public abstract Href self();
|
||||
@Nullable public abstract Href next();
|
||||
@Nullable public abstract Href last();
|
||||
|
||||
@SerializedNames({ "total", "first", "previous", "self", "next", "last" })
|
||||
public static Meta create(long total, Href first, Href previous, Href self, Href next, Href last) {
|
||||
return new AutoValue_PaginatedCollection_Meta(total, first, previous, self, next, last);
|
||||
}
|
||||
|
||||
Meta() { }
|
||||
}
|
||||
|
||||
private final List<T> items;
|
||||
private final Meta meta;
|
||||
|
||||
protected PaginatedCollection(List<T> items, Meta meta) {
|
||||
this.items = ImmutableList.copyOf(checkNotNull(items, "items cannot be null"));
|
||||
this.meta = checkNotNull(meta, "meta cannot be null");
|
||||
}
|
||||
|
||||
public List<T> items() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public Meta meta() {
|
||||
return meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return items.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> nextMarker() {
|
||||
if (meta.next() == null) {
|
||||
return Optional.absent();
|
||||
}
|
||||
return Optional.fromNullable((Object) meta.next());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.domain.options;
|
||||
|
||||
import org.jclouds.http.options.BaseHttpRequestOptions;
|
||||
|
||||
/**
|
||||
* Options to customize how paginated lists are returned.
|
||||
*/
|
||||
public class ListOptions extends BaseHttpRequestOptions {
|
||||
public static final String PAGE_PARAM = "page";
|
||||
public static final String PER_PAGE_PARAM = "per_page";
|
||||
|
||||
/**
|
||||
* Configures the number of entries to return in each page.
|
||||
*/
|
||||
public ListOptions perPage(int perPage) {
|
||||
queryParameters.put(PER_PAGE_PARAM, String.valueOf(perPage));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the number of the page to be returned.
|
||||
*/
|
||||
public ListOptions page(int page) {
|
||||
queryParameters.put(PAGE_PARAM, String.valueOf(page));
|
||||
return this;
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
|
||||
/**
|
||||
* @see {@link ListOptions#perPage(int)}
|
||||
*/
|
||||
public static ListOptions perPage(int perPage) {
|
||||
return new ListOptions().perPage(perPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link ListOptions#page(int)}
|
||||
*/
|
||||
public static ListOptions page(int page) {
|
||||
return new ListOptions().page(page);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.features;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.jclouds.Fallbacks;
|
||||
import org.jclouds.collect.IterableWithMarker;
|
||||
import org.jclouds.collect.PagedIterable;
|
||||
import org.jclouds.http.functions.ParseJson;
|
||||
import org.jclouds.json.Json;
|
||||
import org.jclouds.packet.PacketApi;
|
||||
import org.jclouds.packet.domain.Href;
|
||||
import org.jclouds.packet.domain.Project;
|
||||
import org.jclouds.packet.domain.internal.PaginatedCollection;
|
||||
import org.jclouds.packet.domain.options.ListOptions;
|
||||
import org.jclouds.packet.filters.AddXAuthTokenToRequest;
|
||||
import org.jclouds.packet.functions.BaseToPagedIterable;
|
||||
import org.jclouds.rest.annotations.Fallback;
|
||||
import org.jclouds.rest.annotations.RequestFilters;
|
||||
import org.jclouds.rest.annotations.ResponseParser;
|
||||
import org.jclouds.rest.annotations.Transform;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
@Path("/projects")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RequestFilters(AddXAuthTokenToRequest.class)
|
||||
public interface ProjectApi {
|
||||
|
||||
|
||||
@Named("project:list")
|
||||
@GET
|
||||
@ResponseParser(ParseProjects.class)
|
||||
@Transform(ParseProjects.ToPagedIterable.class)
|
||||
@Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
|
||||
PagedIterable<Project> list();
|
||||
|
||||
@Named("project:list")
|
||||
@GET
|
||||
@ResponseParser(ParseProjects.class)
|
||||
@Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
|
||||
IterableWithMarker<Project> list(ListOptions options);
|
||||
|
||||
final class ParseProjects extends ParseJson<ParseProjects.Projects> {
|
||||
@Inject
|
||||
ParseProjects(Json json) {
|
||||
super(json, TypeLiteral.get(Projects.class));
|
||||
}
|
||||
|
||||
private static class Projects extends PaginatedCollection<Project> {
|
||||
@ConstructorProperties({ "projects", "meta" })
|
||||
public Projects(List<Project> items, Meta meta) {
|
||||
super(items, meta);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ToPagedIterable extends BaseToPagedIterable<Project, ListOptions> {
|
||||
@Inject ToPagedIterable(PacketApi api, Function<Href, ListOptions> linkToOptions) {
|
||||
super(api, linkToOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IterableWithMarker<Project> fetchPageUsingOptions(ListOptions options, Optional<Object> arg0) {
|
||||
return api.projectApi().list(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.filters;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.http.HttpException;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpRequestFilter;
|
||||
import org.jclouds.location.Provider;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@Singleton
|
||||
public class AddXAuthTokenToRequest implements HttpRequestFilter {
|
||||
|
||||
private final Supplier<Credentials> creds;
|
||||
|
||||
@Inject
|
||||
AddXAuthTokenToRequest(@Provider Supplier<Credentials> creds) {
|
||||
this.creds = creds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest filter(HttpRequest request) throws HttpException {
|
||||
Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null");
|
||||
return request.toBuilder().replaceHeader("X-Auth-Token", currentCreds.credential).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.functions;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.collect.IterableWithMarker;
|
||||
import org.jclouds.collect.internal.Arg0ToPagedIterable;
|
||||
import org.jclouds.packet.PacketApi;
|
||||
import org.jclouds.packet.domain.Href;
|
||||
import org.jclouds.packet.domain.options.ListOptions;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
/**
|
||||
* Base class to implement the functions that build the
|
||||
* <code>PagedIterable</code>. Subclasses just need to override the
|
||||
* {@link #fetchPageUsingOptions(ListOptions, Optional)} to invoke the right API
|
||||
* method with the given options parameter to get the next page.
|
||||
*/
|
||||
public abstract class BaseToPagedIterable<T, O extends ListOptions> extends
|
||||
Arg0ToPagedIterable<T, BaseToPagedIterable<T, O>> {
|
||||
private final Function<Href, O> linkToOptions;
|
||||
protected final PacketApi api;
|
||||
|
||||
@Inject protected BaseToPagedIterable(PacketApi api, Function<Href, O> linkToOptions) {
|
||||
this.api = api;
|
||||
this.linkToOptions = linkToOptions;
|
||||
}
|
||||
|
||||
protected abstract IterableWithMarker<T> fetchPageUsingOptions(O options, Optional<Object> arg0);
|
||||
|
||||
@Override
|
||||
protected Function<Object, IterableWithMarker<T>> markerToNextForArg0(final Optional<Object> arg0) {
|
||||
return new Function<Object, IterableWithMarker<T>>() {
|
||||
@Override
|
||||
public IterableWithMarker<T> apply(Object input) {
|
||||
O nextOptions = linkToOptions.apply(Href.class.cast(input));
|
||||
return fetchPageUsingOptions(nextOptions, arg0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.functions;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.jclouds.packet.domain.Href;
|
||||
import org.jclouds.packet.domain.options.ListOptions;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.common.collect.Iterables.getFirst;
|
||||
import static org.jclouds.http.utils.Queries.queryParser;
|
||||
import static org.jclouds.packet.domain.options.ListOptions.PAGE_PARAM;
|
||||
import static org.jclouds.packet.domain.options.ListOptions.PER_PAGE_PARAM;
|
||||
|
||||
/**
|
||||
* Transforms an href returned by the API into a {@link ListOptions} that can be
|
||||
* used to perform a request to get another page of a paginated list.
|
||||
*/
|
||||
public class LinkToListOptions implements Function<Href, ListOptions> {
|
||||
|
||||
@Override
|
||||
public ListOptions apply(Href input) {
|
||||
checkNotNull(input, "input cannot be null");
|
||||
|
||||
Multimap<String, String> queryParams = queryParser().apply(URI.create(input.href()).getQuery());
|
||||
String nextPage = getFirstOrNull(PAGE_PARAM, queryParams);
|
||||
String nextPerPage = getFirstOrNull(PER_PAGE_PARAM, queryParams);
|
||||
|
||||
ListOptions options = new ListOptions();
|
||||
if (nextPage != null) {
|
||||
options.page(Integer.parseInt(nextPage));
|
||||
}
|
||||
if (nextPerPage != null) {
|
||||
options.perPage(Integer.parseInt(nextPerPage));
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public static String getFirstOrNull(String key, Multimap<String, String> params) {
|
||||
return params.containsKey(key) ? emptyToNull(getFirst(params.get(key), null)) : null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.handlers;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
|
||||
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
|
||||
|
||||
/**
|
||||
* This will parse and set an appropriate exception on the command object.
|
||||
*/
|
||||
@Singleton
|
||||
public class PacketErrorHandler implements HttpErrorHandler {
|
||||
|
||||
public void handleError(HttpCommand command, HttpResponse response) {
|
||||
// it is important to always read fully and close streams
|
||||
byte[] data = closeClientButKeepContentStream(response);
|
||||
String message = data != null ? new String(data) : null;
|
||||
|
||||
Exception exception = message != null ? new HttpResponseException(command, response, message)
|
||||
: new HttpResponseException(command, response);
|
||||
message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(),
|
||||
response.getStatusLine());
|
||||
switch (response.getStatusCode()) {
|
||||
case 400:
|
||||
exception = new IllegalArgumentException(message, exception);
|
||||
break;
|
||||
case 401:
|
||||
case 403:
|
||||
exception = new AuthorizationException(message, exception);
|
||||
break;
|
||||
case 404:
|
||||
if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
|
||||
exception = new ResourceNotFoundException(message, exception);
|
||||
}
|
||||
break;
|
||||
case 409:
|
||||
exception = new IllegalStateException(message, exception);
|
||||
break;
|
||||
}
|
||||
command.setException(exception);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet;
|
||||
|
||||
import org.jclouds.providers.internal.BaseProviderMetadataTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "unit", testName = "PacketProviderMetadataTest")
|
||||
public class PacketProviderMetadataTest extends BaseProviderMetadataTest {
|
||||
|
||||
public PacketProviderMetadataTest() {
|
||||
super(new PacketProviderMetadata(), new PacketApiMetadata());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.compute.internal;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.jclouds.apis.BaseApiLiveTest;
|
||||
import org.jclouds.compute.config.ComputeServiceProperties;
|
||||
import org.jclouds.packet.PacketApi;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class BasePacketApiLiveTest extends BaseApiLiveTest<PacketApi> {
|
||||
|
||||
private Predicate<String> deviceRunning;
|
||||
private Predicate<String> deviceTerminated;
|
||||
|
||||
public BasePacketApiLiveTest() {
|
||||
provider = "packet";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Properties setupProperties() {
|
||||
Properties props = super.setupProperties();
|
||||
props.put(ComputeServiceProperties.POLL_INITIAL_PERIOD, 1000);
|
||||
props.put(ComputeServiceProperties.POLL_MAX_PERIOD, 10000);
|
||||
props.put(ComputeServiceProperties.TIMEOUT_IMAGE_AVAILABLE, TimeUnit.MINUTES.toMillis(45));
|
||||
return props;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PacketApi create(Properties props, Iterable<Module> modules) {
|
||||
Injector injector = newBuilder().modules(modules).overrides(props).buildInjector();
|
||||
deviceRunning = injector.getInstance(Key.get(new TypeLiteral<Predicate<String>>(){},
|
||||
Names.named(TIMEOUT_NODE_RUNNING)));
|
||||
deviceTerminated = injector.getInstance(Key.get(new TypeLiteral<Predicate<String>>(){},
|
||||
Names.named(TIMEOUT_NODE_TERMINATED)));
|
||||
return injector.getInstance(PacketApi.class);
|
||||
}
|
||||
|
||||
protected void assertNodeRunning(String deviceId) {
|
||||
assertTrue(deviceRunning.apply(deviceId), String.format("Device %s did not start in the configured timeout", deviceId));
|
||||
}
|
||||
|
||||
protected void assertNodeTerminated(String deviceId) {
|
||||
assertTrue(deviceTerminated.apply(deviceId), String.format("Device %s was not terminated in the configured timeout", deviceId));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.compute.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.ContextBuilder;
|
||||
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
||||
import org.jclouds.json.Json;
|
||||
import org.jclouds.packet.PacketApi;
|
||||
import org.jclouds.packet.PacketProviderMetadata;
|
||||
import org.jclouds.rest.ApiContext;
|
||||
import org.testng.annotations.AfterMethod;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.inject.Module;
|
||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||
import com.squareup.okhttp.mockwebserver.RecordedRequest;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
|
||||
import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
public class BasePacketApiMockTest {
|
||||
|
||||
private static final String X_AUTHORIZATION_TOKEN = "c5401990f0c24135e8d6b5d260603fc71696d4738da9aa04a720229a01a2521d";
|
||||
private static final String DEFAULT_ENDPOINT = new PacketProviderMetadata().getEndpoint();
|
||||
|
||||
private final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor()));
|
||||
|
||||
protected MockWebServer server;
|
||||
protected PacketApi api;
|
||||
private Json json;
|
||||
|
||||
// So that we can ignore formatting.
|
||||
private final JsonParser parser = new JsonParser();
|
||||
|
||||
@BeforeMethod
|
||||
public void start() throws IOException {
|
||||
server = new MockWebServer();
|
||||
server.play();
|
||||
ApiContext<PacketApi> ctx = ContextBuilder.newBuilder("packet")
|
||||
.credentials("", X_AUTHORIZATION_TOKEN)
|
||||
.endpoint(url(""))
|
||||
.modules(modules)
|
||||
.overrides(overrides())
|
||||
.build();
|
||||
json = ctx.utils().injector().getInstance(Json.class);
|
||||
api = ctx.getApi();
|
||||
}
|
||||
|
||||
@AfterMethod(alwaysRun = true)
|
||||
public void stop() throws IOException {
|
||||
server.shutdown();
|
||||
api.close();
|
||||
}
|
||||
|
||||
protected Properties overrides() {
|
||||
Properties properties = new Properties();
|
||||
properties.put(PROPERTY_MAX_RETRIES, "0"); // Do not retry
|
||||
return properties;
|
||||
}
|
||||
|
||||
protected String url(String path) {
|
||||
return server.getUrl(path).toString();
|
||||
}
|
||||
|
||||
protected MockResponse jsonResponse(String resource) {
|
||||
return new MockResponse().addHeader("Content-Type", "application/json").setBody(stringFromResource(resource));
|
||||
}
|
||||
|
||||
protected MockResponse response404() {
|
||||
return new MockResponse().setStatus("HTTP/1.1 404 Not Found");
|
||||
}
|
||||
|
||||
protected MockResponse response204() {
|
||||
return new MockResponse().setStatus("HTTP/1.1 204 No Content");
|
||||
}
|
||||
|
||||
protected String stringFromResource(String resourceName) {
|
||||
try {
|
||||
return Resources.toString(getClass().getResource(resourceName), Charsets.UTF_8)
|
||||
.replace(DEFAULT_ENDPOINT, url(""));
|
||||
} catch (IOException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> T onlyObjectFromResource(String resourceName, TypeToken<Map<String, T>> type) {
|
||||
// Assume JSON objects passed here will be in the form: { "entity": { ... } }
|
||||
String text = stringFromResource(resourceName);
|
||||
Map<String, T> object = json.fromJson(text, type.getType());
|
||||
checkArgument(!object.isEmpty(), "The given json does not contain any object: %s", text);
|
||||
checkArgument(object.keySet().size() == 1, "The given json does not contain more than one object: %s", text);
|
||||
return object.get(getOnlyElement(object.keySet()));
|
||||
}
|
||||
|
||||
protected <T> T objectFromResource(String resourceName, Class<T> type) {
|
||||
String text = stringFromResource(resourceName);
|
||||
return json.fromJson(text, type);
|
||||
}
|
||||
|
||||
protected RecordedRequest assertSent(MockWebServer server, String method, String path) throws InterruptedException {
|
||||
RecordedRequest request = server.takeRequest();
|
||||
assertEquals(request.getMethod(), method);
|
||||
assertEquals(request.getPath(), path);
|
||||
assertEquals(request.getHeader("Accept"), "application/json");
|
||||
assertEquals(request.getHeader("X-Auth-Token"), X_AUTHORIZATION_TOKEN);
|
||||
return request;
|
||||
}
|
||||
|
||||
protected RecordedRequest assertSent(MockWebServer server, String method, String path, String json)
|
||||
throws InterruptedException {
|
||||
RecordedRequest request = assertSent(server, method, path);
|
||||
assertEquals(request.getHeader("Content-Type"), "application/json");
|
||||
assertEquals(parser.parse(new String(request.getBody(), Charsets.UTF_8)), parser.parse(json));
|
||||
return request;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.features;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.jclouds.packet.compute.internal.BasePacketApiLiveTest;
|
||||
import org.jclouds.packet.domain.Project;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.util.Strings.isNullOrEmpty;
|
||||
|
||||
@Test(groups = "live", testName = "ProjectApiLiveTest")
|
||||
public class ProjectApiLiveTest extends BasePacketApiLiveTest {
|
||||
|
||||
public void testListProjects() {
|
||||
final AtomicInteger found = new AtomicInteger(0);
|
||||
assertTrue(Iterables.all(api().list().concat(), new Predicate<Project>() {
|
||||
@Override
|
||||
public boolean apply(Project input) {
|
||||
found.incrementAndGet();
|
||||
return !isNullOrEmpty(input.id());
|
||||
}
|
||||
}), "All projects must have the 'id' field populated");
|
||||
assertTrue(found.get() > 0, "Expected some projects to be returned");
|
||||
}
|
||||
|
||||
public void testListActionsOnePage() {
|
||||
final AtomicInteger found = new AtomicInteger(0);
|
||||
assertTrue(api().list(page(1).perPage(5)).allMatch(new Predicate<Project>() {
|
||||
@Override
|
||||
public boolean apply(Project input) {
|
||||
found.incrementAndGet();
|
||||
return !isNullOrEmpty(input.id());
|
||||
}
|
||||
}), "All projects must have the 'id' field populated");
|
||||
assertTrue(found.get() > 0, "Expected some projects to be returned");
|
||||
}
|
||||
|
||||
|
||||
private ProjectApi api() {
|
||||
return api.projectApi();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.features;
|
||||
|
||||
import org.jclouds.packet.compute.internal.BasePacketApiMockTest;
|
||||
import org.jclouds.packet.domain.Project;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static com.google.common.collect.Iterables.isEmpty;
|
||||
import static com.google.common.collect.Iterables.size;
|
||||
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
@Test(groups = "unit", testName = "ProjectApiMockTest", singleThreaded = true)
|
||||
public class ProjectApiMockTest extends BasePacketApiMockTest {
|
||||
|
||||
public void testListProjects() throws InterruptedException {
|
||||
server.enqueue(jsonResponse("/projects-first.json"));
|
||||
server.enqueue(jsonResponse("/projects-last.json"));
|
||||
|
||||
Iterable<Project> projects = api.projectApi().list().concat();
|
||||
|
||||
assertEquals(size(projects), 8); // Force the PagedIterable to advance
|
||||
assertEquals(server.getRequestCount(), 2);
|
||||
|
||||
assertSent(server, "GET", "/projects");
|
||||
assertSent(server, "GET", "/projects?page=2");
|
||||
}
|
||||
|
||||
public void testListProjectsReturns404() throws InterruptedException {
|
||||
server.enqueue(response404());
|
||||
|
||||
Iterable<Project> projects = api.projectApi().list().concat();
|
||||
|
||||
assertTrue(isEmpty(projects));
|
||||
|
||||
assertEquals(server.getRequestCount(), 1);
|
||||
assertSent(server, "GET", "/projects");
|
||||
}
|
||||
|
||||
public void testListProjectsWithOptions() throws InterruptedException {
|
||||
server.enqueue(jsonResponse("/projects-first.json"));
|
||||
|
||||
Iterable<Project> actions = api.projectApi().list(page(1).perPage(5));
|
||||
|
||||
assertEquals(size(actions), 5);
|
||||
assertEquals(server.getRequestCount(), 1);
|
||||
|
||||
assertSent(server, "GET", "/projects?page=1&per_page=5");
|
||||
}
|
||||
|
||||
public void testListProjectsWithOptionsReturns404() throws InterruptedException {
|
||||
server.enqueue(response404());
|
||||
|
||||
Iterable<Project> actions = api.projectApi().list(page(1).perPage(5));
|
||||
|
||||
assertTrue(isEmpty(actions));
|
||||
|
||||
assertEquals(server.getRequestCount(), 1);
|
||||
assertSent(server, "GET", "/projects?page=1&per_page=5");
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.packet.functions;
|
||||
|
||||
import org.jclouds.packet.domain.Href;
|
||||
import org.jclouds.packet.domain.options.ListOptions;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static org.jclouds.packet.domain.options.ListOptions.PAGE_PARAM;
|
||||
import static org.jclouds.packet.domain.options.ListOptions.PER_PAGE_PARAM;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
@Test(groups = "unit", testName = "LinkToListOptionsTest")
|
||||
public class LinkToListOptionsTest {
|
||||
|
||||
public void testNoOptions() {
|
||||
LinkToListOptions function = new LinkToListOptions();
|
||||
|
||||
ListOptions options = function.apply(Href.create("https://api.packet.net/projects"));
|
||||
assertNotNull(options);
|
||||
|
||||
Multimap<String, String> params = options.buildQueryParameters();
|
||||
assertFalse(params.containsKey(PAGE_PARAM));
|
||||
assertFalse(params.containsKey(PER_PAGE_PARAM));
|
||||
}
|
||||
|
||||
public void testWithOptions() {
|
||||
LinkToListOptions function = new LinkToListOptions();
|
||||
|
||||
ListOptions options = function.apply(Href.create("https://api.packet.net/projects?page=2&per_page=5"));
|
||||
assertNotNull(options);
|
||||
|
||||
Multimap<String, String> params = options.buildQueryParameters();
|
||||
assertEquals(getOnlyElement(params.get(PAGE_PARAM)), "2");
|
||||
assertEquals(getOnlyElement(params.get(PER_PAGE_PARAM)), "5");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0"?>
|
||||
<configuration scan="false">
|
||||
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>target/test-data/jclouds.log</file>
|
||||
<encoder>
|
||||
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="WIREFILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>target/test-data/jclouds-wire.log</file>
|
||||
<encoder>
|
||||
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="COMPUTEFILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>target/jclouds-compute.log</file>
|
||||
<encoder>
|
||||
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.jclouds">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="FILE" />
|
||||
</logger>
|
||||
<logger name="jclouds.compute">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="COMPUTEFILE" />
|
||||
</logger>
|
||||
<logger name="jclouds.wire">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="WIREFILE" />
|
||||
</logger>
|
||||
<logger name="jclouds.headers">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="WIREFILE" />
|
||||
</logger>
|
||||
|
||||
<root>
|
||||
<level value="INFO" />
|
||||
</root>
|
||||
</configuration>
|
|
@ -0,0 +1,315 @@
|
|||
{
|
||||
"projects": [
|
||||
{
|
||||
"id": "93907f48-adfe-43ed-ad89-0e6e83721a54",
|
||||
"name": "Cloudsoft CCS Testing",
|
||||
"created_at": "2016-09-15T08:50:58Z",
|
||||
"updated_at": "2017-01-05T09:36:53Z",
|
||||
"max_devices": {
|
||||
"baremetal_0": null,
|
||||
"baremetal_1": null,
|
||||
"baremetal_2": null,
|
||||
"baremetal_3": null,
|
||||
"baremetal_2a": null,
|
||||
"storage_1": null,
|
||||
"storage_2": null
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
|
||||
},
|
||||
{
|
||||
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
|
||||
},
|
||||
{
|
||||
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
|
||||
},
|
||||
{
|
||||
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
|
||||
}
|
||||
],
|
||||
"memberships": [
|
||||
{
|
||||
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
|
||||
}
|
||||
],
|
||||
"invitations": [],
|
||||
"payment_method": {
|
||||
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
|
||||
},
|
||||
"devices": [
|
||||
{
|
||||
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
|
||||
}
|
||||
],
|
||||
"ssh_keys": [
|
||||
{
|
||||
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
|
||||
}
|
||||
],
|
||||
"volumes": [],
|
||||
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
|
||||
},
|
||||
{
|
||||
"id": "93907f48-adfe-43ed-ad89-0e6e83721a53",
|
||||
"name": "Cloudsoft CCS Testing",
|
||||
"created_at": "2016-09-15T08:50:58Z",
|
||||
"updated_at": "2017-01-05T09:36:53Z",
|
||||
"max_devices": {
|
||||
"baremetal_0": null,
|
||||
"baremetal_1": null,
|
||||
"baremetal_2": null,
|
||||
"baremetal_3": null,
|
||||
"baremetal_2a": null,
|
||||
"storage_1": null,
|
||||
"storage_2": null
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
|
||||
},
|
||||
{
|
||||
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
|
||||
},
|
||||
{
|
||||
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
|
||||
},
|
||||
{
|
||||
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
|
||||
}
|
||||
],
|
||||
"memberships": [
|
||||
{
|
||||
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
|
||||
}
|
||||
],
|
||||
"invitations": [],
|
||||
"payment_method": {
|
||||
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
|
||||
},
|
||||
"devices": [
|
||||
{
|
||||
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
|
||||
}
|
||||
],
|
||||
"ssh_keys": [
|
||||
{
|
||||
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
|
||||
}
|
||||
],
|
||||
"volumes": [],
|
||||
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
|
||||
},
|
||||
{
|
||||
"id": "93907f48-adfe-43ed-ad89-0e6e83721a52",
|
||||
"name": "Cloudsoft CCS Testing",
|
||||
"created_at": "2016-09-15T08:50:58Z",
|
||||
"updated_at": "2017-01-05T09:36:53Z",
|
||||
"max_devices": {
|
||||
"baremetal_0": null,
|
||||
"baremetal_1": null,
|
||||
"baremetal_2": null,
|
||||
"baremetal_3": null,
|
||||
"baremetal_2a": null,
|
||||
"storage_1": null,
|
||||
"storage_2": null
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
|
||||
},
|
||||
{
|
||||
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
|
||||
},
|
||||
{
|
||||
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
|
||||
},
|
||||
{
|
||||
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
|
||||
}
|
||||
],
|
||||
"memberships": [
|
||||
{
|
||||
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
|
||||
}
|
||||
],
|
||||
"invitations": [],
|
||||
"payment_method": {
|
||||
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
|
||||
},
|
||||
"devices": [
|
||||
{
|
||||
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
|
||||
}
|
||||
],
|
||||
"ssh_keys": [
|
||||
{
|
||||
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
|
||||
}
|
||||
],
|
||||
"volumes": [],
|
||||
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
|
||||
},
|
||||
{
|
||||
"id": "93907f48-adfe-43ed-ad89-0e6e83721a51",
|
||||
"name": "Cloudsoft CCS Testing",
|
||||
"created_at": "2016-09-15T08:50:58Z",
|
||||
"updated_at": "2017-01-05T09:36:53Z",
|
||||
"max_devices": {
|
||||
"baremetal_0": null,
|
||||
"baremetal_1": null,
|
||||
"baremetal_2": null,
|
||||
"baremetal_3": null,
|
||||
"baremetal_2a": null,
|
||||
"storage_1": null,
|
||||
"storage_2": null
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
|
||||
},
|
||||
{
|
||||
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
|
||||
},
|
||||
{
|
||||
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
|
||||
},
|
||||
{
|
||||
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
|
||||
}
|
||||
],
|
||||
"memberships": [
|
||||
{
|
||||
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
|
||||
}
|
||||
],
|
||||
"invitations": [],
|
||||
"payment_method": {
|
||||
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
|
||||
},
|
||||
"devices": [
|
||||
{
|
||||
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
|
||||
}
|
||||
],
|
||||
"ssh_keys": [
|
||||
{
|
||||
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
|
||||
}
|
||||
],
|
||||
"volumes": [],
|
||||
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
|
||||
},
|
||||
{
|
||||
"id": "93907f48-adfe-43ed-ad89-0e6e83721a50",
|
||||
"name": "Cloudsoft CCS Testing",
|
||||
"created_at": "2016-09-15T08:50:58Z",
|
||||
"updated_at": "2017-01-05T09:36:53Z",
|
||||
"max_devices": {
|
||||
"baremetal_0": null,
|
||||
"baremetal_1": null,
|
||||
"baremetal_2": null,
|
||||
"baremetal_3": null,
|
||||
"baremetal_2a": null,
|
||||
"storage_1": null,
|
||||
"storage_2": null
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
|
||||
},
|
||||
{
|
||||
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
|
||||
},
|
||||
{
|
||||
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
|
||||
},
|
||||
{
|
||||
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
|
||||
}
|
||||
],
|
||||
"memberships": [
|
||||
{
|
||||
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
|
||||
}
|
||||
],
|
||||
"invitations": [],
|
||||
"payment_method": {
|
||||
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
|
||||
},
|
||||
"devices": [
|
||||
{
|
||||
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
|
||||
}
|
||||
],
|
||||
"ssh_keys": [
|
||||
{
|
||||
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
|
||||
}
|
||||
],
|
||||
"volumes": [],
|
||||
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"first": {
|
||||
"href": "/projects?page=1"
|
||||
},
|
||||
"previous": null,
|
||||
"self": {
|
||||
"href": "/projects?page=1"
|
||||
},
|
||||
"next": {
|
||||
"href": "/projects?page=2"
|
||||
},
|
||||
"last": {
|
||||
"href": "/projects?page=2"
|
||||
},
|
||||
"total": 8
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
{
|
||||
"projects": [
|
||||
{
|
||||
"id": "93907f48-adfe-43ed-ad89-0e6e83721a55",
|
||||
"name": "Cloudsoft CCS Testing",
|
||||
"created_at": "2016-09-15T08:50:58Z",
|
||||
"updated_at": "2017-01-05T09:36:53Z",
|
||||
"max_devices": {
|
||||
"baremetal_0": null,
|
||||
"baremetal_1": null,
|
||||
"baremetal_2": null,
|
||||
"baremetal_3": null,
|
||||
"baremetal_2a": null,
|
||||
"storage_1": null,
|
||||
"storage_2": null
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
|
||||
},
|
||||
{
|
||||
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
|
||||
},
|
||||
{
|
||||
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
|
||||
},
|
||||
{
|
||||
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
|
||||
}
|
||||
],
|
||||
"memberships": [
|
||||
{
|
||||
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
|
||||
}
|
||||
],
|
||||
"invitations": [],
|
||||
"payment_method": {
|
||||
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
|
||||
},
|
||||
"devices": [
|
||||
{
|
||||
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
|
||||
}
|
||||
],
|
||||
"ssh_keys": [
|
||||
{
|
||||
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
|
||||
}
|
||||
],
|
||||
"volumes": [],
|
||||
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
|
||||
},
|
||||
{
|
||||
"id": "93907f48-adfe-43ed-ad89-0e6e83721a56",
|
||||
"name": "Cloudsoft CCS Testing",
|
||||
"created_at": "2016-09-15T08:50:58Z",
|
||||
"updated_at": "2017-01-05T09:36:53Z",
|
||||
"max_devices": {
|
||||
"baremetal_0": null,
|
||||
"baremetal_1": null,
|
||||
"baremetal_2": null,
|
||||
"baremetal_3": null,
|
||||
"baremetal_2a": null,
|
||||
"storage_1": null,
|
||||
"storage_2": null
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
|
||||
},
|
||||
{
|
||||
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
|
||||
},
|
||||
{
|
||||
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
|
||||
},
|
||||
{
|
||||
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
|
||||
}
|
||||
],
|
||||
"memberships": [
|
||||
{
|
||||
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
|
||||
}
|
||||
],
|
||||
"invitations": [],
|
||||
"payment_method": {
|
||||
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
|
||||
},
|
||||
"devices": [
|
||||
{
|
||||
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
|
||||
}
|
||||
],
|
||||
"ssh_keys": [
|
||||
{
|
||||
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
|
||||
}
|
||||
],
|
||||
"volumes": [],
|
||||
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
|
||||
},
|
||||
{
|
||||
"id": "93907f48-adfe-43ed-ad89-0e6e83721a57",
|
||||
"name": "Cloudsoft CCS Testing",
|
||||
"created_at": "2016-09-15T08:50:58Z",
|
||||
"updated_at": "2017-01-05T09:36:53Z",
|
||||
"max_devices": {
|
||||
"baremetal_0": null,
|
||||
"baremetal_1": null,
|
||||
"baremetal_2": null,
|
||||
"baremetal_3": null,
|
||||
"baremetal_2a": null,
|
||||
"storage_1": null,
|
||||
"storage_2": null
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
|
||||
},
|
||||
{
|
||||
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
|
||||
},
|
||||
{
|
||||
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
|
||||
},
|
||||
{
|
||||
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
|
||||
}
|
||||
],
|
||||
"memberships": [
|
||||
{
|
||||
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
|
||||
},
|
||||
{
|
||||
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
|
||||
}
|
||||
],
|
||||
"invitations": [],
|
||||
"payment_method": {
|
||||
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
|
||||
},
|
||||
"devices": [
|
||||
{
|
||||
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
|
||||
}
|
||||
],
|
||||
"ssh_keys": [
|
||||
{
|
||||
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
|
||||
}
|
||||
],
|
||||
"volumes": [],
|
||||
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"first": {
|
||||
"href": "/projects?page=1"
|
||||
},
|
||||
"previous": {
|
||||
"href": "/projects?page=1"
|
||||
},
|
||||
"self": {
|
||||
"href": "/projects?page=2"
|
||||
},
|
||||
"next": null,
|
||||
"last": {
|
||||
"href": "/projects?page=2"
|
||||
},
|
||||
"total": 8
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"projects":[{"id":"93907f48-adfe-43ed-ad89-0e6e83721a54","name":"Cloudsoft CCS Testing","created_at":"2016-09-15T08:50:58Z","updated_at":"2017-01-05T09:36:53Z","max_devices":{"baremetal_0":null,"baremetal_1":null,"baremetal_2":null,"baremetal_3":null,"baremetal_2a":null,"storage_1":null,"storage_2":null},"members":[{"href":"/users/1140617d-262d-4502-a3d6-771d83c930da"},{"href":"/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"},{"href":"/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"},{"href":"/users/ad711bc3-6333-449a-a405-23ca81f38c00"}],"memberships":[{"href":"/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"},{"href":"/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"},{"href":"/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"},{"href":"/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"}],"invitations":[],"payment_method":{"href":"/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"},"devices":[{"href":"/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"}],"ssh_keys":[{"href":"/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"}],"volumes":[],"href":"/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"}],"meta":{"first":{"href":"/projects?page=1"},"previous":null,"self":{"href":"/projects?page=1"},"next":null,"last":{"href":"/projects?page=1"},"total":1}}
|
Loading…
Reference in New Issue