diff --git a/legend-engine-config/legend-engine-server/legend-engine-server-http-server/pom.xml b/legend-engine-config/legend-engine-server/legend-engine-server-http-server/pom.xml
index bafb2d278bc..0075e84873d 100644
--- a/legend-engine-config/legend-engine-server/legend-engine-server-http-server/pom.xml
+++ b/legend-engine-config/legend-engine-server/legend-engine-server-http-server/pom.xml
@@ -393,6 +393,31 @@
org.finos.legend.engine
legend-engine-xt-snowflakeApp-protocol
+
+
+ org.finos.legend.engine
+ legend-engine-xt-dataquality-compiler
+ ${project.version}
+ runtime
+
+
+ org.finos.legend.engine
+ legend-engine-xt-dataquality-grammar
+ ${project.version}
+ runtime
+
+
+ org.finos.legend.engine
+ legend-engine-xt-dataquality-api
+ ${project.version}
+
+
+ org.finos.legend.engine
+ legend-engine-xt-dataquality-protocol
+ ${project.version}
+ runtime
+
+
org.finos.legend.engine
legend-engine-xt-bigqueryFunction-compiler
diff --git a/legend-engine-config/legend-engine-server/legend-engine-server-http-server/src/main/java/org/finos/legend/engine/server/Server.java b/legend-engine-config/legend-engine-server/legend-engine-server-http-server/src/main/java/org/finos/legend/engine/server/Server.java
index c2cc75aca29..b20e345daaf 100644
--- a/legend-engine-config/legend-engine-server/legend-engine-server-http-server/src/main/java/org/finos/legend/engine/server/Server.java
+++ b/legend-engine-config/legend-engine-server/legend-engine-server-http-server/src/main/java/org/finos/legend/engine/server/Server.java
@@ -63,6 +63,7 @@
import org.finos.legend.engine.external.shared.format.model.api.ExternalFormats;
import org.finos.legend.engine.functionActivator.api.FunctionActivatorAPI;
import org.finos.legend.engine.generation.artifact.api.ArtifactGenerationExtensionApi;
+import org.finos.legend.engine.language.dataquality.api.DataQualityExecute;
import org.finos.legend.engine.language.hostedService.api.HostedServiceService;
import org.finos.legend.engine.language.memsql.api.MemSqlFunctionService;
import org.finos.legend.engine.language.pure.compiler.api.Compile;
@@ -416,6 +417,10 @@ public void run(T serverConfiguration, Environment environment)
environment.jersey().register(new LineageAnalytics(modelManager));
environment.jersey().register(new StoreEntitlementAnalytics(modelManager, entitlementServiceExtensions));
+ // DataQuality
+ environment.jersey().register(new DataQualityExecute(modelManager, planExecutor, routerExtensions, generatorExtensions.flatCollect(PlanGeneratorExtension::getExtraPlanTransformers), serverConfiguration.metadataserver, null));
+
+
// Testable
environment.jersey().register(new TestableApi(modelManager));
diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/PureIDELight.java b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/PureIDELight.java
index 2691c6cfb1c..3b32a056e9b 100644
--- a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/PureIDELight.java
+++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/PureIDELight.java
@@ -107,6 +107,7 @@ protected MutableList buildRepositories(SourceLocationCon
.with(this.buildCore("legend-engine-xts-arrow/legend-engine-xt-arrow-pure", "external-format-arrow"))
.with(this.buildCore("legend-engine-xts-relationalai/legend-engine-xt-relationalai-pure", "external-query-relationalai"))
.with(this.buildCore("legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-PCT/legend-engine-pure-functions-relationalStore-PCT-pure", "external_test_connection"))
+ .with(this.buildCore("legend-engine-xts-dataquality/legend-engine-xt-dataquality-pure", "dataquality"))
;
}
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/pom.xml b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/pom.xml
new file mode 100644
index 00000000000..cbf9c4e7617
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/pom.xml
@@ -0,0 +1,275 @@
+
+
+
+ legend-engine-xts-dataquality
+ org.finos.legend.engine
+ 4.48.2-SNAPSHOT
+
+ 4.0.0
+
+ legend-engine-xt-dataquality-api
+ jar
+ Legend Engine - XT - DataQuality - Api
+
+
+ 11
+ 11
+
+
+
+
+
+
+ org.finos.legend.pure
+ legend-pure-runtime-java-engine-compiled
+
+
+ org.finos.legend.engine
+ legend-engine-pure-code-compiled-core
+
+
+ org.finos.legend.pure
+ legend-pure-m3-core
+
+
+
+
+
+ org.finos.legend.engine
+ legend-engine-shared-core
+
+
+ org.finos.legend.engine
+ legend-engine-language-pure-modelManager
+
+
+ org.finos.legend.engine
+ legend-engine-protocol-pure
+
+
+ org.finos.legend.engine
+ legend-engine-language-pure-compiler
+
+
+ org.finos.legend.engine
+ legend-engine-language-pure-compiler
+ test-jar
+ test
+
+
+
+ org.finos.legend.engine
+ legend-engine-xt-relationalStore-javaPlatformBinding-pure
+ runtime
+
+
+ org.finos.legend.engine
+ legend-engine-pure-code-core-extension
+
+
+ org.finos.legend.engine
+ legend-engine-xt-relationalStore-pure
+ runtime
+
+
+ org.finos.legend.engine
+ legend-engine-xt-relationalStore-grammar
+ runtime
+
+
+ org.finos.legend.engine
+ legend-engine-executionPlan-execution
+
+
+ org.finos.legend.engine
+ legend-engine-executionPlan-execution-http-api
+ ${project.version}
+
+
+ org.finos.legend.engine
+ legend-engine-language-pure-modelManager-sdlc
+
+
+
+
+ org.eclipse.collections
+ eclipse-collections-api
+
+
+ org.eclipse.collections
+ eclipse-collections
+
+
+
+
+ org.finos.legend.engine
+ legend-engine-xt-data-space-compiler
+ runtime
+
+
+ org.finos.legend.engine
+ legend-engine-xt-relationalStore-trino-grammar
+ ${project.version}
+ runtime
+
+
+
+ javax.servlet
+ javax.servlet-api
+
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+
+ org.finos.legend.engine
+ legend-engine-xt-dataquality-protocol
+ ${project.version}
+
+
+ org.finos.legend.engine
+ legend-engine-xt-dataquality-pure
+ ${project.version}
+
+
+ org.finos.legend.engine
+ legend-engine-xt-dataquality-grammar
+ runtime
+ ${project.version}
+
+
+ org.finos.legend.engine
+ legend-engine-xt-dataquality-compiler
+ runtime
+ ${project.version}
+
+
+
+
+ org.finos.legend.pure
+ legend-pure-m2-dsl-graph-pure
+
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+
+
+
+
+
+
+
+
+ org.pac4j.jax-rs
+ core
+
+
+ org.pac4j
+ pac4j-core
+
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+
+ io.opentracing
+ opentracing-util
+
+
+ io.opentracing
+ opentracing-api
+
+
+
+
+
+ javax.ws.rs
+ javax.ws.rs-api
+
+
+ io.swagger
+ swagger-annotations
+
+
+
+
+
+ log4j
+ log4j
+ test
+
+
+
+
+
+ junit
+ junit
+ test
+
+
+ org.finos.legend.engine
+ legend-engine-shared-core
+ test-jar
+ test
+
+
+ org.glassfish.jersey.core
+ jersey-common
+ test
+
+
+ org.finos.legend.engine
+ legend-engine-executionPlan-generation
+
+
+ org.finos.legend.engine
+ legend-engine-xt-identity-pac4j
+
+
+ org.finos.legend.engine
+ legend-engine-identity-core
+ ${project.version}
+
+
+ org.finos.legend.engine
+ legend-engine-language-pure-dsl-generation
+
+
+ com.github.tomakehurst
+ wiremock-jre8
+ test
+
+
+ com.h2database
+ h2
+ test
+
+
+
+
\ No newline at end of file
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityExecute.java b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityExecute.java
new file mode 100644
index 00000000000..dc1de429242
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityExecute.java
@@ -0,0 +1,248 @@
+// Copyright 2020 Goldman Sachs
+//
+// Licensed 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.finos.legend.engine.language.dataquality.api;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.opentracing.Scope;
+import io.opentracing.util.GlobalTracer;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.eclipse.collections.api.RichIterable;
+import org.eclipse.collections.api.block.function.Function;
+import org.eclipse.collections.api.factory.Maps;
+import org.eclipse.collections.api.list.MutableList;
+import org.eclipse.collections.api.map.MutableMap;
+import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel;
+import org.finos.legend.engine.language.pure.modelManager.ModelManager;
+import org.finos.legend.engine.language.pure.modelManager.sdlc.configuration.MetaDataServerConfiguration;
+import org.finos.legend.engine.plan.execution.PlanExecutor;
+import org.finos.legend.engine.plan.execution.api.request.RequestContextHelper;
+import org.finos.legend.engine.plan.execution.api.result.ResultManager;
+import org.finos.legend.engine.plan.execution.nodes.helpers.ExecuteNodeParameterTransformationHelper;
+import org.finos.legend.engine.plan.execution.planHelper.PrimitiveValueSpecificationToObjectVisitor;
+import org.finos.legend.engine.plan.execution.result.Result;
+import org.finos.legend.engine.plan.execution.result.serialization.SerializationFormat;
+import org.finos.legend.engine.plan.generation.PlanGenerator;
+import org.finos.legend.engine.plan.generation.transformers.PlanTransformer;
+import org.finos.legend.engine.plan.platform.PlanPlatform;
+import org.finos.legend.engine.protocol.dataquality.model.DataQualityExecuteInput;
+import org.finos.legend.engine.protocol.pure.v1.model.executionPlan.SingleExecutionPlan;
+import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.ParameterValue;
+import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.classInstance.graph.RootGraphFetchTree;
+import org.finos.legend.engine.shared.core.ObjectMapperFactory;
+import org.finos.legend.engine.shared.core.api.result.ManageConstantResult;
+import org.finos.legend.engine.shared.core.identity.Identity;
+import org.finos.legend.engine.shared.core.kerberos.ProfileManagerHelper;
+import org.finos.legend.engine.shared.core.operational.errorManagement.ExceptionTool;
+import org.finos.legend.engine.shared.core.operational.http.InflateInterceptor;
+import org.finos.legend.engine.shared.core.operational.logs.LogInfo;
+import org.finos.legend.engine.shared.core.operational.logs.LoggingEventType;
+import org.finos.legend.engine.shared.core.operational.prometheus.MetricsHandler;
+import org.finos.legend.pure.generated.Root_meta_external_dataquality_DataQuality;
+import org.finos.legend.pure.generated.Root_meta_pure_extension_Extension;
+import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.PackageableElement;
+import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.function.LambdaFunction;
+import org.pac4j.core.profile.CommonProfile;
+import org.pac4j.core.profile.ProfileManager;
+import org.pac4j.jax.rs.annotations.Pac4JProfileManager;
+import org.slf4j.Logger;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.finos.legend.engine.shared.core.operational.http.InflateInterceptor.APPLICATION_ZLIB;
+
+@Api(tags = "DataQuality - Execution")
+@Path("pure/v1/dataquality")
+@Produces(MediaType.APPLICATION_JSON)
+public class DataQualityExecute
+{
+ private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(DataQualityExecute.class);
+ private static final ObjectMapper objectMapper = ObjectMapperFactory.getNewStandardObjectMapperWithPureProtocolExtensionSupports();
+ private final ModelManager modelManager;
+ private final Function> extensions;
+ private final Iterable extends PlanTransformer> transformers;
+ private final PlanExecutor planExecutor;
+ private final DataQualityPlanLoader dataQualityPlanLoader;
+
+
+ public DataQualityExecute(ModelManager modelManager, PlanExecutor planExecutor, Function> extensions, Iterable extends PlanTransformer> transformers, MetaDataServerConfiguration metaDataServerConfiguration, CloseableHttpClient httpClientProvider)
+ {
+ this.modelManager = modelManager;
+ this.extensions = extensions;
+ this.transformers = transformers;
+ this.planExecutor = planExecutor;
+ this.dataQualityPlanLoader = new DataQualityPlanLoader(metaDataServerConfiguration.sdlc, httpClientProvider);
+ MetricsHandler.createMetrics(this.getClass());
+
+ }
+
+ @POST
+ @Path("generatePlan")
+ @Consumes({MediaType.APPLICATION_JSON, APPLICATION_ZLIB})
+ @Produces(MediaType.APPLICATION_JSON)
+ //@Prometheus(name = "generate plan")
+ public Response generatePlan(DataQualityExecuteTrialInput dataQualityExecuteInput, @ApiParam(hidden = true) @Pac4JProfileManager() ProfileManager pm)
+ {
+ MutableList profiles = ProfileManagerHelper.extractProfiles(pm);
+ Identity identity = Identity.makeIdentity(profiles);
+ long start = System.currentTimeMillis();
+ LOGGER.info(new LogInfo(identity.getName(), DataQualityExecutionLoggingEventType.DATAQUALITY_GENERATE_PLAN_START).toString());
+ try (Scope scope = GlobalTracer.get().buildSpan("DataQuality: planGeneration").startActive(true))
+ {
+ // 1. load pure model from PureModelContext
+ PureModel pureModel = this.modelManager.loadModel(dataQualityExecuteInput.model, dataQualityExecuteInput.clientVersion, identity, null);
+ // 2. call DQ PURE func to generate lambda
+ LambdaFunction dqLambdaFunction = DataQualityLambdaGenerator.generateLambda(pureModel, dataQualityExecuteInput.packagePath);
+ // 3. Generate Plan from the lambda generated in the previous step
+ SingleExecutionPlan singleExecutionPlan = PlanGenerator.generateExecutionPlan(dqLambdaFunction, null, null, null, pureModel, dataQualityExecuteInput.clientVersion, PlanPlatform.JAVA, null, this.extensions.apply(pureModel), this.transformers);// since lambda has from function we dont need mapping, runtime and context
+ LOGGER.info(new LogInfo(identity.getName(), DataQualityExecutionLoggingEventType.DATAQUALITY_GENERATE_PLAN_END, System.currentTimeMillis() - start).toString());
+ return ManageConstantResult.manageResult(identity.getName(), singleExecutionPlan, objectMapper);
+ }
+ catch (Exception ex)
+ {
+ return ExceptionTool.exceptionManager(ex, LoggingEventType.GENERATE_PLAN_ERROR, identity.getName());
+ }
+ }
+
+ @POST
+ @Path("execute")
+ @Consumes({MediaType.APPLICATION_JSON, APPLICATION_ZLIB})
+ @Produces(MediaType.APPLICATION_JSON)
+ //@Prometheus(name = "generate plan")
+ public Response execute(@Context HttpServletRequest request, DataQualityExecuteTrialInput dataQualityExecuteInput, @ApiParam(hidden = true) @Pac4JProfileManager() ProfileManager pm, @Context UriInfo uriInfo)
+ {
+ MutableList profiles = ProfileManagerHelper.extractProfiles(pm);
+ Identity identity = Identity.makeIdentity(profiles);
+ long start = System.currentTimeMillis();
+ LOGGER.info(new LogInfo(identity.getName(), DataQualityExecutionLoggingEventType.DATAQUALITY_PLAN_EXECUTION_START).toString());
+ try (Scope scope = GlobalTracer.get().buildSpan("DataQuality: executeTrial").startActive(true))
+ {
+ // 1. load pure model from PureModelContext
+ PureModel pureModel = this.modelManager.loadModel(dataQualityExecuteInput.model, dataQualityExecuteInput.clientVersion, identity, null);
+ // 2. call DQ PURE func to generate lambda
+ LambdaFunction dqLambdaFunction = DataQualityLambdaGenerator.generateLambdaForTrial(pureModel, dataQualityExecuteInput.packagePath, dataQualityExecuteInput.queryLimit);
+ // 3. Generate Plan from the lambda generated in the previous step
+ SingleExecutionPlan singleExecutionPlan = PlanGenerator.generateExecutionPlan(dqLambdaFunction, null, null, null, pureModel, dataQualityExecuteInput.clientVersion, PlanPlatform.JAVA, null, this.extensions.apply(pureModel), this.transformers);// since lambda has from function we dont need mapping, runtime and context
+ // 4. execute plan
+ Map lambdaParameterMap = dataQualityExecuteInput.lambdaParameterValues != null ? dataQualityExecuteInput.lambdaParameterValues.stream().collect(Collectors.toMap(p -> p.name, p -> p.value.accept(new PrimitiveValueSpecificationToObjectVisitor()))) : Maps.mutable.empty();
+ Response response = executePlan(request, identity, start, singleExecutionPlan, lambdaParameterMap);
+ if (response.getStatusInfo().getFamily().equals(Response.Status.Family.SUCCESSFUL))
+ {
+ MetricsHandler.observeRequest(uriInfo != null ? uriInfo.getPath() : null, start, System.currentTimeMillis());
+ }
+ LOGGER.info(new LogInfo(identity.getName(), DataQualityExecutionLoggingEventType.DATAQUALITY_PLAN_EXECUTION_END, System.currentTimeMillis() - start).toString());
+ return response;
+ }
+ catch (Exception ex)
+ {
+ return ExceptionTool.exceptionManager(ex, LoggingEventType.EXECUTION_PLAN_EXEC_ERROR, identity.getName());
+ }
+ }
+
+
+ @POST
+ @Path("executeArtifacts")
+ @Consumes({MediaType.APPLICATION_JSON, APPLICATION_ZLIB})
+ @Produces(MediaType.APPLICATION_JSON)
+ //@Prometheus(name = "generate plan")
+ public Response execute(@Context HttpServletRequest request, DataQualityExecuteInput dataQualityParameterValue, @ApiParam(hidden = true) @Pac4JProfileManager() ProfileManager pm, @Context UriInfo uriInfo)
+ {
+ MutableList profiles = ProfileManagerHelper.extractProfiles(pm);
+ Identity identity = Identity.makeIdentity(profiles);
+ long start = System.currentTimeMillis();
+ LOGGER.info(new LogInfo(identity.getName(), DataQualityExecutionLoggingEventType.DATAQUALITY_ARTIFACT_PLAN_EXECUTION_START).toString());
+ try (Scope scope = GlobalTracer.get().buildSpan("DataQuality: execute").startActive(true))
+ {
+ // 1. fetch plan from artifacts
+ SingleExecutionPlan singleExecutionPlan = this.dataQualityPlanLoader.fetchPlanFromSDLC(identity, dataQualityParameterValue);
+
+ // 2. execute plan
+ Map lambdaParameterMap = dataQualityParameterValue.lambdaParameterValues != null ? dataQualityParameterValue.lambdaParameterValues.stream().collect(Collectors.toMap(p -> p.name, p -> p.value.accept(new PrimitiveValueSpecificationToObjectVisitor()))) : Maps.mutable.empty();
+ Response response = executePlan(request, identity, start, singleExecutionPlan, lambdaParameterMap);
+ if (response.getStatusInfo().getFamily().equals(Response.Status.Family.SUCCESSFUL))
+ {
+ MetricsHandler.observeRequest(uriInfo != null ? uriInfo.getPath() : null, start, System.currentTimeMillis());
+ }
+ LOGGER.info(new LogInfo(identity.getName(), DataQualityExecutionLoggingEventType.DATAQUALITY_ARTIFACT_PLAN_EXECUTION_END, System.currentTimeMillis() - start).toString());
+ return response;
+ }
+ catch (Exception ex)
+ {
+ return ExceptionTool.exceptionManager(ex, LoggingEventType.EXECUTION_PLAN_EXEC_ERROR, identity.getName());
+ }
+ }
+
+ private Response executePlan(HttpServletRequest request, Identity identity, long start, SingleExecutionPlan singleExecutionPlan, Map lambdaParameterMap)
+ {
+ MutableMap parametersToConstantResult = Maps.mutable.empty();
+ ExecuteNodeParameterTransformationHelper.buildParameterToConstantResult(singleExecutionPlan, lambdaParameterMap, parametersToConstantResult);
+ Result result = planExecutor.execute(singleExecutionPlan, parametersToConstantResult, request.getRemoteUser(), identity, null, RequestContextHelper.RequestContext(request));
+ return this.wrapInResponse(identity, SerializationFormat.DEFAULT, start, result);
+ }
+
+
+ private Response wrapInResponse(Identity identity, SerializationFormat format, long start, Result result)
+ {
+ LOGGER.info(new LogInfo(identity.getName(), DataQualityExecutionLoggingEventType.DATAQUALITY_EXECUTE_INTERACTIVE_STOP, (double) System.currentTimeMillis() - start).toString());
+ MetricsHandler.observe("execute", start, System.currentTimeMillis());
+ try (Scope scope = GlobalTracer.get().buildSpan("Manage Results").startActive(true))
+ {
+ return ResultManager.manageResult(identity.getName(), result, format, LoggingEventType.EXECUTE_INTERACTIVE_ERROR);
+ }
+ }
+
+ @POST
+ @Path("propertyPathTree")
+ @ApiOperation(value = "Analyze the DataQuality tree to generate property path tree for given constraints")
+ @Consumes({MediaType.APPLICATION_JSON, InflateInterceptor.APPLICATION_ZLIB})
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response generatePropertyPathTree(DataQualityExecuteTrialInput input,
+ @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm)
+ {
+ MutableList profiles = ProfileManagerHelper.extractProfiles(pm);
+ Identity identity = Identity.makeIdentity(profiles);
+ LOGGER.info(new LogInfo(identity.getName(), DataQualityExecutionLoggingEventType.DATAQUALITY_PROPERTY_PATH_TREE_START).toString());
+ try
+ {
+ PureModel pureModel = this.modelManager.loadModel(input.model, input.clientVersion, identity, null);
+ PackageableElement packageableElement = pureModel.getPackageableElement(input.packagePath);
+ if (!DataQualityPropertyPathTreeGenerator.isDataQualityInstance(packageableElement))
+ {
+ return ExceptionTool.exceptionManager("Invalid Element", LoggingEventType.MODEL_RESOLVE_ERROR, identity.getName());
+ }
+ RootGraphFetchTree rootGraphFetchTree = DataQualityPropertyPathTreeGenerator.getPropertyPathTree(((Root_meta_external_dataquality_DataQuality)packageableElement)._validationTree(), input.clientVersion, pureModel, this.extensions.apply(pureModel));
+ LOGGER.info(new LogInfo(identity.getName(), DataQualityExecutionLoggingEventType.DATAQUALITY_PROPERTY_PATH_TREE_END).toString());
+ return ManageConstantResult.manageResult(identity.getName(), rootGraphFetchTree);
+ }
+ catch (Exception ex)
+ {
+ return ExceptionTool.exceptionManager(ex, LoggingEventType.MODEL_RESOLVE_ERROR, identity.getName());
+ }
+
+ }
+
+}
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityExecuteTrialInput.java b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityExecuteTrialInput.java
new file mode 100644
index 00000000000..88d98aa31e2
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityExecuteTrialInput.java
@@ -0,0 +1,32 @@
+// Copyright 2020 Goldman Sachs
+//
+// Licensed 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.finos.legend.engine.language.dataquality.api;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContext;
+import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.ParameterValue;
+
+import java.util.List;
+
+public class DataQualityExecuteTrialInput
+{
+ public String clientVersion;
+ @JsonProperty(required = true)
+ public PureModelContext model;
+ @JsonProperty
+ public String packagePath;
+ public Integer queryLimit;
+ public List lambdaParameterValues;
+}
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityExecutionLoggingEventType.java b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityExecutionLoggingEventType.java
new file mode 100644
index 00000000000..4e53dd6e728
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityExecutionLoggingEventType.java
@@ -0,0 +1,33 @@
+// Copyright 2021 Goldman Sachs
+//
+// Licensed 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.finos.legend.engine.language.dataquality.api;
+
+import org.finos.legend.engine.shared.core.operational.logs.ILoggingEventType;
+
+public enum DataQualityExecutionLoggingEventType implements ILoggingEventType
+{
+ DATAQUALITY_PLAN_EXECUTION_START,
+ DATAQUALITY_PLAN_EXECUTION_END,
+ DATAQUALITY_PLAN_EXECUTION_TRIAL_ERROR,
+ DATAQUALITY_ARTIFACT_PLAN_EXECUTION_START,
+ DATAQUALITY_ARTIFACT_PLAN_EXECUTION_END,
+ DATAQUALITY_PLAN_EXECUTION_ERROR,
+ DATAQUALITY_GENERATE_PLAN_START,
+ DATAQUALITY_GENERATE_PLAN_END,
+ DATAQUALITY_EXECUTE_INTERACTIVE_STOP,
+ DATAQUALITY_EXECUTE_INTERACTIVE_ERROR,
+ DATAQUALITY_PROPERTY_PATH_TREE_START,
+ DATAQUALITY_PROPERTY_PATH_TREE_END
+}
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityLambdaGenerator.java b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityLambdaGenerator.java
new file mode 100644
index 00000000000..3af91656fcc
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityLambdaGenerator.java
@@ -0,0 +1,51 @@
+// Copyright 2021 Goldman Sachs
+//
+// Licensed 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.finos.legend.engine.language.dataquality.api;
+
+import org.eclipse.collections.impl.factory.Lists;
+import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel;
+import org.finos.legend.pure.generated.Root_meta_external_dataquality_DataQuality;
+import org.finos.legend.pure.generated.core_dataquality_generation_dataquality;
+import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.PackageableElement;
+import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.function.LambdaFunction;
+
+import java.util.Objects;
+
+public class DataQualityLambdaGenerator
+{
+ public static final int DEFAULT_QUERY_LIMIT = 100;
+
+ public static LambdaFunction generateLambda(PureModel pureModel, String qualifiedPath)
+ {
+ PackageableElement packageableElement = pureModel.getPackageableElement(qualifiedPath);
+ return generateLambda(pureModel, packageableElement);
+ }
+
+ public static LambdaFunction generateLambda(PureModel pureModel, PackageableElement packageableElement)
+ {
+ return core_dataquality_generation_dataquality.Root_meta_external_dataquality_executeDataQualityValidation_DataQuality_1__Integer_MANY__LambdaFunction_1_((Root_meta_external_dataquality_DataQuality)packageableElement, Lists.immutable.empty(), pureModel.getExecutionSupport());
+ }
+
+ public static LambdaFunction generateLambdaForTrial(PureModel pureModel, String qualifiedPath, Integer queryLimit)
+ {
+ PackageableElement packageableElement = pureModel.getPackageableElement(qualifiedPath);
+ int trialQueryLimit = DEFAULT_QUERY_LIMIT;
+ if (Objects.nonNull(queryLimit))
+ {
+ trialQueryLimit = queryLimit;
+ }
+ return core_dataquality_generation_dataquality.Root_meta_external_dataquality_executeDataQualityValidation_DataQuality_1__Integer_MANY__LambdaFunction_1_((Root_meta_external_dataquality_DataQuality)packageableElement, Lists.immutable.of((long)trialQueryLimit), pureModel.getExecutionSupport());
+ }
+}
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityPlanLoader.java b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityPlanLoader.java
new file mode 100644
index 00000000000..c8b5852198f
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityPlanLoader.java
@@ -0,0 +1,97 @@
+// Copyright 2021 Goldman Sachs
+//
+// Licensed 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.finos.legend.engine.language.dataquality.api;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.opentracing.Scope;
+import io.opentracing.util.GlobalTracer;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.finos.legend.engine.language.pure.dsl.generation.extension.Artifact;
+import org.finos.legend.engine.language.pure.modelManager.sdlc.SDLCLoader;
+import org.finos.legend.engine.language.pure.modelManager.sdlc.configuration.ServerConnectionConfiguration;
+import org.finos.legend.engine.plan.generation.PlanGenerator;
+import org.finos.legend.engine.protocol.dataquality.model.DataQualityExecuteInput;
+import org.finos.legend.engine.protocol.pure.v1.model.context.AlloySDLC;
+import org.finos.legend.engine.protocol.pure.v1.model.executionPlan.SingleExecutionPlan;
+import org.finos.legend.engine.shared.core.ObjectMapperFactory;
+import org.finos.legend.engine.shared.core.identity.Identity;
+import org.finos.legend.engine.shared.core.kerberos.HttpClientBuilder;
+import org.finos.legend.engine.shared.core.operational.Assert;
+import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException;
+
+import java.io.InputStream;
+import java.util.List;
+
+public class DataQualityPlanLoader
+{
+ private static final TypeReference> ARTIFACT_TYPE = new TypeReference>()
+ {
+ };
+
+ private static final ObjectMapper objectMapper = ObjectMapperFactory.getNewStandardObjectMapperWithPureProtocolExtensionSupports();
+ public static final String EXECUTION_PLAN_FILE_NAME = "dataQualityValidationExecutionPlan.json";
+
+ private final ServerConnectionConfiguration sdlcServerConnectionConfig;
+ private final CloseableHttpClient httpClientProvider;
+
+ public DataQualityPlanLoader(ServerConnectionConfiguration sdlcServerConnectionConfig, CloseableHttpClient httpClientProvider)
+ {
+ this.sdlcServerConnectionConfig = sdlcServerConnectionConfig;
+ this.httpClientProvider = httpClientProvider;
+ }
+
+ public SingleExecutionPlan fetchPlanFromSDLC(Identity identity, DataQualityExecuteInput dataQualityParameterValue)
+ {
+ List metaDataArtifactList = loadPlanDataQualityFromHTTPURL(getMetaDataApiUrl(dataQualityParameterValue.elementPath, dataQualityParameterValue.alloySDLC));
+ return getPlanFromArtifactResponse(metaDataArtifactList);
+ }
+
+ private String getMetaDataApiUrl(String elementPath, AlloySDLC alloySDLC)
+ {
+ Assert.assertTrue(alloySDLC != null && alloySDLC.groupId != null && alloySDLC.artifactId != null && alloySDLC.version != null, () -> "AlloySDLC info must contain and group and artifact IDs to access metadata services");
+ return String.format("%s/generations/%s/%s/versions/%s/%s", sdlcServerConnectionConfig.getBaseUrl(), alloySDLC.groupId, alloySDLC.artifactId, alloySDLC.version, elementPath);
+ }
+
+ private List loadPlanDataQualityFromHTTPURL(String url)
+ {
+
+ try (
+ CloseableHttpClient client = httpClientProvider == null ? (CloseableHttpClient) HttpClientBuilder.getHttpClient(new BasicCookieStore()) : httpClientProvider;
+ Scope scope = GlobalTracer.get().buildSpan("Load project upstream dependencies").startActive(true)
+ )
+ {
+ HttpGet httpRequest = new HttpGet(url);
+ HttpEntity entity = SDLCLoader.execHttpRequest(scope.span(), client, httpRequest);
+ try (InputStream content = entity.getContent())
+ {
+ return objectMapper.readValue(content, ARTIFACT_TYPE);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private SingleExecutionPlan getPlanFromArtifactResponse(List metaDataArtifactList)
+ {
+ Artifact planArtifact = metaDataArtifactList.stream().filter(artifact -> artifact.path.contains(EXECUTION_PLAN_FILE_NAME)).findAny().orElseThrow(() -> new EngineException("Error fetching DataQuality Execution plan"));
+ return PlanGenerator.stringToPlan(planArtifact.content);
+ }
+}
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityPropertyPathTreeGenerator.java b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityPropertyPathTreeGenerator.java
new file mode 100644
index 00000000000..00511892e44
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/api/DataQualityPropertyPathTreeGenerator.java
@@ -0,0 +1,84 @@
+// Copyright 2021 Goldman Sachs
+//
+// Licensed 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.finos.legend.engine.language.dataquality.api;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.eclipse.collections.api.RichIterable;
+import org.eclipse.collections.api.factory.Maps;
+import org.eclipse.collections.impl.factory.Lists;
+import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel;
+import org.finos.legend.engine.protocol.pure.PureClientVersions;
+import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.classInstance.graph.RootGraphFetchTree;
+import org.finos.legend.engine.shared.core.ObjectMapperFactory;
+import org.finos.legend.pure.generated.Root_meta_external_dataquality_DataQuality;
+import org.finos.legend.pure.generated.Root_meta_external_dataquality_DataQualityRootGraphFetchTree;
+import org.finos.legend.pure.generated.Root_meta_pure_extension_Extension;
+import org.finos.legend.pure.generated.core_dataquality_generation_dataquality;
+import org.finos.legend.pure.m3.coreinstance.meta.pure.graphFetch.GraphFetchTree;
+import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.PackageableElement;
+import org.finos.legend.pure.m3.execution.ExecutionSupport;
+import org.finos.legend.pure.runtime.java.compiled.generation.processors.support.map.PureMap;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static org.finos.legend.pure.generated.core_pure_protocol_protocol.Root_meta_alloy_metadataServer_alloyToJSON_Any_1__String_1_;
+
+public class DataQualityPropertyPathTreeGenerator
+{
+ public static RootGraphFetchTree getPropertyPathTree(Root_meta_external_dataquality_DataQualityRootGraphFetchTree dataQualityRootGraphFetchTree, String clientVersion, PureModel pureModel, RichIterable extends Root_meta_pure_extension_Extension> extensions) throws JsonProcessingException
+ {
+ org.finos.legend.pure.m3.coreinstance.meta.pure.graphFetch.RootGraphFetchTree rootGraphFetchTree = core_dataquality_generation_dataquality.Root_meta_external_dataquality_getEnrichedTreeForStructuralValidations_DataQualityRootGraphFetchTree_1__RootGraphFetchTree_1_(dataQualityRootGraphFetchTree, pureModel.getExecutionSupport());
+ return serializeToJSON(rootGraphFetchTree, clientVersion, pureModel, extensions);
+ }
+
+ private static RootGraphFetchTree serializeToJSON(org.finos.legend.pure.m3.coreinstance.meta.pure.graphFetch.RootGraphFetchTree purePlan, String clientVersion, PureModel pureModel, RichIterable extends Root_meta_pure_extension_Extension> extensions) throws JsonProcessingException
+ {
+ String cl = (clientVersion == null || !supports(clientVersion)) ? PureClientVersions.production : clientVersion;
+ Object transformed = transformToVersionedModel(purePlan, cl, extensions, pureModel.getExecutionSupport());
+ return serializeToJSON(transformed, pureModel);
+ }
+
+ private static Object transformToVersionedModel(org.finos.legend.pure.m3.coreinstance.meta.pure.graphFetch.RootGraphFetchTree pureGraph, String version, RichIterable extends Root_meta_pure_extension_Extension> extensions, ExecutionSupport executionSupport)
+ {
+ try
+ {
+ Class cl = Class.forName("org.finos.legend.pure.generated.core_pure_protocol_" + version + "_transfers_valueSpecification");
+ Method graphFetchProtocolMethod = cl.getMethod("Root_meta_protocols_pure_" + version + "_transformation_fromPureGraph_valueSpecification_transformGraphFetchTree_GraphFetchTree_1__String_MANY__Map_1__Extension_MANY__GraphFetchTree_1_", GraphFetchTree.class, RichIterable.class, PureMap.class, RichIterable.class, org.finos.legend.pure.m3.execution.ExecutionSupport.class);
+ return graphFetchProtocolMethod.invoke(null, pureGraph, Lists.mutable.empty(), new PureMap(Maps.mutable.empty()), extensions, executionSupport);
+ }
+ catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static RootGraphFetchTree serializeToJSON(Object protocolPlan, PureModel pureModel) throws JsonProcessingException
+ {
+ String asJSON = Root_meta_alloy_metadataServer_alloyToJSON_Any_1__String_1_(protocolPlan, pureModel.getExecutionSupport());
+ return ObjectMapperFactory.getNewStandardObjectMapperWithPureProtocolExtensionSupports().readValue(asJSON, RootGraphFetchTree.class);
+ }
+
+ public static boolean isDataQualityInstance(PackageableElement element)
+ {
+ return element instanceof Root_meta_external_dataquality_DataQuality
+ && ((Root_meta_external_dataquality_DataQuality) element)._validationTree() != null;
+ }
+
+ private static boolean supports(String version)
+ {
+ return "vX_X_X".equals(version) || PureClientVersions.versionAGreaterThanOrEqualsVersionB(version, "v1_20_0");
+ }
+}
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/deployment/DataQualityValidationArtifactGenerationExtension.java b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/deployment/DataQualityValidationArtifactGenerationExtension.java
new file mode 100644
index 00000000000..585ee6f1d61
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/java/org/finos/legend/engine/language/dataquality/deployment/DataQualityValidationArtifactGenerationExtension.java
@@ -0,0 +1,97 @@
+// Copyright 2021 Goldman Sachs
+//
+// Licensed 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.finos.legend.engine.language.dataquality.deployment;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.eclipse.collections.api.RichIterable;
+import org.eclipse.collections.api.block.function.Function;
+import org.eclipse.collections.api.factory.Lists;
+import org.finos.legend.engine.language.dataquality.api.DataQualityLambdaGenerator;
+import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel;
+import org.finos.legend.engine.language.pure.dsl.generation.extension.Artifact;
+import org.finos.legend.engine.language.pure.dsl.generation.extension.ArtifactGenerationExtension;
+import org.finos.legend.engine.plan.generation.PlanGenerator;
+import org.finos.legend.engine.plan.generation.transformers.LegendPlanTransformers;
+import org.finos.legend.engine.plan.platform.PlanPlatform;
+import org.finos.legend.engine.protocol.dataquality.model.DataQualityMetadata;
+import org.finos.legend.engine.protocol.dataquality.model.DataQualityRule;
+import org.finos.legend.engine.protocol.pure.v1.PureProtocolObjectMapperFactory;
+import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData;
+import org.finos.legend.engine.protocol.pure.v1.model.executionPlan.SingleExecutionPlan;
+import org.finos.legend.engine.pure.code.core.PureCoreExtensionLoader;
+import org.finos.legend.engine.shared.core.ObjectMapperFactory;
+import org.finos.legend.pure.generated.Root_meta_external_dataquality_DataQuality;
+import org.finos.legend.pure.generated.Root_meta_external_dataquality_DataQualityRule;
+import org.finos.legend.pure.generated.Root_meta_pure_extension_Extension;
+import org.finos.legend.pure.generated.core_dataquality_generation_dataquality;
+import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.PackageableElement;
+import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.function.LambdaFunction;
+import org.slf4j.Logger;
+
+import java.util.List;
+
+public class DataQualityValidationArtifactGenerationExtension implements ArtifactGenerationExtension
+{
+ private final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(DataQualityValidationArtifactGenerationExtension.class);
+ private final ObjectMapper mapper = ObjectMapperFactory.withStandardConfigurations(PureProtocolObjectMapperFactory.withPureProtocolExtensions(new ObjectMapper()));
+
+ public static final String ROOT_PATH = "dataQualityValidation";
+ public static final String META_DATA_FILE_NAME = "dataQualityRulesMetadata.json";
+ public static final String EXECUTION_PLAN_FILE_NAME = "dataQualityValidationExecutionPlan.json";
+
+
+ @Override
+ public String getKey()
+ {
+ return ROOT_PATH;
+ }
+
+ @Override
+ public boolean canGenerate(PackageableElement element)
+ {
+ return element instanceof Root_meta_external_dataquality_DataQuality
+ && ((Root_meta_external_dataquality_DataQuality) element)._validationTree() != null;
+ }
+
+ @Override
+ public List generate(PackageableElement packageableElement, PureModel pureModel, PureModelContextData pureModelContextData, String clientVersion)
+ {
+ List artifacts = Lists.mutable.empty();
+
+ try
+ {
+ RichIterable extends Root_meta_external_dataquality_DataQualityRule> dataQualityRules =
+ core_dataquality_generation_dataquality.Root_meta_external_dataquality_generateDQMetaDataForDQValidation_DataQuality_1__DataQualityRule_MANY_((Root_meta_external_dataquality_DataQuality)packageableElement, pureModel.getExecutionSupport());
+ List dataQualityRuleList = Lists.mutable.withAll(dataQualityRules).collect(d -> new DataQualityRule(d._constraintName(), d._constraintType(), d._constraintGrammar(), d._propertyPath()));
+ DataQualityMetadata dataQualityMetadata = new DataQualityMetadata();
+ dataQualityMetadata.dqRules = dataQualityRuleList;
+ artifacts.add(new Artifact(mapper.writeValueAsString(dataQualityMetadata), META_DATA_FILE_NAME, "json"));
+
+ Function> routerExtensions = (PureModel p) -> PureCoreExtensionLoader.extensions().flatCollect(e -> e.extraPureCoreExtensions(p.getExecutionSupport()));
+ // 1. call DQ PURE func to generate lambda
+ LambdaFunction dqLambdaFunction = DataQualityLambdaGenerator.generateLambda(pureModel, packageableElement);
+ // 2. Generate Plan from the lambda generated in the previous step
+ SingleExecutionPlan singleExecutionPlan = PlanGenerator.generateExecutionPlan(dqLambdaFunction, null, null, null, pureModel, clientVersion, PlanPlatform.JAVA, null,
+ routerExtensions.apply(pureModel), LegendPlanTransformers.transformers);// since lambda has from function we dont need mapping, runtime and context
+ artifacts.add(new Artifact(mapper.writeValueAsString(singleExecutionPlan), EXECUTION_PLAN_FILE_NAME, "json"));
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Unable to compute dataQuality validation artifact for dqValidation: " + packageableElement.getName() + ". Exception:" + e.getMessage());
+ }
+ return artifacts;
+ }
+
+}
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/resources/META-INF/services/org.finos.legend.engine.language.pure.dsl.generation.extension.ArtifactGenerationExtension b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/resources/META-INF/services/org.finos.legend.engine.language.pure.dsl.generation.extension.ArtifactGenerationExtension
new file mode 100644
index 00000000000..892b5e69787
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/main/resources/META-INF/services/org.finos.legend.engine.language.pure.dsl.generation.extension.ArtifactGenerationExtension
@@ -0,0 +1 @@
+org.finos.legend.engine.language.dataquality.deployment.DataQualityValidationArtifactGenerationExtension
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/java/org/finos/legend/engine/language/dataquality/api/TestDataQualityApi.java b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/java/org/finos/legend/engine/language/dataquality/api/TestDataQualityApi.java
new file mode 100644
index 00000000000..48c3106bb85
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/java/org/finos/legend/engine/language/dataquality/api/TestDataQualityApi.java
@@ -0,0 +1,69 @@
+// Copyright 2020 Goldman Sachs
+//
+// Licensed 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.finos.legend.engine.language.dataquality.api;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.finos.legend.engine.language.pure.compiler.Compiler;
+import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel;
+import org.finos.legend.engine.language.pure.compiler.toPureGraph.extension.CompilerExtensionLoader;
+import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData;
+import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.classInstance.graph.RootGraphFetchTree;
+import org.finos.legend.engine.shared.core.ObjectMapperFactory;
+import org.finos.legend.engine.shared.core.deployment.DeploymentMode;
+import org.finos.legend.engine.shared.core.identity.Identity;
+import org.finos.legend.pure.generated.Root_meta_external_dataquality_DataQuality;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Objects;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class TestDataQualityApi
+{
+ private static final ObjectMapper objectMapper = ObjectMapperFactory.getNewStandardObjectMapperWithPureProtocolExtensionSupports();
+
+ @Test
+ public void testDataQualityGetPropertyPathTree() throws IOException
+ {
+ URL url = Objects.requireNonNull(getClass().getClassLoader().getResource("inputs/modelWithDataQualityValidation.json"));
+ PureModelContextData pureModelContextData = objectMapper.readValue(url, PureModelContextData.class);
+ CompilerExtensionLoader.logExtensionList();
+ PureModel model = Compiler.compile(pureModelContextData, DeploymentMode.TEST, Identity.getAnonymousIdentity().getName());
+ org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.PackageableElement packageableElement = model.getPackageableElement("meta::dataquality::PersonDataQualityValidation");
+ assertTrue(DataQualityPropertyPathTreeGenerator.isDataQualityInstance(packageableElement));
+ RootGraphFetchTree rootGraphFetchTree = DataQualityPropertyPathTreeGenerator.getPropertyPathTree(((Root_meta_external_dataquality_DataQuality)packageableElement)._validationTree(), "vX_X_X", model, null);
+ assertEquals("{\"_type\":\"rootGraphFetchTree\",\"_type\":\"rootGraphFetchTree\",\"class\":\"meta::dataquality::Person\",\"subTrees\":[{\"_type\":\"propertyGraphFetchTree\",\"_type\":\"propertyGraphFetchTree\",\"parameters\":[],\"property\":\"name\",\"subTrees\":[],\"subTypeTrees\":[]},{\"_type\":\"propertyGraphFetchTree\",\"_type\":\"propertyGraphFetchTree\",\"parameters\":[],\"property\":\"age\",\"subTrees\":[],\"subTypeTrees\":[]}],\"subTypeTrees\":[]}", objectMapper.writeValueAsString(rootGraphFetchTree));
+
+ org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.PackageableElement packageableElement2 = model.getPackageableElement("meta::dataquality::Person");
+ assertFalse(DataQualityPropertyPathTreeGenerator.isDataQualityInstance(packageableElement2));
+ }
+
+ @Ignore
+ @Test
+ public void testDataQualityGetPropertyPathTree_WithNullDataQualityValidation() throws IOException
+ {
+ URL url = Objects.requireNonNull(getClass().getClassLoader().getResource("inputs/modelWithNullDataQualityValidation.json"));
+ PureModelContextData pureModelContextData = objectMapper.readValue(url, PureModelContextData.class);
+ CompilerExtensionLoader.logExtensionList();
+ PureModel model = Compiler.compile(pureModelContextData, DeploymentMode.TEST, Identity.getAnonymousIdentity().getName());
+ org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.PackageableElement packageableElement = model.getPackageableElement("meta::dataquality::PersonDataQualityValidation");
+ assertFalse(DataQualityPropertyPathTreeGenerator.isDataQualityInstance(packageableElement));
+ }
+}
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/java/org/finos/legend/engine/language/dataquality/api/TestDataQualityPlanLoader.java b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/java/org/finos/legend/engine/language/dataquality/api/TestDataQualityPlanLoader.java
new file mode 100644
index 00000000000..20e5ab6112e
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/java/org/finos/legend/engine/language/dataquality/api/TestDataQualityPlanLoader.java
@@ -0,0 +1,105 @@
+// Copyright 2021 Goldman Sachs
+//
+// Licensed 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.finos.legend.engine.language.dataquality.api;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
+import org.finos.legend.engine.language.pure.dsl.generation.extension.Artifact;
+import org.finos.legend.engine.language.pure.modelManager.sdlc.configuration.ServerConnectionConfiguration;
+import org.finos.legend.engine.protocol.dataquality.model.DataQualityExecuteInput;
+import org.finos.legend.engine.protocol.pure.v1.model.context.AlloySDLC;
+import org.finos.legend.engine.protocol.pure.v1.model.executionPlan.SingleExecutionPlan;
+import org.finos.legend.engine.shared.core.ObjectMapperFactory;
+import org.finos.legend.engine.shared.core.identity.Identity;
+import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+import java.util.Objects;
+
+import static org.junit.Assert.assertNotNull;
+
+public class TestDataQualityPlanLoader
+{
+ private static final ObjectMapper objectMapper = ObjectMapperFactory.getNewStandardObjectMapperWithPureProtocolExtensionSupports();
+
+ private static final TypeReference> ARTIFACT_TYPE = new TypeReference>()
+ {
+ };
+
+ @ClassRule
+ public static WireMockClassRule wireMockServer = new WireMockClassRule();
+
+ @Rule
+ public WireMockClassRule rule = wireMockServer;
+
+ @Test
+ public void testFetchPlanFromSDLC() throws IOException
+ {
+ configureWireMockForNoRetries();
+ DataQualityPlanLoader dataQualityPlanLoader = new DataQualityPlanLoader(createServerConnectionConfiguration(), null);
+ SingleExecutionPlan singleExecutionPlan = dataQualityPlanLoader.fetchPlanFromSDLC(Identity.getAnonymousIdentity(), createDataQualityParameterValue());
+ assertNotNull(singleExecutionPlan);
+ }
+
+ @Test
+ public void testFetchPlanFromSDLC_EmptyProjectCoordinates_Exception() throws IOException
+ {
+ configureWireMockForNoRetries();
+ DataQualityPlanLoader dataQualityPlanLoader = new DataQualityPlanLoader(createServerConnectionConfiguration(), null);
+ DataQualityExecuteInput dataQualityParameterValue = new DataQualityExecuteInput();
+ dataQualityParameterValue.alloySDLC = null;
+ dataQualityParameterValue.elementPath = "meta::dataquality::PersonDataQualityValidation";
+ Assert.assertEquals("AlloySDLC info must contain and group and artifact IDs to access metadata services", Assert.assertThrows(EngineException.class, () -> dataQualityPlanLoader.fetchPlanFromSDLC(Identity.getAnonymousIdentity(), dataQualityParameterValue)).getMessage());
+ }
+
+ private DataQualityExecuteInput createDataQualityParameterValue()
+ {
+ AlloySDLC alloySDLC = new AlloySDLC();
+ alloySDLC.groupId = "com.dq.test";
+ alloySDLC.artifactId = "test-sandbox";
+ alloySDLC.version = "master-SNAPSHOT";
+ DataQualityExecuteInput dataQualityParameterValue = new DataQualityExecuteInput();
+ dataQualityParameterValue.alloySDLC = alloySDLC;
+ dataQualityParameterValue.elementPath = "meta::dataquality::PersonDataQualityValidation";
+ return dataQualityParameterValue;
+ }
+
+ private ServerConnectionConfiguration createServerConnectionConfiguration()
+ {
+ ServerConnectionConfiguration serverConfiguration = new ServerConnectionConfiguration();
+
+ serverConfiguration.host = "localhost";
+ serverConfiguration.port = rule.port();
+ serverConfiguration.prefix = "/api";
+
+ return serverConfiguration;
+ }
+
+ private static void configureWireMockForNoRetries() throws IOException
+ {
+ URL url = Objects.requireNonNull(TestDataQualityPlanLoader.class.getClassLoader().getResource("inputs/dataQualityArtifacts.json"));
+ List response = objectMapper.readValue(url, ARTIFACT_TYPE);
+ WireMock.stubFor(WireMock.get("/api/generations/com.dq.test/test-sandbox/versions/master-SNAPSHOT/meta::dataquality::PersonDataQualityValidation")
+ .willReturn(WireMock.aResponse().withStatus(200).withBody(objectMapper.writeValueAsString(response))));
+ }
+}
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/java/org/finos/legend/engine/language/dataquality/deployment/TestDataQualityValidationArtifactGenerationExtension.java b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/java/org/finos/legend/engine/language/dataquality/deployment/TestDataQualityValidationArtifactGenerationExtension.java
new file mode 100644
index 00000000000..6020161953c
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/java/org/finos/legend/engine/language/dataquality/deployment/TestDataQualityValidationArtifactGenerationExtension.java
@@ -0,0 +1,61 @@
+// Copyright 2020 Goldman Sachs
+//
+// Licensed 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.finos.legend.engine.language.dataquality.deployment;
+
+import org.finos.legend.engine.language.pure.compiler.Compiler;
+import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel;
+import org.finos.legend.engine.language.pure.compiler.toPureGraph.extension.CompilerExtensionLoader;
+import org.finos.legend.engine.language.pure.dsl.generation.extension.Artifact;
+import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData;
+import org.finos.legend.engine.shared.core.ObjectMapperFactory;
+import org.finos.legend.engine.shared.core.deployment.DeploymentMode;
+import org.finos.legend.engine.shared.core.identity.Identity;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class TestDataQualityValidationArtifactGenerationExtension
+{
+ @Test
+ public void testDataQualityValidationArtifact() throws IOException
+ {
+ URL url = Objects.requireNonNull(getClass().getClassLoader().getResource("inputs/modelWithDataQualityValidation.json"));
+ PureModelContextData pureModelContextData = ObjectMapperFactory.getNewStandardObjectMapperWithPureProtocolExtensionSupports().readValue(url, PureModelContextData.class);
+ CompilerExtensionLoader.logExtensionList();
+
+ PureModel model = Compiler.compile(pureModelContextData, DeploymentMode.TEST_IGNORE_FUNCTION_MATCH, Identity.getAnonymousIdentity().getName());
+ DataQualityValidationArtifactGenerationExtension extension = new DataQualityValidationArtifactGenerationExtension();
+ org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.PackageableElement packageableElement = model.getPackageableElement("meta::dataquality::PersonDataQualityValidation");
+ assertTrue(extension.canGenerate(packageableElement));
+ List outputs = extension.generate(packageableElement, model, pureModelContextData, "vX_X_X");
+ assertEquals(2, outputs.size());
+ Optional artifactOptional = outputs.stream().filter(artifact -> "dataQualityRulesMetadata.json".equalsIgnoreCase(artifact.path)).findAny();
+ assertTrue(artifactOptional.isPresent());
+ Artifact dataQualityMetaData = artifactOptional.get();
+ assertEquals("{\"dqRules\":[{\"constraintGrammar\":\"($this.age >= 18)\",\"constraintName\":\"mustBeOfLegalAge\",\"constraintType\":\"Alloy_Constraint_Validation\",\"propertyPath\":\"Person\"},{\"constraintGrammar\":\"Class\",\"constraintName\":\"Person\",\"constraintType\":\"Alloy_Class_Validation\",\"propertyPath\":\"Person\"},{\"constraintGrammar\":\"1\",\"constraintName\":\"name\",\"constraintType\":\"Alloy_Structural_Validation\",\"propertyPath\":\"Person::name\"},{\"constraintGrammar\":\"1\",\"constraintName\":\"age\",\"constraintType\":\"Alloy_Structural_Validation\",\"propertyPath\":\"Person::age\"}]}", dataQualityMetaData.content);
+
+ org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.PackageableElement packageableElement2 = model.getPackageableElement("meta::dataquality::Person");
+ assertFalse(extension.canGenerate(packageableElement2));
+
+ }
+}
diff --git a/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/resources/inputs/dataQualityArtifacts.json b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/resources/inputs/dataQualityArtifacts.json
new file mode 100644
index 00000000000..9d457909686
--- /dev/null
+++ b/legend-engine-xts-dataquality/legend-engine-xt-dataquality-api/src/test/resources/inputs/dataQualityArtifacts.json
@@ -0,0 +1,12 @@
+[
+ {
+ "content": "{\"dqRules\":[{\"constraintGrammar\":\"($this.age >= 18)\",\"constraintName\":\"mustBeOfLegalAge\",\"constraintType\":\"Alloy_Constraint_Validation\",\"propertyPath\":\"Person\"},{\"constraintGrammar\":\"Class\",\"constraintName\":\"Person\",\"constraintType\":\"Alloy_Class_Validation\",\"propertyPath\":\"Person\"},{\"constraintGrammar\":\"1\",\"constraintName\":\"name\",\"constraintType\":\"Alloy_Structural_Validation\",\"propertyPath\":\"Person::name\"},{\"constraintGrammar\":\"1\",\"constraintName\":\"age\",\"constraintType\":\"Alloy_Structural_Validation\",\"propertyPath\":\"Person::age\"}]}",
+ "format": "json",
+ "path": "dataQualityRulesMetadata.json"
+ },
+ {
+ "content": "{\"_type\":\"simple\",\"authDependent\":false,\"globalImplementationSupport\":{\"_type\":\"java\",\"classes\":[{\"name\":\"Person\",\"package\":\"_pure.app.meta.dataquality\",\"source\":\"package _pure.app.meta.dataquality;\\n\\nimport java.math.*;\\nimport java.util.*;\\nimport org.finos.legend.engine.plan.dependencies.domain.date.PureDate;\\n\\npublic interface Person extends org.finos.legend.engine.plan.dependencies.store.shared.IReferencedObject\\n{\\n default String typeName$()\\n {\\n return \\\"Person\\\";\\n }\\n\\n default String typePath$()\\n {\\n return \\\"meta::dataquality::Person\\\";\\n }\\n\\n String getName();\\n long getAge();\\n String getAlloyStoreObjectReference$();\\n}\"},{\"name\":\"Serialize\",\"package\":\"_pure.plan.root\",\"source\":\"package _pure.plan.root;\\n\\nimport org.finos.legend.engine.plan.dependencies.store.platform.IGraphSerializer;\\nimport org.finos.legend.engine.plan.dependencies.store.platform.IPlatformPureExpressionExecutionNodeSerializeSpecifics;\\nimport org.finos.legend.engine.plan.dependencies.store.platform.ISerializationWriter;\\nimport org.finos.legend.engine.plan.dependencies.store.shared.IExecutionNodeContext;\\n\\npublic class Serialize implements IPlatformPureExpressionExecutionNodeSerializeSpecifics\\n{\\n public IGraphSerializer> serializer(ISerializationWriter writer,\\n IExecutionNodeContext context)\\n {\\n return new Serializer(writer, context);\\n }\\n}\"},{\"name\":\"Serializer\",\"package\":\"_pure.plan.root\",\"source\":\"package _pure.plan.root;\\n\\nimport _pure.app.meta.dataquality.Person;\\nimport java.util.List;\\nimport org.finos.legend.engine.plan.dependencies.domain.dataQuality.EnforcementLevel;\\nimport org.finos.legend.engine.plan.dependencies.domain.dataQuality.IChecked;\\nimport org.finos.legend.engine.plan.dependencies.domain.dataQuality.IDefect;\\nimport org.finos.legend.engine.plan.dependencies.domain.dataQuality.RelativePathNode;\\nimport org.finos.legend.engine.plan.dependencies.domain.dataQuality.RuleType;\\nimport org.finos.legend.engine.plan.dependencies.store.platform.IGraphSerializer;\\nimport org.finos.legend.engine.plan.dependencies.store.platform.ISerializationWriter;\\nimport org.finos.legend.engine.plan.dependencies.store.shared.IExecutionNodeContext;\\nimport org.finos.legend.engine.plan.dependencies.store.shared.IReferencedObject;\\n\\npublic class Serializer implements IGraphSerializer\\n{\\n private ISerializationWriter writer;\\n private IExecutionNodeContext context;\\n\\n Serializer(ISerializationWriter writer, IExecutionNodeContext context)\\n {\\n this.writer = writer;\\n this.context = context;\\n }\\n\\n public void serialize(IChecked value)\\n {\\n this.writer.startObject(\\\"meta::pure::dataQuality::Checked\\\");\\n this.writer\\n .writeComplexProperty(\\\"defects\\\",\\n value.getDefects(),\\n this::writeIDefect_defects);\\n this.writer\\n .writeComplexProperty(\\\"value\\\",\\n (Person) value.getValue(),\\n this::writePerson_value);\\n this.writer.endObject();\\n }\\n\\n public void writeIDefect_defects(IDefect value)\\n {\\n this.writer.startObject(\\\"meta::pure::dataQuality::Defect\\\");\\n this.writer.writeStringProperty(\\\"id\\\", value.getId());\\n this.writer.writeStringProperty(\\\"externalId\\\", value.getExternalId());\\n this.writer.writeStringProperty(\\\"message\\\", value.getMessage());\\n this.writer\\n .writeEnumProperty(\\\"enforcementLevel\\\",\\n \\\"meta::pure::dataQuality::EnforcementLevel\\\",\\n value.getEnforcementLevel() == null\\n ? null\\n : value.getEnforcementLevel().getName());\\n this.writer\\n .writeEnumProperty(\\\"ruleType\\\",\\n \\\"meta::pure::dataQuality::RuleType\\\",\\n value.getRuleType() == null\\n ? null\\n : value.getRuleType().getName());\\n this.writer\\n .writeStringProperty(\\\"ruleDefinerPath\\\",\\n value.getRuleDefinerPath());\\n this.writer\\n .writeComplexProperty(\\\"path\\\",\\n value.getPath(),\\n this::writeRelativePathNode_defects_path);\\n this.writer.endObject();\\n }\\n\\n public void writeRelativePathNode_defects_path(RelativePathNode value)\\n {\\n this.writer.startObject(\\\"meta::pure::dataQuality::RelativePathNode\\\");\\n this.writer\\n .writeStringProperty(\\\"propertyName\\\",\\n value.getPropertyName());\\n this.writer.writeIntegerProperty(\\\"index\\\", value.getIndex());\\n this.writer.endObject();\\n }\\n\\n public void writePerson_value(Person value)\\n {\\n if (value instanceof IReferencedObject)\\n {\\n this.writer\\n .startObject(value.typePath$(),\\n ((IReferencedObject) value).getAlloyStoreObjectReference$());\\n }\\n else\\n {\\n this.writer.startObject(value.typePath$());\\n }\\n this.writer.writeStringProperty(\\\"name\\\", value.getName());\\n this.writer.writeIntegerProperty(\\\"age\\\", value.getAge());\\n this.writer.endObject();\\n }\\n}\"},{\"name\":\"Execute\",\"package\":\"_pure.plan.root.n1.localGraph\",\"source\":\"package _pure.plan.root.n1.localGraph;\\n\\nimport java.lang.reflect.Method;\\nimport java.sql.ResultSet;\\nimport java.util.*;\\nimport java.util.function.*;\\nimport java.util.stream.*;\\nimport org.eclipse.collections.api.tuple.Pair;\\nimport org.finos.legend.engine.plan.dependencies.domain.graphFetch.IGraphInstance;\\nimport org.finos.legend.engine.plan.dependencies.store.relational.graphFetch.IRelationalRootQueryTempTableGraphFetchExecutionNodeSpecifics;\\nimport org.finos.legend.engine.plan.dependencies.store.shared.IExecutionNodeContext;\\nimport org.finos.legend.engine.plan.dependencies.store.shared.IReferencedObject;\\n\\npublic class Execute implements IRelationalRootQueryTempTableGraphFetchExecutionNodeSpecifics\\n{\\n private Specifics specifics;\\n\\n public Execute()\\n {\\n this.specifics = new Specifics();\\n }\\n\\n public void prepare(ResultSet resultSet, String databaseTimeZone, String databaseConnection)\\n {\\n this.specifics.prepare(resultSet, databaseTimeZone, databaseConnection);\\n }\\n\\n public IGraphInstance extends IReferencedObject> nextGraphInstance()\\n {\\n return this.specifics.nextGraphInstance();\\n }\\n\\n public List primaryKeyGetters()\\n {\\n return this.specifics.primaryKeyGetters();\\n }\\n\\n public List> allInstanceSetImplementations()\\n {\\n return this.specifics.allInstanceSetImplementations();\\n }\\n\\n public List primaryKeyColumns(int setIndex)\\n {\\n return this.specifics.primaryKeyColumns(setIndex);\\n }\\n\\n public boolean supportsCaching()\\n {\\n return true;\\n }\\n}\"},{\"name\":\"GraphFetch_Node0_Person_Impl\",\"package\":\"_pure.plan.root.n1.localGraph\",\"source\":\"package _pure.plan.root.n1.localGraph;\\n\\nimport java.math.*;\\nimport java.util.*;\\nimport java.util.function.*;\\nimport java.util.stream.*;\\nimport org.finos.legend.engine.plan.dependencies.domain.date.DayOfWeek;\\nimport org.finos.legend.engine.plan.dependencies.domain.date.DurationUnit;\\nimport org.finos.legend.engine.plan.dependencies.domain.date.PureDate;\\nimport org.finos.legend.engine.plan.dependencies.util.Library;\\nimport com.fasterxml.jackson.annotation.JsonInclude;\\nimport com.fasterxml.jackson.core.JsonGenerator;\\nimport com.fasterxml.jackson.databind.JsonSerializer;\\nimport com.fasterxml.jackson.databind.ObjectMapper;\\nimport com.fasterxml.jackson.databind.SerializerProvider;\\nimport com.fasterxml.jackson.databind.module.SimpleModule;\\nimport java.io.IOException;\\n\\npublic class GraphFetch_Node0_Person_Impl implements _pure.app.meta.dataquality.Person, org.finos.legend.engine.plan.dependencies.domain.dataQuality.Constrained<_pure.app.meta.dataquality.Person>, java.io.Serializable\\n{\\n private String name;\\n private long age;\\n private Object pk$_0;\\n private static final ObjectMapper objectMapper$ = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL).registerModule(new SimpleModule().addSerializer(PureDate.class, new JsonSerializer() { @Override public void serialize(PureDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeRawValue(\\\"\\\\\\\"\\\" + value.toString() + \\\"\\\\\\\"\\\"); } }));\\n private String setId$;\\n public static String databaseConnection$;\\n private String alloyStoreObjectReference$;\\n private static final long serialVersionUID = 1461153389L;\\n\\n public String getName()\\n {\\n return this.name;\\n }\\n\\n public void setName(String name)\\n {\\n this.name = name;\\n }\\n\\n public void addName(String object)\\n {\\n if ((Object) this.name != null)\\n {\\n throw new IllegalStateException(\\\"Found multiple objects for property 'name' of multiplicity with bound 1\\\");\\n }\\n this.name = object;\\n }\\n\\n public long getAge()\\n {\\n return this.age;\\n }\\n\\n public void setAge(long age)\\n {\\n this.age = age;\\n }\\n\\n public void addAge(long object)\\n {\\n if ((Object) new Long(this.age) != null)\\n {\\n throw new IllegalStateException(\\\"Found multiple objects for property 'age' of multiplicity with bound 1\\\");\\n }\\n this.age = object;\\n }\\n\\n public List allConstraints()\\n {\\n return this.allConstraints(new org.finos.legend.engine.plan.dependencies.domain.dataQuality.GraphContext());\\n }\\n\\n public _pure.app.meta.dataquality.Person withConstraintsApplied()\\n {\\n java.util.List defects = allConstraints();\\n if (!defects.isEmpty())\\n {\\n throw new IllegalStateException(defects.stream().map(org.finos.legend.engine.plan.dependencies.domain.dataQuality.IDefect::getMessage).collect(java.util.stream.Collectors.joining(\\\"\\\\n\\\")));\\n }\\n return this;\\n }\\n\\n public org.finos.legend.engine.plan.dependencies.domain.dataQuality.IChecked<_pure.app.meta.dataquality.Person> toChecked()\\n {\\n return this.toChecked(null, true);\\n }\\n\\n public org.finos.legend.engine.plan.dependencies.domain.dataQuality.IChecked<_pure.app.meta.dataquality.Person> toChecked(boolean applyConstraints)\\n {\\n return this.toChecked(null, applyConstraints);\\n }\\n\\n public org.finos.legend.engine.plan.dependencies.domain.dataQuality.IChecked<_pure.app.meta.dataquality.Person> toChecked(Object source)\\n {\\n return this.toChecked(source, true);\\n }\\n\\n public org.finos.legend.engine.plan.dependencies.domain.dataQuality.IChecked<_pure.app.meta.dataquality.Person> toChecked(Object source,\\n boolean applyConstraints)\\n {\\n java.util.List defects = applyConstraints ? allConstraints() : java.util.Collections.emptyList();\\n return new org.finos.legend.engine.plan.dependencies.domain.dataQuality.IChecked<_pure.app.meta.dataquality.Person>() {\\n public java.util.List getDefects() { return defects; }\\n public Object getSource() { return source; }\\n public _pure.app.meta.dataquality.Person getValue() { return GraphFetch_Node0_Person_Impl.this; }\\n };\\n }\\n\\n public List allConstraints(org.finos.legend.engine.plan.dependencies.domain.dataQuality.GraphContext context)\\n {\\n List result = new ArrayList();\\n if (!context.visited.contains(this))\\n {\\n context.visited.add(this);\\n this.constraint_mustBeOfLegalAge().ifPresent(result::add);\\n }\\n return result;\\n }\\n\\n public Optional constraint_mustBeOfLegalAge()\\n {\\n try\\n {\\n if (!(this.getAge() >= 18L))\\n {\\n String message = \\\"Constraint :[mustBeOfLegalAge] violated in the Class Person\\\";\\n return Optional.of(org.finos.legend.engine.plan.dependencies.domain.dataQuality.BasicDefect.newConstraintDefect(\\\"mustBeOfLegalAge\\\",\\n null,\\n message,\\n org.finos.legend.engine.plan.dependencies.domain.dataQuality.EnforcementLevel.Error,\\n \\\"meta::dataquality::Person\\\"));\\n }\\n return Optional.empty();\\n }\\n catch (Exception e)\\n {\\n String message = \\\"Unable to evaluate constraint [mustBeOfLegalAge]: \\\" + (e.getMessage() == null\\n ? \\\"data not available - check your mappings\\\"\\n : e.getMessage());\\n return Optional.of(org.finos.legend.engine.plan.dependencies.domain.dataQuality.BasicDefect.newConstraintDefect(\\\"mustBeOfLegalAge\\\",\\n null,\\n message,\\n org.finos.legend.engine.plan.dependencies.domain.dataQuality.EnforcementLevel.Error,\\n \\\"meta::dataquality::Person\\\"));\\n }\\n }\\n\\n public Object getPk$_0()\\n {\\n return this.pk$_0;\\n }\\n\\n public void setPk$_0(Object pk$_0)\\n {\\n this.pk$_0 = pk$_0;\\n }\\n\\n public String getSetId$()\\n {\\n return this.setId$;\\n }\\n\\n public void setSetId$(String setId)\\n {\\n this.setId$ = setId;\\n }\\n\\n public String getAlloyStoreObjectReference$()\\n {\\n if (this.alloyStoreObjectReference$ == null)\\n {\\n try\\n {\\n StringBuilder referenceBuilder = new StringBuilder();\\n referenceBuilder.append(\\\"001:\\\");\\n referenceBuilder.append(\\\"010:\\\");\\n\\n referenceBuilder.append(\\\"0000000010:\\\");\\n referenceBuilder.append(\\\"Relational:\\\");\\n\\n referenceBuilder.append(\\\"0000000038:\\\");\\n referenceBuilder.append(\\\"meta::dataquality::dataqualitymappings:\\\");\\n\\n referenceBuilder.append(\\\"0000000023:\\\");\\n referenceBuilder.append(\\\"meta_dataquality_Person:\\\");\\n\\n String setId = this.getSetId$();\\n referenceBuilder.append(String.format(\\\"%010d\\\", setId.length()));\\n referenceBuilder.append(\\\":\\\");\\n referenceBuilder.append(setId);\\n referenceBuilder.append(\\\":\\\");\\n\\n String databaseConnectionString = _pure.plan.root.n1.localGraph.GraphFetch_Node0_Person_Impl.databaseConnection$;\\n referenceBuilder.append(String.format(\\\"%010d\\\", databaseConnectionString.length()));\\n referenceBuilder.append(\\\":\\\");\\n referenceBuilder.append(databaseConnectionString);\\n referenceBuilder.append(\\\":\\\");\\n\\n Map pkMap = new HashMap<>();\\n\\n pkMap.put(\\\"pk$_0\\\", this.getPk$_0());\\n String pkMapString = objectMapper$.writeValueAsString(pkMap);\\n referenceBuilder.append(String.format(\\\"%010d\\\", pkMapString.length()));\\n referenceBuilder.append(\\\":\\\");\\n referenceBuilder.append(pkMapString);\\n\\n this.alloyStoreObjectReference$ = \\\"ASOR:\\\" + org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(referenceBuilder.toString().getBytes());\\n }\\n catch (Exception e)\\n {\\n throw new RuntimeException(e);\\n }\\n }\\n\\n return this.alloyStoreObjectReference$;\\n }\\n\\n public void setAlloyStoreObjectReference$(String reference)\\n {\\n this.alloyStoreObjectReference$ = reference;\\n }\\n\\n private static long getClassSize$()\\n {\\n return 116L;\\n }\\n\\n public long getInstanceSize$()\\n {\\n long size = GraphFetch_Node0_Person_Impl.getClassSize$();\\n if (this.name != null)\\n {\\n size = size + this.name.length();\\n }\\n if (this.setId$ != null)\\n {\\n size = size + this.setId$.length();\\n }\\n if (this.alloyStoreObjectReference$ != null)\\n {\\n size = size + this.alloyStoreObjectReference$.length();\\n }\\n return size;\\n }\\n}\"},{\"name\":\"Specifics\",\"package\":\"_pure.plan.root.n1.localGraph\",\"source\":\"package _pure.plan.root.n1.localGraph;\\n\\nimport java.lang.reflect.Method;\\nimport java.sql.JDBCType;\\nimport java.sql.ResultSet;\\nimport java.sql.ResultSetMetaData;\\nimport java.sql.Types;\\nimport java.util.*;\\nimport java.util.function.*;\\nimport java.util.stream.*;\\nimport org.eclipse.collections.api.tuple.Pair;\\nimport org.eclipse.collections.impl.tuple.Tuples;\\nimport org.finos.legend.engine.plan.dependencies.domain.date.PureDate;\\nimport org.finos.legend.engine.plan.dependencies.domain.graphFetch.IGraphInstance;\\nimport org.finos.legend.engine.plan.dependencies.store.shared.IConstantResult;\\nimport org.finos.legend.engine.plan.dependencies.store.shared.IExecutionNodeContext;\\n\\nclass Specifics\\n{\\n private static final List STRING_TYPES = Arrays.asList(Types.CHAR, Types.VARCHAR, Types.LONGVARCHAR, Types.NCHAR, Types.NVARCHAR, Types.LONGNVARCHAR, Types.OTHER, Types.NULL);\\n private static final List INT_TYPES = Arrays.asList(Types.TINYINT, Types.SMALLINT, Types.INTEGER, Types.BIGINT, Types.NULL);\\n private static final List FLOAT_TYPES = Arrays.asList(Types.REAL, Types.FLOAT, Types.DOUBLE, Types.DECIMAL, Types.NUMERIC, Types.NULL);\\n private static final List DECIMAL_TYPES = Arrays.asList(Types.DECIMAL, Types.NUMERIC, Types.NULL);\\n private static final List BOOL_TYPES = Arrays.asList(Types.BIT, Types.BOOLEAN, Types.NULL);\\n private static final List STRICT_DATE_TYPES = Arrays.asList(Types.DATE, Types.NULL);\\n private static final List DATE_TIME_TYPES = Arrays.asList(Types.TIMESTAMP, Types.NULL);\\n private ResultSet resultSet;\\n private String databaseTimeZone;\\n private String databaseConnection;\\n private List columnTypes;\\n private List> propertyIndices;\\n private List>> propertyGetters;\\n private Calendar calendar;\\n private Method parentPropertyAdder;\\n private Method parentEdgePointPropertyAdder;\\n\\n private Object getAlloyNativeValueFromResultSet(ResultSet resultSet,\\n int columnIndex,\\n int columnType)\\n {\\n try\\n {\\n Object result = null;\\n switch (columnType)\\n {\\n case Types.DATE:\\n {\\n java.sql.Date date = resultSet.getDate(columnIndex);\\n if (date != null)\\n {\\n result = PureDate.fromSQLDate(date);\\n }\\n break;\\n }\\n case Types.TIMESTAMP:\\n {\\n java.sql.Timestamp timestamp = resultSet.getTimestamp(columnIndex, this.calendar);\\n if (timestamp != null)\\n {\\n result = PureDate.fromSQLTimestamp(timestamp);\\n }\\n break;\\n }\\n case Types.TINYINT:\\n case Types.SMALLINT:\\n case Types.INTEGER:\\n case Types.BIGINT:\\n {\\n long num = resultSet.getLong(columnIndex);\\n if (!resultSet.wasNull())\\n {\\n result = Long.valueOf(num);\\n }\\n break;\\n }\\n case Types.REAL:\\n case Types.FLOAT:\\n case Types.DOUBLE:\\n {\\n double num = resultSet.getDouble(columnIndex);\\n if (!resultSet.wasNull())\\n {\\n result = Double.valueOf(num);\\n }\\n break;\\n }\\n case Types.DECIMAL:\\n case Types.NUMERIC:\\n {\\n result = resultSet.getBigDecimal(columnIndex);\\n break;\\n }\\n case Types.CHAR:\\n case Types.VARCHAR:\\n case Types.LONGVARCHAR:\\n case Types.NCHAR:\\n case Types.NVARCHAR:\\n case Types.LONGNVARCHAR:\\n case Types.OTHER:\\n {\\n result = resultSet.getString(columnIndex);\\n break;\\n }\\n case Types.BIT:\\n case Types.BOOLEAN:\\n {\\n boolean bool = resultSet.getBoolean(columnIndex);\\n if (!resultSet.wasNull())\\n {\\n result = Boolean.valueOf(bool);\\n }\\n break;\\n }\\n case Types.BINARY:\\n case Types.VARBINARY:\\n case Types.LONGVARBINARY:\\n {\\n byte[] bytes = resultSet.getBytes(columnIndex);\\n if (bytes != null)\\n {\\n result = this.encodeHex(bytes);\\n }\\n break;\\n }\\n case Types.NULL:\\n {\\n // do nothing: value is already assigned to null\\n break;\\n }\\n default:\\n {\\n result = resultSet.getObject(columnIndex);\\n }\\n }\\n return result;}\\n catch (Exception e)\\n {\\n throw new RuntimeException(e);\\n }\\n }\\n\\n private String encodeHex(byte[] data)\\n {\\n final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};\\n final int l = data.length;\\n final char[] out = new char[l << 1];\\n for (int i = 0, j = 0; i < l; i++)\\n {\\n out[j++] = DIGITS_LOWER[(0xF0 & data[i]) >>> 4];\\n out[j++] = DIGITS_LOWER[0x0F & data[i]];\\n }\\n return new String(out);\\n }\\n\\n private Supplier