Skip to content
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

Validators for JSON and Graphql Exporter #1833

Merged
merged 2 commits into from
Feb 12, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2021, Yahoo Inc.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package com.yahoo.elide.async.export.validator;

import com.yahoo.elide.core.exceptions.BadRequestException;
import com.yahoo.elide.core.request.EntityProjection;

import java.util.Collection;

/**
* Validates none of the projections have relationships.
*/
public class NoRelationshipsProjectionValidator implements Validator {

@Override
public void validateProjection(Collection<EntityProjection> projections) {
for (EntityProjection projection : projections) {
if (!projection.getRelationships().isEmpty()) {
throw new BadRequestException(
"Export is not supported for Query that requires traversing Relationships.");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2021, Yahoo Inc.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package com.yahoo.elide.async.export.validator;

import com.yahoo.elide.core.exceptions.BadRequestException;
import com.yahoo.elide.core.request.EntityProjection;

import java.util.Collection;

/**
* Validates each projection in collection have one projection only.
*/
public class SingleRootProjectionValidator implements Validator {

@Override
public void validateProjection(Collection<EntityProjection> projections) {
if (projections.size() != 1) {
throw new BadRequestException("Export is only supported for single Query with one root projection.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
*/
package com.yahoo.elide.async.export.validator;

import graphql.language.Field;
import com.yahoo.elide.core.request.EntityProjection;

import java.util.Collection;

/**
* Utility interface used to validate Entity Projections.
Expand All @@ -14,8 +16,7 @@ public interface Validator {

/**
* Validates the EntityProjection.
* @param entityType Class of the Entity to be validated.
* @param field GraphQL Field.
* @param projections Collection of EntityProjections to validate.
*/
public void validateProjection(Class<?> entityType, Field field);
public void validateProjection(Collection<EntityProjection> projections);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

import com.yahoo.elide.Elide;
import com.yahoo.elide.async.export.formatter.TableExportFormatter;
import com.yahoo.elide.async.export.validator.NoRelationshipsProjectionValidator;
import com.yahoo.elide.async.models.AsyncAPI;
import com.yahoo.elide.async.models.TableExport;
import com.yahoo.elide.async.service.AsyncExecutorService;
import com.yahoo.elide.async.service.storageengine.ResultStorageEngine;
import com.yahoo.elide.core.RequestScope;
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.core.security.User;
import com.yahoo.elide.graphql.GraphQLRequestScope;
Expand All @@ -27,10 +27,10 @@
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.UUID;

/**
Expand All @@ -41,7 +41,8 @@ public class GraphQLTableExportOperation extends TableExportOperation {

public GraphQLTableExportOperation(TableExportFormatter formatter, AsyncExecutorService service,
AsyncAPI export, RequestScope scope, ResultStorageEngine engine) {
super(formatter, service, export, scope, engine);
super(formatter, service, export, scope, engine,
Arrays.asList(new NoRelationshipsProjectionValidator()));
}

@Override
Expand All @@ -53,9 +54,8 @@ public RequestScope getRequestScope(TableExport export, User user, String apiVer
}

@Override
public EntityProjection getProjection(TableExport export, RequestScope scope)
throws BadRequestException {
EntityProjection projection;
public Collection<EntityProjection> getProjections(TableExport export, RequestScope scope) {
GraphQLProjectionInfo projectionInfo;
try {
String graphQLDocument = export.getQuery();
Elide elide = getService().getElide();
Expand All @@ -65,20 +65,13 @@ public EntityProjection getProjection(TableExport export, RequestScope scope)
Map<String, Object> variables = QueryRunner.extractVariables(mapper, node);
String queryString = QueryRunner.extractQuery(node);

GraphQLProjectionInfo projectionInfo =
new GraphQLEntityProjectionMaker(elide.getElideSettings(), variables, scope.getApiVersion())
.make(queryString);

//TODO Call Validators.
Optional<Entry<String, EntityProjection>> optionalEntry =
projectionInfo.getProjections().entrySet().stream().findFirst();

projection = optionalEntry.isPresent() ? optionalEntry.get().getValue() : null;
projectionInfo = new GraphQLEntityProjectionMaker(elide.getElideSettings(), variables,
scope.getApiVersion()).make(queryString);

} catch (IOException e) {
throw new IllegalStateException(e);
}

return projection;
return projectionInfo.getProjections().values();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import com.yahoo.elide.Elide;
import com.yahoo.elide.async.export.formatter.TableExportFormatter;
import com.yahoo.elide.async.export.validator.NoRelationshipsProjectionValidator;
import com.yahoo.elide.async.models.AsyncAPI;
import com.yahoo.elide.async.models.TableExport;
import com.yahoo.elide.async.service.AsyncExecutorService;
Expand All @@ -22,6 +23,8 @@
import lombok.extern.slf4j.Slf4j;

import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;

Expand All @@ -35,7 +38,8 @@ public class JSONAPITableExportOperation extends TableExportOperation {

public JSONAPITableExportOperation(TableExportFormatter formatter, AsyncExecutorService service,
AsyncAPI export, RequestScope scope, ResultStorageEngine engine) {
super(formatter, service, export, scope, engine);
super(formatter, service, export, scope, engine,
Arrays.asList(new NoRelationshipsProjectionValidator()));
}

@Override
Expand All @@ -53,7 +57,7 @@ public RequestScope getRequestScope(TableExport export, User user, String apiVer
}

@Override
public EntityProjection getProjection(TableExport export, RequestScope scope) throws BadRequestException {
public Collection<EntityProjection> getProjections(TableExport export, RequestScope scope) {
EntityProjection projection = null;
try {
URIBuilder uri = new URIBuilder(export.getQuery());
Expand All @@ -64,6 +68,6 @@ public EntityProjection getProjection(TableExport export, RequestScope scope) th
} catch (URISyntaxException e) {
throw new BadRequestException(e.getMessage());
}
return projection;
return Collections.singletonList(projection);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import com.yahoo.elide.Elide;
import com.yahoo.elide.async.export.formatter.TableExportFormatter;
import com.yahoo.elide.async.export.validator.SingleRootProjectionValidator;
import com.yahoo.elide.async.export.validator.Validator;
import com.yahoo.elide.async.models.AsyncAPI;
import com.yahoo.elide.async.models.AsyncAPIResult;
import com.yahoo.elide.async.models.TableExport;
Expand All @@ -28,8 +30,12 @@
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import java.util.concurrent.Callable;
Expand All @@ -45,14 +51,16 @@ public abstract class TableExportOperation implements Callable<AsyncAPIResult> {
private TableExport exportObj;
private RequestScope scope;
private ResultStorageEngine engine;
private List<Validator> validators = new ArrayList<>(Arrays.asList(new SingleRootProjectionValidator()));

public TableExportOperation(TableExportFormatter formatter, AsyncExecutorService service,
AsyncAPI exportObj, RequestScope scope, ResultStorageEngine engine) {
AsyncAPI exportObj, RequestScope scope, ResultStorageEngine engine, List<Validator> validators) {
this.formatter = formatter;
this.service = service;
this.exportObj = (TableExport) exportObj;
this.scope = scope;
this.engine = engine;
this.validators.addAll(validators);
}

@Override
Expand All @@ -64,7 +72,9 @@ public AsyncAPIResult call() {
try (DataStoreTransaction tx = elide.getDataStore().beginTransaction()) {

RequestScope requestScope = getRequestScope(exportObj, scope.getUser(), apiVersion, tx);
EntityProjection projection = getProjection(exportObj, requestScope);
Collection<EntityProjection> projections = getProjections(exportObj, requestScope);
validateProjections(projections);
EntityProjection projection = projections.iterator().next();

Observable<PersistentResource> observableResults = export(exportObj, requestScope, projection);

Expand All @@ -85,7 +95,7 @@ public AsyncAPIResult call() {
exportResult.setUrl(new URL(generateDownloadURL(exportObj, (RequestScope) scope)));
exportResult.setRecordCount(recordNumber);
} catch (BadRequestException e) {
exportResult.setMessage("Bad Request body");
exportResult.setMessage(e.getMessage());
} catch (MalformedURLException e) {
exportResult.setMessage("Download url generation failure.");
} catch (Exception e) {
Expand Down Expand Up @@ -194,14 +204,16 @@ protected TableExport storeResults(TableExport exportObj, ResultStorageEngine re
return resultStorageEngine.storeResults(exportObj, result);
}

private void validateProjections(Collection<EntityProjection> projections) {
validators.forEach(validator -> validator.validateProjection(projections));
}

/**
* Generate Entity Projection from the query.
* @param exportObj TableExport type object.
* @param requestScope requestScope object.
* @return EntityProjection object.
* @throws BadRequestException BadRequestException.
* @return Collection of EntityProjection object.
*/
public abstract EntityProjection getProjection(TableExport exportObj, RequestScope requestScope)
throws BadRequestException;
public abstract Collection<EntityProjection> getProjections(TableExport exportObj, RequestScope requestScope);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2021, Verizon Media.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package com.yahoo.elide.async.models;

import com.yahoo.elide.annotation.Include;

import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Include(rootLevel = true, type = "group")
@Entity
public class ArtifactGroup {
@Id
private String name = "";

@OneToMany(mappedBy = "group")
private List<ArtifactProduct> products = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2021, Verizon Media.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package com.yahoo.elide.async.models;

import com.yahoo.elide.annotation.Include;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

@Include(type = "product")
@Entity
public class ArtifactProduct {
@Id
private String name = "";

@ManyToOne
private ArtifactGroup group = null;
}