Skip to content

Improve generated Swagger spec compatibility with Endpoints Portal #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public String genOpenApiDoc(
SwaggerContext swaggerContext = new SwaggerContext()
.setHostname(hostname)
.setBasePath(basePath);
Swagger swagger = generator.writeSwagger(apiConfigs, true, swaggerContext);
Swagger swagger = generator.writeSwagger(apiConfigs, swaggerContext);
String swaggerStr = Json.mapper().writer(new EndpointsPrettyPrinter())
.writeValueAsString(swagger);
if (outputToDisk) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,23 @@ private Constant() {}
public static final String SKIP_CLIENT_ID_CHECK = "*";

/**
* Root URL of discovery doc generation API. This is on a host that Endpoints project owns so
* that even if producer has not picked an App Engine app host, this call can still succeed.
* Friendly name to refer to Google ID token authentication with accounts.google.com issuer.
*/
public static final String DISCOVERY_GEN_ROOT = "https://webapis-discovery.appspot.com/_ah/api";
public static final String GOOGLE_ID_TOKEN_ALT = "google_id_token_legacy";

/**
* Friendly name to refer to Google ID token authentication with accounts.google.com issuer.
* Google ID token authentication variant with https://accounts.google.com issuer.
*/
public static final String GOOGLE_ID_TOKEN_NAME = "google_id_token";

/**
* Google ID token authentication variant with https://accounts.google.com issuer.
* Google JWKS URI
*/
public static final String GOOGLE_JWKS_URI = "https://www.googleapis.com/oauth2/v1/certs";

/**
* Google OAuth2 authentication URL
*/
public static final String GOOGLE_ID_TOKEN_NAME_HTTPS = "google_id_token_https";
public static final String GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@
*/
package com.google.api.server.spi;

import com.google.api.server.spi.EndpointMethod.ResolvedSignature;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.reflect.TypeToken;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.List;
import java.util.Map;

