Skip to content

Commit

Permalink
Fix async when graphql not enabled (#3246)
Browse files Browse the repository at this point in the history
* Fix async when graphql not enabled

* Allow elide-datastore-aggregation to be excluded

* Refactor

* Set injectors for EntityDictionary in MetaDataStore, TemplateConfigValidator and DynamicConfigValidator

---------

Co-authored-by: Aaron Klish <[email protected]>
  • Loading branch information
justin-tay and aklish committed Jul 14, 2024
1 parent 9b39eb4 commit 566234e
Show file tree
Hide file tree
Showing 39 changed files with 771 additions and 193 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.yahoo.elide.core.security.ChangeSpec;
import com.yahoo.elide.core.security.RequestScope;
import com.yahoo.elide.graphql.QueryRunner;
import com.yahoo.elide.graphql.QueryRunners;

import java.time.Duration;
import java.util.Optional;
Expand All @@ -44,7 +45,8 @@ public void validateOptions(AsyncApi query, RequestScope requestScope) {
super.validateOptions(query, requestScope);

if (query.getQueryType().equals(QueryType.GRAPHQL_V1_0)) {
QueryRunner runner = getAsyncExecutorService().getRunners().get(requestScope.getRoute().getApiVersion());
QueryRunner runner = getAsyncExecutorService().getProviders().getProvider(QueryRunners.class)
.getRunner(requestScope.getRoute().getApiVersion());
if (runner == null) {
throw new InvalidOperationException("Invalid API Version");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.yahoo.elide.core.exceptions.InvalidOperationException;
import com.yahoo.elide.core.security.User;
import com.yahoo.elide.graphql.QueryRunner;
import com.yahoo.elide.graphql.QueryRunners;

import lombok.extern.slf4j.Slf4j;

Expand All @@ -33,7 +34,7 @@ public GraphQLAsyncQueryOperation(AsyncExecutorService service, AsyncApi queryOb
public ElideResponse<String> execute(AsyncApi queryObj, RequestScope scope) throws URISyntaxException {
User user = scope.getUser();
String apiVersion = scope.getRoute().getApiVersion();
QueryRunner runner = getService().getRunners().get(apiVersion);
QueryRunner runner = getService().getProviders().getProvider(QueryRunners.class).getRunner(apiVersion);
if (runner == null) {
throw new InvalidOperationException("Invalid API Version");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.yahoo.elide.core.request.route.Route;
import com.yahoo.elide.core.security.User;
import com.yahoo.elide.graphql.GraphQLRequestScope;
import com.yahoo.elide.graphql.GraphQLSettings;
import com.yahoo.elide.graphql.QueryRunner;
import com.yahoo.elide.graphql.parser.GraphQLEntityProjectionMaker;
import com.yahoo.elide.graphql.parser.GraphQLProjectionInfo;
Expand Down Expand Up @@ -79,4 +80,17 @@ public Collection<EntityProjection> getProjections(TableExport export, RequestSc

return projectionInfo.getProjections().values();
}

@Override
public String getBaseUrl(RequestScope requestScope) {
String baseUrl = requestScope.getRoute().getBaseUrl();
GraphQLSettings graphqlSettings = requestScope.getElideSettings().getSettings(GraphQLSettings.class);
if (graphqlSettings != null) {
String graphqlApiPath = graphqlSettings.getPath();
if (graphqlApiPath != null && baseUrl.endsWith(graphqlApiPath)) {
baseUrl = baseUrl.substring(0, baseUrl.length() - graphqlApiPath.length());
}
}
return baseUrl;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public JsonApiAsyncQueryOperation(AsyncExecutorService service, AsyncApi queryOb
@Override
public ElideResponse<String> execute(AsyncApi queryObj, RequestScope scope)
throws URISyntaxException {
JsonApi jsonApi = getService().getJsonApi();
JsonApi jsonApi = getService().getProviders().getProvider(JsonApi.class);
User user = scope.getUser();
String apiVersion = scope.getRoute().getApiVersion();
UUID requestUUID = UUID.fromString(queryObj.getRequestId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.yahoo.elide.core.security.User;
import com.yahoo.elide.jsonapi.EntityProjectionMaker;
import com.yahoo.elide.jsonapi.JsonApiRequestScope;
import com.yahoo.elide.jsonapi.JsonApiSettings;

import org.apache.hc.core5.net.URIBuilder;

Expand Down Expand Up @@ -99,4 +100,17 @@ public Collection<EntityProjection> getProjections(TableExport export, RequestSc
}
return Collections.singletonList(projection);
}

@Override
public String getBaseUrl(RequestScope requestScope) {
String baseUrl = requestScope.getRoute().getBaseUrl();
JsonApiSettings jsonApiSettings = requestScope.getElideSettings().getSettings(JsonApiSettings.class);
if (jsonApiSettings != null) {
String jsonApiPath = jsonApiSettings.getPath();
if (jsonApiPath != null && baseUrl.endsWith(jsonApiPath)) {
baseUrl = baseUrl.substring(0, baseUrl.length() - jsonApiPath.length());
}
}
return baseUrl;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
import com.yahoo.elide.core.datastore.DataStoreTransaction;
import com.yahoo.elide.core.exceptions.BadRequestException;
import com.yahoo.elide.core.request.EntityProjection;
import com.yahoo.elide.graphql.GraphQLSettings;
import com.yahoo.elide.jsonapi.JsonApiSettings;

import org.apache.commons.lang3.StringUtils;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -149,16 +149,6 @@ public AsyncApiResult call() {
return exportResult;
}

private Flux<String> concatStringWithFlux(String toConcat, Flux<String> observable,
boolean stringFirst) {
if (toConcat == null) {
return observable;
}

return stringFirst ? Flux.just(toConcat).concatWith(observable)
: observable.concatWith(Flux.just(toConcat));
}

/**
* Initializes a new RequestScope for the export operation with the submitted query.
* @param exportObj TableExport type object.
Expand All @@ -178,25 +168,9 @@ public abstract RequestScope getRequestScope(TableExport exportObj, RequestScope
*/
public String generateDownloadURL(TableExport exportObj, RequestScope scope) {
AsyncSettings asyncSettings = scope.getElideSettings().getSettings(AsyncSettings.class);
JsonApiSettings jsonApiSettings = scope.getElideSettings().getSettings(JsonApiSettings.class);
GraphQLSettings graphqlSettings = scope.getElideSettings().getSettings(GraphQLSettings.class);

String downloadPath = asyncSettings.getExport().getPath();
String baseURL = scope.getRoute().getBaseUrl();
if (jsonApiSettings != null) {
String jsonApiPath = jsonApiSettings.getPath();
if (jsonApiPath != null && baseURL.endsWith(jsonApiPath)) {
baseURL = baseURL.substring(0, baseURL.length() - jsonApiPath.length());
}
}
if (graphqlSettings != null) {
String graphqlApiPath = graphqlSettings.getPath();
if (graphqlApiPath != null && baseURL.endsWith(graphqlApiPath)) {
baseURL = baseURL.substring(0, baseURL.length() - graphqlApiPath.length());
}
}
String tableExportID = getTableExportID(exportObj);
return baseURL + downloadPath + "/" + tableExportID;
return getBaseUrl(scope, downloadPath) + "/" + tableExportID;
}

/**
Expand All @@ -222,6 +196,28 @@ private void validateProjections(Collection<EntityProjection> projections) {
validators.forEach(validator -> validator.validateProjection(projections));
}

/**
* Gets the base url for tableExport.
*
* @param scope RequestScope.
* @param prefix the prefix.
* @return the base url.
*/
protected String getBaseUrl(RequestScope scope, String prefix) {
String baseUrl = scope.getElideSettings().getBaseUrl();
if (StringUtils.isEmpty(baseUrl)) {
baseUrl = getBaseUrl(scope);
}
if (prefix.length() > 1) {
if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.substring(0, baseUrl.length() - 1) + prefix;
} else {
baseUrl = baseUrl + prefix;
}
}
return baseUrl;
}

/**
* Generate Entity Projection from the query.
* @param exportObj TableExport type object.
Expand All @@ -230,4 +226,11 @@ private void validateProjections(Collection<EntityProjection> projections) {
*/
public abstract Collection<EntityProjection> getProjections(TableExport exportObj, RequestScope requestScope);

/**
* Gets the base url.
*
* @param requestScope requestScope object
* @return the base url
*/
public abstract String getBaseUrl(RequestScope requestScope);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,13 @@
import com.yahoo.elide.async.operation.AsyncApiUpdateOperation;
import com.yahoo.elide.async.service.dao.AsyncApiDao;
import com.yahoo.elide.core.security.User;
import com.yahoo.elide.graphql.QueryRunner;
import com.yahoo.elide.jsonapi.JsonApi;

import graphql.execution.DataFetcherExceptionHandler;
import graphql.execution.SimpleDataFetcherExceptionHandler;
import jakarta.inject.Inject;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
Expand All @@ -45,13 +38,12 @@ public class AsyncExecutorService {

public static final int DEFAULT_THREAD_POOL_SIZE = 6;

private Elide elide;
private JsonApi jsonApi;
private Map<String, QueryRunner> runners;
private ExecutorService executor;
private ExecutorService updater;
private AsyncApiDao asyncApiDao;
private ThreadLocal<AsyncApiResultFuture> asyncResultFutureThreadLocal = new ThreadLocal<>();
private final Elide elide;
private final ExecutorService executor;
private final ExecutorService updater;
private final AsyncApiDao asyncApiDao;
private final ThreadLocal<AsyncApiResultFuture> asyncResultFutureThreadLocal = new ThreadLocal<>();
private final AsyncProviderService providers;

/**
* A Future with Synchronous Execution Complete Flag.
Expand All @@ -64,19 +56,12 @@ private static class AsyncApiResultFuture {

@Inject
public AsyncExecutorService(Elide elide, ExecutorService executor, ExecutorService updater, AsyncApiDao asyncApiDao,
Optional<DataFetcherExceptionHandler> optionalDataFetcherExceptionHandler) {
AsyncProviderService asyncProviderService) {
this.elide = elide;
runners = new HashMap<>();

for (String apiVersion : elide.getElideSettings().getEntityDictionary().getApiVersions()) {
runners.put(apiVersion, new QueryRunner(elide, apiVersion,
optionalDataFetcherExceptionHandler.orElseGet(SimpleDataFetcherExceptionHandler::new)));
}

this.jsonApi = new JsonApi(this.elide);
this.executor = executor;
this.updater = updater;
this.asyncApiDao = asyncApiDao;
this.providers = asyncProviderService;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2024, the original author or authors.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package com.yahoo.elide.async.service;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

/**
* Service that returns the providers needed.
*/
public class AsyncProviderService {
private final Map<String, Object> providers;

/**
* Constructor.
*
* @param providers the providers
*/
public AsyncProviderService(Map<String, Object> providers) {
this.providers = providers;
}

/**
* Gets the provider.
*
* @param <T> the provider type
* @param clazz the provider type
* @return the provider
*/
public <T> T getProvider(Class<T> clazz) {
return clazz.cast(providers.get(clazz.getName()));
}

/**
* Gets the builder for {@link AsyncProviderService}.
*
* @return the builder.
*/
public static AsyncProviderServiceBuilder builder() {
return new AsyncProviderServiceBuilder();
}

/**
* Builder for {@link AsyncProviderService}.
*/
public static class AsyncProviderServiceBuilder
extends AsyncProviderServiceBuilderSupport<AsyncProviderServiceBuilder> {
public AsyncProviderService build() {
return new AsyncProviderService(this.providers);
}

@Override
public AsyncProviderServiceBuilder self() {
return this;
}
}

/**
* Builder Support for {@link AsyncProviderService}.
*
* @param <S> the builder class
*/
public static abstract class AsyncProviderServiceBuilderSupport<S> {
public abstract S self();
protected Map<String, Object> providers = new HashMap<>();

/**
* Sets the providers.
*
* @param providers the providers
* @return the builder
*/
public S providers(Map<String, Object> providers) {
this.providers = providers;
return self();
}

/**
* Customize the providers.
*
* @param customizer the customizer
* @return the builder
*/
public S providers(Consumer<Map<String, Object>> customizer) {
customizer.accept(this.providers);
return self();
}

/**
* Sets the provider.
*
* @param <T> the provider type
* @param clazz the provider class
* @param provider the provider instance
* @return the builder
*/
public <T> S provider(Class<T> clazz, T provider) {
// The clazz is not obtained from the provider object as it may be a derived class or proxy
this.providers.put(clazz.getName(), provider);
return self();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright 2024, the original author or authors.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package com.yahoo.elide.async.service;

import com.yahoo.elide.async.service.AsyncProviderService.AsyncProviderServiceBuilder;

/**
* Customizer for {@link AsyncProviderServiceBuilder}.
*/
public interface AsyncProviderServiceBuilderCustomizer {
void customize(AsyncProviderServiceBuilder builder);
}
Loading

0 comments on commit 566234e

Please sign in to comment.