Skip to content
This repository was archived by the owner on Jun 30, 2023. It is now read-only.
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
@@ -0,0 +1,64 @@
package com.google.api.server.spi.config.model;

import com.google.api.server.spi.Constant;
import com.google.api.server.spi.config.scope.AuthScopeExpression;
import com.google.api.server.spi.config.scope.AuthScopeExpressions;
import com.google.api.server.spi.discovery.DiscoveryGenerator;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.io.Resources;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
import java.util.SortedMap;
import java.util.TreeMap;

/**
* Stores a list of OAuth2 scopes with their corresponding descriptions.
* Loads Google scopes from file googleScopeDescriptions.properties in same package.
*/
public class AuthScopeRepository {

private static final ImmutableMap<String, String> GOOGLE_SCOPE_DESCRIPTIONS
= loadScopeDescriptions("googleScopeDescriptions.properties");

private static ImmutableMap<String, String> loadScopeDescriptions(String fileName) {
try {
Properties properties = new Properties();
URL resourceFile = Resources.getResource(DiscoveryGenerator.class, fileName);
InputStream inputStream = resourceFile.openStream();
properties.load(inputStream);
inputStream.close();
return Maps.fromProperties(properties);
} catch (IOException e) {
throw new IllegalStateException("Cannot load scope descriptions from " + fileName, e);
}
}

private final SortedMap<String, String> descriptionsByScope = new TreeMap<>();

public AuthScopeRepository() {
//userinfo.email should always be requested, as it is required for authentication
add(AuthScopeExpressions.interpret(Constant.API_EMAIL_SCOPE));
}

public void add(AuthScopeExpression scopeExpression) {
for (String scope : scopeExpression.getAllScopes()) {
String description = MoreObjects.firstNonNull(GOOGLE_SCOPE_DESCRIPTIONS.get(scope), scope);
descriptionsByScope.put(scope, description);
}
}

/**
* Returns the added scopes and their descriptions.
* Unknown scopes will have the scope itself as description.
*
* @return a sorted map containing scopes as key, descriptions as value
*/
public SortedMap<String, String> getDescriptionsByScope() {
return descriptionsByScope;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.api.client.util.Preconditions;
import com.google.api.server.spi.Constant;
import com.google.api.server.spi.ObjectMapperUtil;
import com.google.api.server.spi.Strings;
import com.google.api.server.spi.TypeLoader;
Expand All @@ -33,7 +32,9 @@
import com.google.api.server.spi.config.model.Schema;
import com.google.api.server.spi.config.model.Schema.Field;
import com.google.api.server.spi.config.model.SchemaRepository;
import com.google.api.server.spi.config.model.AuthScopeRepository;
import com.google.api.server.spi.config.model.StandardParameters;
import com.google.api.server.spi.config.scope.AuthScopeExpression;
import com.google.api.server.spi.config.scope.AuthScopeExpressions;
import com.google.api.services.discovery.model.DirectoryList;
import com.google.api.services.discovery.model.DirectoryList.Items;
Expand Down Expand Up @@ -64,9 +65,10 @@
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.Map.Entry;
import java.util.TreeMap;

/**
Expand All @@ -76,12 +78,6 @@ public class DiscoveryGenerator {
private static final Splitter DOT_SPLITTER = Splitter.on('.');
private static final ObjectMapper objectMapper = ObjectMapperUtil.createStandardObjectMapper();
private static final RestDescription REST_SKELETON = new RestDescription()
.setAuth(new Auth()
.setOauth2(new Oauth2()
.setScopes(Maps.newHashMap(ImmutableMap.of(
Constant.API_EMAIL_SCOPE,
new ScopesElement()
.setDescription("View your email address"))))))
.setBatchPath("batch")
.setDescription("This is an API")
.setDiscoveryVersion("v1")
Expand Down Expand Up @@ -133,7 +129,7 @@ public Result writeDiscovery(
}

private RestDescription writeApi(ApiKey apiKey, Iterable<ApiConfig> apiConfigs,
DiscoveryContext context, SchemaRepository repo) {
DiscoveryContext context, SchemaRepository schemaRepo) {
// The first step is to scan all methods and try to extract a base path, aka a common prefix
// for all methods. This prefix must end in a slash and can't contain any path parameters.
String servicePath = computeApiServicePath(apiConfigs);
Expand All @@ -147,6 +143,8 @@ private RestDescription writeApi(ApiKey apiKey, Iterable<ApiConfig> apiConfigs,
.setServicePath(servicePath)
.setVersion(apiKey.getVersion());

final AuthScopeRepository scopeRepo = new AuthScopeRepository();

for (ApiConfig config : apiConfigs) {
// API descriptions should be identical across all configs, but the last one will take
// precedence here if there happens to be divergence.
Expand Down Expand Up @@ -174,14 +172,21 @@ private RestDescription writeApi(ApiKey apiKey, Iterable<ApiConfig> apiConfigs,
if (config.getCanonicalName() != null) {
doc.setCanonicalName(config.getCanonicalName());
}
scopeRepo.add(config.getScopeExpression());
for (ApiMethodConfig methodConfig : config.getApiClassConfig().getMethods().values()) {
if (!methodConfig.isIgnored()) {
writeApiMethod(config, servicePath, doc, methodConfig, repo);
writeApiMethod(config, servicePath, doc, methodConfig, schemaRepo, scopeRepo);
}
}
}

List<Schema> schemas = repo.getAllSchemaForApi(apiKey);
Map<String, ScopesElement> scopeElements = new LinkedHashMap<>();
for (Entry<String, String> entry : scopeRepo.getDescriptionsByScope().entrySet()) {
scopeElements.put(entry.getKey(), new ScopesElement().setDescription(entry.getValue()));
}
doc.setAuth(new Auth().setOauth2(new Oauth2().setScopes(scopeElements)));

List<Schema> schemas = schemaRepo.getAllSchemaForApi(apiKey);
if (!schemas.isEmpty()) {
Map<String, JsonSchema> docSchemas = Maps.newTreeMap();
for (Schema schema : schemas) {
Expand All @@ -193,16 +198,18 @@ private RestDescription writeApi(ApiKey apiKey, Iterable<ApiConfig> apiConfigs,
}

private void writeApiMethod(ApiConfig config, String servicePath, RestDescription doc,
ApiMethodConfig methodConfig, SchemaRepository repo) {
ApiMethodConfig methodConfig, SchemaRepository schemaRepo, AuthScopeRepository scopeRepo) {
List<String> parts = DOT_SPLITTER.splitToList(methodConfig.getFullMethodName());
Map<String, RestMethod> methods = getMethodMapFromDoc(doc, parts);
Map<String, JsonSchema> parameters = convertMethodParameters(methodConfig);
AuthScopeExpression scopeExpression = methodConfig.getScopeExpression();
RestMethod method = new RestMethod()
.setDescription(methodConfig.getDescription())
.setHttpMethod(methodConfig.getHttpMethod())
.setId(methodConfig.getFullMethodName())
.setPath(methodConfig.getCanonicalPath().substring(servicePath.length()))
.setScopes(AuthScopeExpressions.encodeMutable(methodConfig.getScopeExpression()));
.setScopes(AuthScopeExpressions.encodeMutable(scopeExpression));
scopeRepo.add(scopeExpression);
List<String> parameterOrder = computeParameterOrder(methodConfig);
if (!parameterOrder.isEmpty()) {
method.setParameterOrder(parameterOrder);
Expand All @@ -213,13 +220,13 @@ private void writeApiMethod(ApiConfig config, String servicePath, RestDescriptio
ApiParameterConfig requestParamConfig = getAndCheckMethodRequestResource(methodConfig);
if (requestParamConfig != null) {
TypeToken<?> requestType = requestParamConfig.getSchemaBaseType();
Schema schema = repo.getOrAdd(requestType, config);
Schema schema = schemaRepo.getOrAdd(requestType, config);
method.setRequest(new Request().set$ref(schema.name()).setParameterName("resource"));
}
if (methodConfig.hasResourceInResponse()) {
TypeToken<?> returnType =
ApiAnnotationIntrospector.getSchemaType(methodConfig.getReturnType(), config);
Schema schema = repo.getOrAdd(returnType, config);
Schema schema = schemaRepo.getOrAdd(returnType, config);
method.setResponse(new Response().set$ref(schema.name()));
}
methods.put(parts.get(parts.size() - 1), method);
Expand Down
Loading