Expand All @@ -42,7 +46,7 @@ public class MethodHierarchyReader {
private final Class<?> endpointClass;
// Map from method signatures to a list of method overrides for that signature (ordered
// subclass -> superclass).
private ListMultimap<EndpointMethod.ResolvedSignature, EndpointMethod> endpointMethods;
private Multimap<ResolvedSignature, EndpointMethod> endpointMethods;

/**
* Constructs a {@code MethodHierarchyReader} for the given class type.
Expand All @@ -60,8 +64,8 @@ public MethodHierarchyReader(Class<?> endpointClass) {

private void readMethodHierarchyIfNecessary() {
if (endpointMethods == null) {
ImmutableListMultimap.Builder<EndpointMethod.ResolvedSignature, EndpointMethod> builder =
ImmutableListMultimap.builder();
ImmutableMultimap.Builder<EndpointMethod.ResolvedSignature, EndpointMethod> builder =
ImmutableMultimap.builder();
buildServiceMethods(builder, TypeToken.of(endpointClass));
endpointMethods = builder.build();
}
Expand All @@ -72,24 +76,10 @@ private void readMethodHierarchyIfNecessary() {
*
* @param overrides A list of method overrides ordered subclass -> superclass.
*/
private EndpointMethod getLeafMethod(List<EndpointMethod> overrides) {
private EndpointMethod getLeafMethod(Collection<EndpointMethod> overrides) {
// Because the list is ordered subclass -> superclass, index 0 will always contain the leaf
// subclass implementation.
return overrides.get(0);
}

/**
* Returns {@link ListMultimap#asMap multimap.asMap()}, with its type
* corrected from {@code Map<K, Collection<V>>} to {@code Map<K, List<V>>}.
*/
// Copied from com.google.common.collect.Multimaps. We can't use the actual method from
// that class as appengine build magic gives us an older version of guava that doesn't yet have
// this method.
// TODO: Switch to Multimaps.asMap() once it becomes available in appengine.
@SuppressWarnings("unchecked")
// safe by specification of ListMultimap.asMap()
private static <K, V> Map<K, List<V>> asMap(ListMultimap<K, V> multimap) {
return (Map<K, List<V>>) (Map<K, ?>) multimap.asMap();
return overrides.iterator().next();
}

/**
Expand All @@ -99,7 +89,7 @@ private static <K, V> Map<K, List<V>> asMap(ListMultimap<K, V> multimap) {
public Iterable<Method> getLeafMethods() {
readMethodHierarchyIfNecessary();
ImmutableList.Builder<Method> builder = ImmutableList.builder();
for (List<EndpointMethod> overrides : asMap(endpointMethods).values()) {
for (Collection<EndpointMethod> overrides : endpointMethods.asMap().values()) {
builder.add(getLeafMethod(overrides).getMethod());
}
return builder.build();
Expand All @@ -113,7 +103,7 @@ public Iterable<Method> getLeafMethods() {
public Iterable<EndpointMethod> getLeafEndpointMethods() {
readMethodHierarchyIfNecessary();
ImmutableList.Builder<EndpointMethod> builder = ImmutableList.builder();
for (List<EndpointMethod> overrides : asMap(endpointMethods).values()) {
for (Collection<EndpointMethod> overrides : endpointMethods.asMap().values()) {
builder.add(getLeafMethod(overrides));
}
return builder.build();
Expand All @@ -127,7 +117,7 @@ public Iterable<EndpointMethod> getLeafEndpointMethods() {
public Iterable<List<Method>> getMethodOverrides() {
readMethodHierarchyIfNecessary();
ImmutableList.Builder<List<Method>> builder = ImmutableList.builder();
for (List<EndpointMethod> overrides : asMap(endpointMethods).values()) {
for (Collection<EndpointMethod> overrides : endpointMethods.asMap().values()) {
ImmutableList.Builder<Method> methodBuilder = ImmutableList.builder();
for (EndpointMethod method : overrides) {
methodBuilder.add(method.getMethod());
Expand All @@ -142,9 +132,9 @@ public Iterable<List<Method>> getMethodOverrides() {
* Bridge methods are ignored. For each method, all valid method implementations are included,
* ordered subclass to superclass. Methods are stored in the EndpointMethod container.
*/
public Iterable<List<EndpointMethod>> getEndpointOverrides() {
public Iterable<Collection<EndpointMethod>> getEndpointOverrides() {
readMethodHierarchyIfNecessary();
return asMap(endpointMethods).values();
return endpointMethods.asMap().values();
}

/**
Expand All @@ -155,7 +145,7 @@ public Iterable<List<EndpointMethod>> getEndpointOverrides() {
public Map<String, Method> getNameToLeafMethodMap() {
readMethodHierarchyIfNecessary();
ImmutableMap.Builder<String, Method> builder = ImmutableMap.builder();
for (List<EndpointMethod> overrides : asMap(endpointMethods).values()) {
for (Collection<EndpointMethod> overrides : endpointMethods.asMap().values()) {
Method leafMethod = getLeafMethod(overrides).getMethod();
builder.put(leafMethod.getName(), leafMethod);
}
Expand All @@ -170,7 +160,7 @@ public Map<String, Method> getNameToLeafMethodMap() {
public ListMultimap<String, EndpointMethod> getNameToEndpointOverridesMap() {
readMethodHierarchyIfNecessary();
ImmutableListMultimap.Builder<String, EndpointMethod> builder = ImmutableListMultimap.builder();
for (List<EndpointMethod> overrides : asMap(endpointMethods).values()) {
for (Collection<EndpointMethod> overrides : endpointMethods.asMap().values()) {
builder.putAll(getLeafMethod(overrides).getMethod().getName(), overrides);
}
return builder.build();
Expand All @@ -183,13 +173,14 @@ public ListMultimap<String, EndpointMethod> getNameToEndpointOverridesMap() {
* @param serviceType is the class object being inspected for service methods
*/
private void buildServiceMethods(
ImmutableListMultimap.Builder<EndpointMethod.ResolvedSignature, EndpointMethod> builder,
ImmutableMultimap.Builder<EndpointMethod.ResolvedSignature, EndpointMethod> builder,
TypeToken<?> serviceType) {
for (TypeToken<?> typeToken : serviceType.getTypes().classes()) {
Class<?> serviceClass = typeToken.getRawType();
if (Object.class.equals(serviceClass)) {
return;
}
//getDeclaredMethods returns methods in random order, so must not assume any specific order
for (Method method : serviceClass.getDeclaredMethods()) {
if (!isServiceMethod(method)) {
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,15 @@
* The location of the JSON web key set used to verify tokens generated by this issuer.
*/
String jwksUri() default "";

/**
* The authorization URL to use for the authorization flow.
*/
String authorizationUrl() default "";

/**
* When true, scopes will be used in authorization flow.
*/
boolean useScopesInAuthFlow() default false;

}
Loading