diff --git a/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/HelperRuntimeBuilder.java b/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/HelperRuntimeBuilder.java index af78076a3bb..03babccd700 100644 --- a/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/HelperRuntimeBuilder.java +++ b/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/HelperRuntimeBuilder.java @@ -290,7 +290,12 @@ public static List getMappingCompatib public static boolean isRuntimeCompatibleWithMapping(Root_meta_pure_runtime_PackageableRuntime runtime, Mapping mappingToCheck) { - return ListIterate.collect(runtime._runtimeValue()._mappings().toList(), mapping -> + return isRuntimeCompatibleWithMapping(runtime._runtimeValue(), mappingToCheck); + } + + public static boolean isRuntimeCompatibleWithMapping(Root_meta_core_runtime_EngineRuntime runtime, Mapping mappingToCheck) + { + return ListIterate.collect(runtime._mappings().toList(), mapping -> { Set mappings = new HashSet<>(); mappings.add(mapping); diff --git a/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/main/java/org/finos/legend/engine/language/pure/dsl/service/compiler/toPureGraph/HelperServiceBuilder.java b/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/main/java/org/finos/legend/engine/language/pure/dsl/service/compiler/toPureGraph/HelperServiceBuilder.java index eaf12ded7ce..41bdcec93f0 100644 --- a/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/main/java/org/finos/legend/engine/language/pure/dsl/service/compiler/toPureGraph/HelperServiceBuilder.java +++ b/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/main/java/org/finos/legend/engine/language/pure/dsl/service/compiler/toPureGraph/HelperServiceBuilder.java @@ -17,7 +17,9 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.collections.api.RichIterable; import org.eclipse.collections.impl.factory.Lists; +import org.eclipse.collections.impl.list.mutable.FastList; import org.eclipse.collections.impl.utility.ListIterate; +import org.eclipse.collections.impl.utility.internal.IterableIterate; import org.finos.legend.engine.language.pure.compiler.toPureGraph.*; import org.finos.legend.engine.language.pure.compiler.toPureGraph.data.EmbeddedDataFirstPassBuilder; import org.finos.legend.engine.protocol.pure.v1.model.SourceInformation; @@ -26,6 +28,8 @@ import org.finos.legend.engine.protocol.pure.v1.model.context.PackageableElementType; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.ParameterValue; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.runtime.EngineRuntime; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.runtime.Runtime; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.runtime.RuntimePointer; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.*; import org.finos.legend.engine.shared.core.operational.Assert; import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException; @@ -37,6 +41,7 @@ import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.valuespecification.VariableExpression; import java.util.*; +import java.util.function.Supplier; import java.util.stream.Collectors; public class HelperServiceBuilder @@ -79,7 +84,7 @@ private static void inferEmbeddedRuntimeMapping(org.finos.legend.engine.protocol } } - public static Root_meta_legend_service_metamodel_Execution processServiceExecution(Execution execution, CompileContext context) + public static Root_meta_legend_service_metamodel_Execution processServiceExecution(Execution execution, Service service, CompileContext context) { if (execution instanceof PureSingleExecution) { @@ -92,7 +97,7 @@ public static Root_meta_legend_service_metamodel_Execution processServiceExecuti mapping = context.resolveMapping(pureSingleExecution.mapping, pureSingleExecution.mappingSourceInformation); inferEmbeddedRuntimeMapping(pureSingleExecution.runtime, pureSingleExecution.mapping); runtime = HelperRuntimeBuilder.buildPureRuntime(pureSingleExecution.runtime, context); - HelperRuntimeBuilder.checkRuntimeMappingCoverage(runtime, Lists.fixedSize.of(mapping), context, pureSingleExecution.runtime.sourceInformation); + checkMappingRuntimeCompatibility(() -> "Service '" + service.getPath() + "'", runtime, pureSingleExecution.runtime, mapping, pureSingleExecution.mapping, pureSingleExecution.runtime.sourceInformation, context.pureModel); lambda = HelperValueSpecificationBuilder.buildLambda(pureSingleExecution.func, context); } else @@ -119,7 +124,7 @@ else if (execution instanceof PureMultiExecution) return new Root_meta_legend_service_metamodel_PureMultiExecution_Impl("", null, context.pureModel.getClass("meta::legend::service::metamodel::PureMultiExecution")) ._executionKey(pureMultiExecution.executionKey) ._func(lambda) - ._executionParameters(ListIterate.collect(pureMultiExecution.executionParameters, executionParameter -> processServiceKeyedExecutionParameter(executionParameter, context, executionKeyValues))); + ._executionParameters(ListIterate.collect(pureMultiExecution.executionParameters, executionParameter -> processServiceKeyedExecutionParameter(executionParameter, service, context, executionKeyValues))); } else { @@ -132,12 +137,13 @@ else if (execution instanceof PureMultiExecution) .orElseThrow(() -> new UnsupportedOperationException("Unsupported service execution type '" + execution.getClass().getSimpleName() + "'")); } - private static Root_meta_legend_service_metamodel_KeyedExecutionParameter processServiceKeyedExecutionParameter(KeyedExecutionParameter keyedExecutionParameter, CompileContext context, Set executionKeyValues) + private static Root_meta_legend_service_metamodel_KeyedExecutionParameter processServiceKeyedExecutionParameter(KeyedExecutionParameter keyedExecutionParameter, Service service, CompileContext context, Set executionKeyValues) { Mapping mapping = context.resolveMapping(keyedExecutionParameter.mapping, keyedExecutionParameter.mappingSourceInformation); inferEmbeddedRuntimeMapping(keyedExecutionParameter.runtime, keyedExecutionParameter.mapping); Root_meta_core_runtime_Runtime runtime = HelperRuntimeBuilder.buildPureRuntime(keyedExecutionParameter.runtime, context); - HelperRuntimeBuilder.checkRuntimeMappingCoverage(runtime, Lists.fixedSize.of(mapping), context, keyedExecutionParameter.runtime.sourceInformation); + + checkMappingRuntimeCompatibility(() -> "Service '" + service.getPath() + "', key: '" + keyedExecutionParameter.key + "',", runtime, keyedExecutionParameter.runtime, mapping, keyedExecutionParameter.mapping, keyedExecutionParameter.runtime.sourceInformation, context.pureModel); if (!executionKeyValues.add(keyedExecutionParameter.key)) { throw new EngineException("Execution parameter with key '" + keyedExecutionParameter.key + "' already existed", keyedExecutionParameter.sourceInformation, EngineErrorType.COMPILATION); @@ -191,10 +197,10 @@ public static void validateServiceTestParameterValues(CompileContext context, Li InstanceValue paramValue = (InstanceValue) parameterValue.get()._value().getOnly(); org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.multiplicity.Multiplicity paramMultiplicity = param._multiplicity(); org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.multiplicity.Multiplicity paramValueMultiplicity = paramValue._multiplicity(); - if (!"Nil".equals(paramValue._genericType()._rawType())) - { - HelperModelBuilder.checkCompatibility(context, paramValue._genericType()._rawType(), paramValueMultiplicity, param._genericType()._rawType(), paramMultiplicity, "Parameter value type does not match with parameter type for parameter: '" + param._name() + "'", sourceInformation); - } + if (!"Nil".equals(paramValue._genericType()._rawType())) + { + HelperModelBuilder.checkCompatibility(context, paramValue._genericType()._rawType(), paramValueMultiplicity, param._genericType()._rawType(), paramMultiplicity, "Parameter value type does not match with parameter type for parameter: '" + param._name() + "'", sourceInformation); + } } else { @@ -296,14 +302,14 @@ public static Root_meta_legend_service_metamodel_ExecutionParameters processExec { inferEmbeddedRuntimeMapping(execParams.runtime, execParams.mapping); Root_meta_core_runtime_Runtime runtime = HelperRuntimeBuilder.buildPureRuntime(execParams.runtime, context); - HelperRuntimeBuilder.checkRuntimeMappingCoverage(runtime, Lists.fixedSize.of(mapping), context, execParams.runtime.sourceInformation); + param._runtime(runtime); } else { Assert.assertTrue(execParams.runtimeComponents != null, () -> "Runtime components must be specified when runtime isn't"); RuntimeComponents c = execParams.runtimeComponents; - PackageableElement binding = null; + PackageableElement binding; try { binding = platform_pure_essential_meta_graph_pathToElement.Root_meta_pure_functions_meta_pathToElement_String_1__PackageableElement_1_(c.binding.path, context.getExecutionSupport()); @@ -332,4 +338,62 @@ else if (params instanceof MultiExecutionParameters) } throw new UnsupportedOperationException("Unsupported service execution type '" + params.getClass().getSimpleName() + "'"); } + + private static void checkMappingRuntimeCompatibility(Supplier context, Root_meta_core_runtime_Runtime pureRuntime, Runtime runtime, Mapping pureMapping, String mapping, SourceInformation sourceInformation, PureModel pureModel) + { + boolean compatible = !(pureRuntime instanceof Root_meta_core_runtime_EngineRuntime) || HelperRuntimeBuilder.isRuntimeCompatibleWithMapping((Root_meta_core_runtime_EngineRuntime) pureRuntime, pureMapping); + + if (!compatible) + { + String runtimeName = runtime instanceof RuntimePointer ? " '" + ((RuntimePointer) runtime).runtime + "'" : ""; + pureModel.addWarnings(Lists.mutable.with(new Warning(sourceInformation, context.get() + " Runtime" + runtimeName + " does not cover mapping '" + mapping + "'"))); + } + } + + public static void validate(ExecutionEnvironmentInstance execEnv, Root_meta_legend_service_metamodel_ExecutionEnvironmentInstance pureExecEnv, CompileContext compileContext) + { + checkRuntimeMappingCompatibility(execEnv, pureExecEnv, compileContext); + } + + public static void checkRuntimeMappingCompatibility(ExecutionEnvironmentInstance execEnv, Root_meta_legend_service_metamodel_ExecutionEnvironmentInstance pureExecEnv, CompileContext compileContext) + { + Map params = IterableIterate.flatCollect(execEnv.executionParameters, p -> + { + if (p instanceof SingleExecutionParameters) + { + return FastList.newListWith((SingleExecutionParameters) p); + } + else if (p instanceof MultiExecutionParameters) + { + return FastList.newList(((MultiExecutionParameters) p).singleExecutionParameters); + } + return FastList.newList(); + }).groupByUniqueKey(s -> s.key); + + IterableIterate.forEach(pureExecEnv._executionParameters(), e -> + { + if (e instanceof Root_meta_legend_service_metamodel_MultiExecutionParameters) + { + IterableIterate.forEach(((Root_meta_legend_service_metamodel_MultiExecutionParameters) e)._singleExecutionParameters(), + s -> checkMappingRuntimeCompatibility(execEnv, s._key(), s, params.get(s._key()), compileContext) + ); + } + else if (e instanceof Root_meta_legend_service_metamodel_SingleExecutionParameters) + { + Root_meta_legend_service_metamodel_SingleExecutionParameters param = (Root_meta_legend_service_metamodel_SingleExecutionParameters) e; + checkMappingRuntimeCompatibility(execEnv, param._key(), param, params.get(param._key()), compileContext); + } + else + { + throw new UnsupportedOperationException("Unsupported service execution type '" + execEnv.getClass().getSimpleName() + "'"); + } + }); + } + + private static void checkMappingRuntimeCompatibility(ExecutionEnvironmentInstance execEnv, String key, Root_meta_legend_service_metamodel_SingleExecutionParameters pureParam, SingleExecutionParameters param, CompileContext compileContext) + { + Root_meta_core_runtime_Runtime pureRuntime = pureParam._runtime() == null ? pureParam._runtimeComponents()._runtime() : pureParam._runtime(); + Runtime runtime = param.runtime == null ? param.runtimeComponents.runtime : param.runtime; + checkMappingRuntimeCompatibility(() -> "Execution Environment '" + execEnv.getPath() + "', key: '" + key + "',", pureRuntime, runtime, pureParam._mapping(), param.mapping, runtime.sourceInformation, compileContext.pureModel); + } } diff --git a/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/main/java/org/finos/legend/engine/language/pure/dsl/service/compiler/toPureGraph/ServiceCompilerExtensionImpl.java b/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/main/java/org/finos/legend/engine/language/pure/dsl/service/compiler/toPureGraph/ServiceCompilerExtensionImpl.java index be0b62f6186..34eaac2f92b 100644 --- a/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/main/java/org/finos/legend/engine/language/pure/dsl/service/compiler/toPureGraph/ServiceCompilerExtensionImpl.java +++ b/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/main/java/org/finos/legend/engine/language/pure/dsl/service/compiler/toPureGraph/ServiceCompilerExtensionImpl.java @@ -92,7 +92,7 @@ public Iterable> getExtraProcessors() { Root_meta_legend_service_metamodel_Service pureService = (Root_meta_legend_service_metamodel_Service) context.pureModel.getPackageableElement(service); - pureService._execution(HelperServiceBuilder.processServiceExecution(service.execution, context)); + pureService._execution(HelperServiceBuilder.processServiceExecution(service.execution, service, context)); // Legacy flow if (service.test != null) @@ -209,6 +209,11 @@ public Iterable> getExtraProcessors() { Root_meta_legend_service_metamodel_ExecutionEnvironmentInstance pureExecEnv = (Root_meta_legend_service_metamodel_ExecutionEnvironmentInstance) context.pureModel.getPackageableElement(execEnv); pureExecEnv._executionParameters(ListIterate.collect(execEnv.executionParameters, params -> HelperServiceBuilder.processExecutionParameters(params, context))); + }, + (execEnv, context) -> + { + Root_meta_legend_service_metamodel_ExecutionEnvironmentInstance pureExecEnv = (Root_meta_legend_service_metamodel_ExecutionEnvironmentInstance) context.pureModel.getPackageableElement(execEnv); + HelperServiceBuilder.validate(execEnv, pureExecEnv, context); }) ); } diff --git a/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/test/java/org/finos/legend/engine/language/pure/compiler/test/TestServiceCompilationFromGrammar.java b/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/test/java/org/finos/legend/engine/language/pure/compiler/test/TestServiceCompilationFromGrammar.java index dab31110198..9544670d8b1 100644 --- a/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/test/java/org/finos/legend/engine/language/pure/compiler/test/TestServiceCompilationFromGrammar.java +++ b/legend-engine-xts-service/legend-engine-language-pure-dsl-service/src/test/java/org/finos/legend/engine/language/pure/compiler/test/TestServiceCompilationFromGrammar.java @@ -14,6 +14,7 @@ package org.finos.legend.engine.language.pure.compiler.test; +import org.eclipse.collections.impl.list.mutable.FastList; import org.junit.Test; public class TestServiceCompilationFromGrammar extends TestCompilationFromGrammar.TestCompilationFromGrammarTestSuite @@ -2526,66 +2527,66 @@ public void testServiceTestSuiteCompilationErrorMessages() //Should no longer throw errors. Correctly aggregates under listed connection test(resource + "###Service\n" + - "Service test::service::FirmService\n" + - "{\n" + - " pattern: '/testFirmService';\n" + - " owners:\n" + - " [\n" + - " 'owner1',\n" + - " 'owner2'\n" + - " ];\n" + - " documentation: '';\n" + - " autoActivateUpdates: true;\n" + - " execution: Single\n" + - " {\n" + - " query: |test::model::Firm.all()->graphFetch(#{test::model::Firm{employees{firstName,lastName},legalName}}#)->serialize(#{test::model::Firm{employees{firstName,lastName},legalName}}#);\n" + - " mapping: test::mapping::FirmMapping;\n" + - " runtime: test::runtime::SFirmRuntime;\n" + - " }\n" + - " testSuites:\n" + - " [\n" + - " testSuite1:\n" + - " {\n" + - " data:\n" + - " [\n" + - " connections:\n" + - " [\n" + - " connection1:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[{\"employees\":[{\"firstName\":\"firstName 36\",\"lastName\":\"lastName 77\"}],\"legalName\":\"legalName 19\"}, {\"employees\":[{\"firstName\":\"firstName 37\",\"lastName\":\"lastName 78\"}],\"legalName\":\"legalName 20\"}]';\n" + - " }#,\n" + - " connection1:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '[{\"employees\":[{\"firstName\":\"firstName 36\",\"lastName\":\"lastName 77\"}],\"legalName\":\"legalName 19\"}, {\"employees\":[{\"firstName\":\"firstName 37\",\"lastName\":\"lastName 78\"}],\"legalName\":\"legalName 20\"}]';\n" + - " }#\n" + - " ]\n" + - " ]\n" + - " tests:\n" + - " [\n" + - " test1:\n" + - " {\n" + - " asserts:\n" + - " [\n" + - " assert1:\n" + - " EqualToJson\n" + - " #{\n" + - " expected:\n" + - " ExternalFormat\n" + - " #{\n" + - " contentType: 'application/json';\n" + - " data: '{\"employees\":[{\"firstName\":\"firstName 36\",\"lastName\":\"lastName 77\"}],\"legalName\":\"legalName 19\"}';\n" + - " }#;\n" + - " }#\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - " ]\n" + - "}\n" + "Service test::service::FirmService\n" + + "{\n" + + " pattern: '/testFirmService';\n" + + " owners:\n" + + " [\n" + + " 'owner1',\n" + + " 'owner2'\n" + + " ];\n" + + " documentation: '';\n" + + " autoActivateUpdates: true;\n" + + " execution: Single\n" + + " {\n" + + " query: |test::model::Firm.all()->graphFetch(#{test::model::Firm{employees{firstName,lastName},legalName}}#)->serialize(#{test::model::Firm{employees{firstName,lastName},legalName}}#);\n" + + " mapping: test::mapping::FirmMapping;\n" + + " runtime: test::runtime::SFirmRuntime;\n" + + " }\n" + + " testSuites:\n" + + " [\n" + + " testSuite1:\n" + + " {\n" + + " data:\n" + + " [\n" + + " connections:\n" + + " [\n" + + " connection1:\n" + + " ExternalFormat\n" + + " #{\n" + + " contentType: 'application/json';\n" + + " data: '[{\"employees\":[{\"firstName\":\"firstName 36\",\"lastName\":\"lastName 77\"}],\"legalName\":\"legalName 19\"}, {\"employees\":[{\"firstName\":\"firstName 37\",\"lastName\":\"lastName 78\"}],\"legalName\":\"legalName 20\"}]';\n" + + " }#,\n" + + " connection1:\n" + + " ExternalFormat\n" + + " #{\n" + + " contentType: 'application/json';\n" + + " data: '[{\"employees\":[{\"firstName\":\"firstName 36\",\"lastName\":\"lastName 77\"}],\"legalName\":\"legalName 19\"}, {\"employees\":[{\"firstName\":\"firstName 37\",\"lastName\":\"lastName 78\"}],\"legalName\":\"legalName 20\"}]';\n" + + " }#\n" + + " ]\n" + + " ]\n" + + " tests:\n" + + " [\n" + + " test1:\n" + + " {\n" + + " asserts:\n" + + " [\n" + + " assert1:\n" + + " EqualToJson\n" + + " #{\n" + + " expected:\n" + + " ExternalFormat\n" + + " #{\n" + + " contentType: 'application/json';\n" + + " data: '{\"employees\":[{\"firstName\":\"firstName 36\",\"lastName\":\"lastName 77\"}],\"legalName\":\"legalName 19\"}';\n" + + " }#;\n" + + " }#\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}\n" ); // Mis-match between parameter type & parameter value @@ -3438,7 +3439,7 @@ public void testServiceWithPostValidation() public void testExecutionEnvironmentCompilation() { test("###Service\n" + - "ExecutionEnvironment test::executionEnvironment\n" + + "ExecutionEnvironment test::executionEnvironment\n" + "{\n" + " executions:\n" + " [\n" + @@ -3453,7 +3454,7 @@ public void testExecutionEnvironmentCompilation() " runtime: test::myRuntime2;\n" + " }\n" + " ];\n" + - "}\n","COMPILATION error at [8:16-31]: Can't find mapping 'test::myMapping1'"); + "}\n", "COMPILATION error at [8:16-31]: Can't find mapping 'test::myMapping1'"); String resource = "Class test::class\n" + "{\n" + @@ -3491,7 +3492,7 @@ public void testExecutionEnvironmentCompilation() " runtime: test::runtime;\n" + " }\n" + " ];\n" + - "}\n","COMPILATION error at [27:16-31]: Can't find runtime 'test::myRuntime1'"); + "}\n", "COMPILATION error at [27:16-31]: Can't find runtime 'test::myRuntime1'"); test(resource + "###Service\n" + "ExecutionEnvironment test::executionEnvironment\n" + @@ -3588,4 +3589,113 @@ public void testExecutionEnvironmentCompilation() "}\n", "COMPILATION error at [32:5-42:5]: Cannot use both runtime and runtime components"); } -} \ No newline at end of file + + @Test + public void testMappingRuntimeCompatibility() + { + String resource = "Class test::class\n" + + "{\n" + + " prop1 : Integer[0..1];\n" + + "}\n" + + "###Mapping\n" + + "Mapping test::mapping\n" + + "(\n" + + ")\n" + + "Mapping test::mapping2\n" + + "(\n" + + ")\n" + + "###Connection\n" + + "JsonModelConnection test::connection\n" + + "{\n" + + " class : test::class;" + + " url : 'asd';\n" + + "}\n" + + "###Runtime\n" + + "Runtime test::runtime\n" + + "{\n" + + " mappings: [test::mapping];\n" + + "}\n"; + + test(resource + "###Service\n" + + "ExecutionEnvironment test::executionEnvironment\n" + + "{\n" + + " executions:\n" + + " [\n" + + " PROD:\n" + + " {\n" + + " mapping: test::mapping2;\n" + + " runtime: test::runtime;\n" + + " }\n" + + " ];\n" + + "}\n", null, FastList.newListWith("COMPILATION error at [30:16-28]: Execution Environment 'test::executionEnvironment', key: 'PROD', Runtime 'test::runtime' does not cover mapping 'test::mapping2'")); + + test(resource + "###Service \n" + + "Service test::Service \n" + + "{ \n" + + " pattern: 'url/myUrl/'; \n" + + " owners: ['ownerName']; \n" + + " documentation: 'test'; \n" + + " autoActivateUpdates: true; \n" + + " execution: Single \n" + + " { \n" + + " query: test::class.all()->project([col(p|$p.prop1, 'prop1')]); \n" + + " mapping: test::mapping2; \n" + + " runtime: test::runtime; \n" + + " }\n" + + "}", null, FastList.newListWith("COMPILATION error at [33:14-26]: Service 'test::Service' Runtime 'test::runtime' does not cover mapping 'test::mapping2'")); + + test(resource + "###Service\n" + + "Service test::Service\n" + + "{\n" + + " pattern: 'url/myUrl/';\n" + + " owners: ['ownerName'];\n" + + " documentation: 'test';\n" + + " autoActivateUpdates: true;\n" + + " execution: Multi\n" + + " {\n" + + " query: src: test::class[1]|$src.prop1;\n" + + " key: 'env';\n" + + " executions['PROD']:\n" + + " {\n" + + " mapping: test::mapping2;\n" + + " runtime: test::runtime;\n" + + " }\n" + + " executions['QA']:\n" + + " {\n" + + " mapping: test::mapping2;\n" + + " runtime: test::runtime;\n" + + " }\n" + + " }\n" + + "}\n", null, FastList.newListWith( + "COMPILATION error at [36:16-28]: Service 'test::Service', key: 'PROD', Runtime 'test::runtime' does not cover mapping 'test::mapping2'", + "COMPILATION error at [41:16-28]: Service 'test::Service', key: 'QA', Runtime 'test::runtime' does not cover mapping 'test::mapping2'")); + + String binding = "###ExternalFormat\n" + + "Binding test::bind\n" + + "{\n" + + " contentType: 'application/json';\n" + + " modelIncludes: [\n" + + " test::class\n" + + " ];\n" + + "}\n"; + test(resource + binding + "###Service\n" + + "ExecutionEnvironment test::executionEnvironment\n" + + "{\n" + + " executions:\n" + + " [\n" + + " UAT:\n" + + " {\n" + + " mapping: test::mapping2;\n" + + " runtimeComponents:\n" + + " {\n" + + " runtime: test::runtime;\n" + + " class: test::class;\n" + + " binding: test::bind;\n" + + " }\n" + + " }\n" + + " ];\n" + + "}\n", null, FastList.newListWith( + "COMPILATION error at [40:12-34]: Execution Environment 'test::executionEnvironment', key: 'UAT', Runtime 'test::runtime' does not cover mapping 'test::mapping2'" + )); + } +}