From fb4228d805c12cafa2aa2ad228e8c280c31dc956 Mon Sep 17 00:00:00 2001 From: afine-gs Date: Tue, 15 Oct 2024 12:44:11 -0400 Subject: [PATCH] add servicestore lineage support --- .../pom.xml | 15 +++ .../core_analytics_lineage.definition.json | 1 + .../core_analytics_lineage/flow.pure | 20 +++- .../core_analytics_lineage/fullAnalytics.pure | 108 ++++++++++++------ .../core_analytics_lineage/graph.pure | 13 ++- .../tests/lineageTests.pure | 24 ++++ .../core_servicestore/tests/mapping.pure | 21 ++-- 7 files changed, 151 insertions(+), 51 deletions(-) diff --git a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/pom.xml b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/pom.xml index 8199820a27f..f25c8161dea 100644 --- a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/pom.xml +++ b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/pom.xml @@ -95,6 +95,11 @@ legend-engine-pure-functions-standard-pure ${project.version} + + org.finos.legend.engine + legend-engine-xt-serviceStore-pure + ${project.version} + @@ -163,6 +168,11 @@ legend-engine-pure-functions-standard-pure ${project.version} + + org.finos.legend.engine + legend-engine-xt-serviceStore-pure + ${project.version} + @@ -209,6 +219,11 @@ legend-pure-m2-dsl-graph-pure ${legend.pure.version} + + org.finos.legend.engine + legend-engine-xt-serviceStore-pure + ${project.version} + diff --git a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage.definition.json b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage.definition.json index 217d8a0147e..0911e8a38e6 100644 --- a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage.definition.json +++ b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage.definition.json @@ -10,6 +10,7 @@ "core_functions_standard", "core_functions_unclassified", "core_relational", + "core_servicestore", "core" ] } \ No newline at end of file diff --git a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/flow.pure b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/flow.pure index a28b65177ee..11621fb363d 100644 --- a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/flow.pure +++ b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/flow.pure @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import meta::pure::store::set::*; +import meta::core::runtime::*; import meta::analytics::lineage::flow::*; import meta::relational::metamodel::*; import meta::relational::metamodel::relation::*; @@ -21,12 +23,14 @@ Class meta::analytics::lineage::flow::Flow functions : FunctionDefinition[*]; systems : System[*]; packages : Package[*]; - databases : Database[*]; + databases : meta::pure::store::set::SetBasedStore[*]; tables : NamedRelation[*]; properties : Property[*]; classes : Class[*]; groups : Group[*]; links : FlowEdge[*]; + services: String[*]; + } Class meta::analytics::lineage::flow::Group @@ -97,7 +101,7 @@ Class meta::analytics::lineage::flow::ClassNode extends FlowNode Class meta::analytics::lineage::flow::DatabaseNode extends FlowNode { - <> db : Database[1]; + <> db : SetBasedStore[1]; } Class meta::analytics::lineage::flow::NamedRelationNode extends FlowNode @@ -125,6 +129,11 @@ Class meta::analytics::lineage::flow::PackageNode extends FlowNode <> package : Package[1]; } +Class meta::analytics::lineage::flow::ServiceNode extends FlowNode +{ + <> service : String[1]; +} + function meta::analytics::lineage::flow::merge(flows:Flow[*]):Flow[1] { ^Flow @@ -136,7 +145,8 @@ function meta::analytics::lineage::flow::merge(flows:Flow[*]):Flow[1] packages = $flows.packages->removeDuplicates(), classes = $flows.classes->removeDuplicates(), properties = $flows.properties->removeDuplicates(), - links = $flows.links->removeDuplicates() + links = $flows.links->removeDuplicates(), + services = $flows.services->removeDuplicates() ) } @@ -156,7 +166,9 @@ function meta::analytics::lineage::flow::removeNode(flow:Flow[1],node:FlowNode[1 packages = if($node->instanceOf(PackageNode),|$flow.packages->filter(p|$p!=$node->cast(@PackageNode).package),|$flow.packages), classes = if($node->instanceOf(ClassNode),|$flow.classes->filter(c|$c!=$c->cast(@ClassNode).class),|$flow.classes), properties = if($node->instanceOf(PropertyNode),|$flow.properties->filter(p|$p!=$p->cast(@PropertyNode).property),|$flow.properties), - links = $flow.links->filter(l|!$l->in($in))->filter(l|!$l->in($out))->concatenate($in.source->fold({x,y:FlowEdge[*]|$y->concatenate($out.target->map(e|^FlowEdge(source=$x, target=$e, lineageMaturity=maturity($x, $e, $maturityTests))))},[])) + links = $flow.links->filter(l|!$l->in($in))->filter(l|!$l->in($out))->concatenate($in.source->fold({x,y:FlowEdge[*]|$y->concatenate($out.target->map(e|^FlowEdge(source=$x, target=$e, lineageMaturity=maturity($x, $e, $maturityTests))))},[])), + services = if($node->instanceOf(ServiceNode),|$flow.services->filter(p|$p!=$p->cast(@ServiceNode).service),|$flow.services) + ); } diff --git a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/fullAnalytics.pure b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/fullAnalytics.pure index c99afa14ddb..884ef105897 100644 --- a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/fullAnalytics.pure +++ b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/fullAnalytics.pure @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import meta::external::store::service::metamodel::mapping::*; +import meta::external::store::service::metamodel::runtime::*; +import meta::external::store::service::metamodel::*; import meta::pure::executionPlan::*; import meta::core::runtime::*; import meta::pure::lineage::scanRelations::*; @@ -87,7 +90,7 @@ function meta::analytics::lineage::computeLineage(f:FunctionDefinition[1], let sourceMapping = $mappings->last()->toOne(); let funcBody = $f.expressionSequence->evaluateAndDeactivate(); - let updatedFuncBody = $funcBody->map(e|$e->meta::pure::lineage::analytics::inlineQualifiedProperties(newMap([]->cast(@Pair), VariableExpression->classPropertyByName('name')->cast(@Property)), $f->openVariableValues(), $extensions)); + let updatedFuncBody = $funcBody->map(e|$e->meta::pure::lineage::analytics::inlineQualifiedProperties(newMap([]->cast(@Pair), VariableExpression->classPropertyByName('name ')->cast(@Property)), $f->openVariableValues(), $extensions)); let propertyTrees = $updatedFuncBody->map(e|$e->buildMultiLevelPropertyTrees($modelToModelMappings, $extensions)); let combinedTrees = if( $propertyTrees->size()>1,|$propertyTrees->first()->toOne()->findAndAddChildrenAtTheEnd([],$propertyTrees->tail()),|$propertyTrees->toOne()); @@ -190,31 +193,66 @@ function meta::analytics::lineage::flowDatabase::toFlowDatabase(f:FunctionDefini | $connections->toOne()->cast(@DatabaseConnection), | []);, | []); + + + $mappings->map(m| let flowDB = $propertyTree->toFlowDatabase($m, [], false, []); + $flowDB->map(d|$d->match( [ r:meta::analytics::lineage::FlowRelationalStore[1]| + let tables = $r.relation; + let dbs = $tables->map(r|$r->schema()).database->removeDuplicates(); + let maturityTests = maturityTests(); + ^Flow( + functions = if($tables->size() == 0 && $dbs->size() == 0, | [], | $f), + databases = $dbs, + tables = $tables->cast(@NamedRelation), + links = $tables->map(t|let db = $t->map(r|$r->schema()).database->toOne(); + let tbNode = ^NamedRelationNode(relation=$t->cast(@NamedRelation)); + let fNode = ^FunctionNode(func=$f); + let dbNode = ^DatabaseNode(db=$db); + [ + ^FlowEdge(source=$tbNode, target=$fNode, lineageMaturity=maturity($tbNode, $fNode, $maturityTests)), + ^FlowEdge(source=$dbNode, target=$tbNode, lineageMaturity=maturity($dbNode, $tbNode, $maturityTests)) + ]; + ) + );, - $mappings->map(m| - let tables = $propertyTree->toFlowDatabase($m, [], false, [])->cast(@NamedRelation)->removeDuplicates(); - let dbs = $tables->map(r|$r->schema()).database->removeDuplicates(); - let maturityTests = maturityTests(); - ^Flow( - functions = if($tables->size() == 0 && $dbs->size() == 0, | [], | $f), - databases = $dbs, - tables = $tables->cast(@NamedRelation), - links = $tables->map(t|let db = $t->map(r|$r->schema()).database->toOne(); - let tbNode = ^NamedRelationNode(relation=$t->cast(@NamedRelation)); - let fNode = ^FunctionNode(func=$f); - let dbNode = ^DatabaseNode(db=$db); - [ - ^FlowEdge(source=$tbNode, target=$fNode, lineageMaturity=maturity($tbNode, $fNode, $maturityTests)), - ^FlowEdge(source=$dbNode, target=$tbNode, lineageMaturity=maturity($dbNode, $tbNode, $maturityTests)) - ]; - ) - ); - )->merge(); -} - -function meta::analytics::lineage::flowDatabase::toFlowDatabase(p:PropertyPathTree[1], m:Mapping[1], sets:SetImplementation[*], disregardClass:Boolean[1], extraChildren:PropertyPathTree[*]):NamedRelation[*] -{ - $p.value->match([ + s:meta::analytics::lineage::FlowService[1]|let serviceUrls = $s.services->map( service| let url= $runtime->toOne()->connectionByElement($service.owner)->cast(@ServiceStoreConnection).baseUrl + $service.resolveFullPathRecursively(); + let serviceNode = ^ServiceNode(service=$url); + let dbNode = ^DatabaseNode(db=$service.owner); + let fNode = ^FunctionNode(func=$f); + ^Flow(services=$url, + functions = $f, + databases = $service.owner, + links = [^FlowEdge(source=$serviceNode, target=$dbNode), + ^FlowEdge(source=$dbNode, target=$fNode)] + ); + ); + + + ]); + + + ) ;)->merge(); +} + +Class meta::analytics::lineage::FlowStore +{ + + +} +Class meta::analytics::lineage::FlowRelationalStore extends meta::analytics::lineage::FlowStore +{ + relation:NamedRelation[*]; +} + +Class meta::analytics::lineage::FlowService extends meta::analytics::lineage::FlowStore +{ + + services:meta::external::store::service::metamodel::Service[*]; + +} + +function meta::analytics::lineage::flowDatabase::toFlowDatabase(p:PropertyPathTree[1], m:Mapping[1], sets:SetImplementation[*], disregardClass:Boolean[1], extraChildren:PropertyPathTree[*]):meta::analytics::lineage::FlowStore[*] +{ $p.value->match([ pr:PropertyPathNode[1]| let possiblePropertyTargetClasses = $p.children.value->map(vv|$vv->match([ v:PropertyPathNode[1]|$v.class, @@ -226,9 +264,11 @@ function meta::analytics::lineage::flowDatabase::toFlowDatabase(p:PropertyPathTr let isDataTypeProperty = !$pr.property.genericType.rawType->isEmpty() && $pr.property.genericType.rawType->toOne()->instanceOf(DataType); if ($isDataTypeProperty, | $propertyMappings->map(pm |$pm->match([ - rpm: RelationalPropertyMapping[1] | $rpm->meta::analytics::lineage::flowDatabase::getTables(), + rpm: RelationalPropertyMapping[1] |^FlowRelationalStore(relation= $rpm->meta::analytics::lineage::flowDatabase::getTables()), ppm: PurePropertyMapping[1] | [], - agr: meta::pure::mapping::aggregationAware::AggregationAwarePropertyMapping[1] |meta::pure::router::routing::reprocessAggregationAwarePropertyMapping($agr)->cast(@RelationalPropertyMapping)->meta::analytics::lineage::flowDatabase::getTables();, + s:ServiceStorePropertyMapping[1] | + let service = $s.owner->cast(@RootServiceInstanceSetImplementation).servicesMapping.service; + ^meta::analytics::lineage::FlowService(services=$service);, a: Any[*] | fail('Database lineage not support for given type of mapping: ' + $pm->typeName()); []; ])), | $propertyMappings->map(pm|processNonDataTypeProperty($p, $pm, $possiblePropertyTargetClasses, $m, $extraChildren)) @@ -250,7 +290,7 @@ function meta::analytics::lineage::flowDatabase::toFlowDatabase(p:PropertyPathTr ]); } -function <> meta::analytics::lineage::flowDatabase::processNonDataTypeProperty(p:PropertyPathTree[1], pm: PropertyMapping[1], possiblePropertyTargetClasses: Class[*], m:Mapping[1], extraChildren:PropertyPathTree[*]): NamedRelation[*] +function <> meta::analytics::lineage::flowDatabase::processNonDataTypeProperty(p:PropertyPathTree[1], pm: PropertyMapping[1], possiblePropertyTargetClasses: Class[*], m:Mapping[1], extraChildren:PropertyPathTree[*]): meta::analytics::lineage::FlowStore[*] { let targetSetImplementationId = $pm->match([ i: InlineEmbeddedRelationalInstanceSetImplementation[1]| @@ -263,13 +303,11 @@ function <> meta::analytics::lineage::flowDatabase::processNonDa | $targetSiId); ]); - let potentialTablesFromJoin = $pm->match([ - r:RelationalPropertyMapping[1]| - if($targetSetImplementationId->contains($r.targetSetImplementationId), - | $r->meta::analytics::lineage::flowDatabase::getTables(), - | []), - a:Any[1]|[] - ]); + let potentialTablesFromJoin = $pm->match([ r:RelationalPropertyMapping[1]|if($targetSetImplementationId->contains($r.targetSetImplementationId), + |^meta::analytics::lineage::FlowRelationalStore(relation=$r->meta::analytics::lineage::flowDatabase::getTables()), + | []), + a:Any[1]|[] + ]); let tablesFromChildren = meta::analytics::lineage::flowDatabase::manageQualifiers($p.children, $extraChildren) ->map(c|$c->toFlowDatabase($m, $targetSetImplementationId->map(id|$m->classMappingById($id)), false, $extraChildren)); diff --git a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/graph.pure b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/graph.pure index e9398d6fae1..ea4db89caea 100644 --- a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/graph.pure +++ b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/graph.pure @@ -13,6 +13,7 @@ // limitations under the License. +import meta::external::store::service::metamodel::*; import meta::analytics::lineage::flow::*; import meta::analytics::lineage::graph::*; import meta::json::*; @@ -88,7 +89,7 @@ function meta::analytics::lineage::graph::toGraph(flow:Flow[1], base:Flow[0..1]) let groupNodesIndex = $groupNodes->groupBy(g|$g.data.id); let index = $flow.groups->map(g|$g.nodes->map(n|pair($n->toId(), $groupNodesIndex->get($g->toId())->toOne().values->at(0))))->newMap(); - let nodes = $flow.groups->concatenate($flow.functions)->concatenate($flow.systems)->concatenate($flow.tables)->concatenate($flow.databases)->concatenate($flow.properties)->concatenate($flow.classes)->concatenate($flow.packages)->map(o|$o->toNode($index,$base)); + let nodes = $flow.groups->concatenate($flow.functions)->concatenate($flow.systems)->concatenate($flow.tables)->concatenate($flow.databases)->concatenate($flow.properties)->concatenate($flow.classes)->concatenate($flow.packages)->concatenate($flow.services)->map(o|$o->toNode($index,$base)); let nodesById = $nodes->groupBy(t|$t.data.id); ^Graph @@ -139,7 +140,10 @@ function <> meta::analytics::lineage::graph::toId(a:Any[1]):Stri a:Database[1]|'db_'+$a.name->toOne(), a:DatabaseNode[1]|$a.db->toId(), a:NamedRelation[1]|let schema = $a->schema(); 'tb_'+$schema.database.name->toOne()+$schema.name+$a.name;, - a:NamedRelationNode[1]|$a.relation->toId() + a:NamedRelationNode[1]|$a.relation->toId(), + s:ServiceStore[1]|'service_'+$s->elementToPath(), + s:String[1] |$s, + s:ServiceNode[1]| $s.service ] ) } @@ -173,7 +177,10 @@ function <> meta::analytics::lineage::graph::toName(a:Any[1]):St a:System[1]|$a.name, a:Group[1]|$a.name, a:Database[1]|$a.name->toOne(), - a:NamedRelation[1]|$a.name; + a:NamedRelation[1]|$a.name, + s:ServiceStore[1]|$s.name->toOne(), + s:String[1]|$s + ] ) } diff --git a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/tests/lineageTests.pure b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/tests/lineageTests.pure index 83380aaa840..136476b7ce1 100644 --- a/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/tests/lineageTests.pure +++ b/legend-engine-xts-analytics/legend-engine-xts-analytics-lineage/legend-engine-xt-analytics-lineage-pure/src/main/resources/core_analytics_lineage/tests/lineageTests.pure @@ -493,3 +493,27 @@ function <> meta::analytics::lineage::tests::re '[type: [ProductClassificationSystemTable.id , ProductClassificationSystemTable.name , ProductTable.classificationSystemId ]]', $lineage); } + + +###Pure +import meta::analytics::lineage::*; +import meta::external::store::service::tests::router::*; +import meta::external::store::service::tests::runtime::*; +import meta::external::store::service::tests::mapping::*; +import meta::pure::graphFetch::execution::*; +import meta::external::store::service::tests::domain::*; +function <> meta::analytics::lineage::tests::servicestore::testServiceStoreimple():Boolean[1] +{ + let tree = #{ + S_Trade { + s_tradeId, + s_traderDetails, + s_tradeDetails + } + }#; + + let query = {|S_Trade.all()->graphFetch($tree)->serialize($tree)}; + let lineage = $query->computeLineage(serviceStoreMapping(), serviceStoreRuntime(), serviceStoreTestExtensions()); + assertLineage(['Lambda', 'http://127.0.0.1:9090/api/user/serviceStore/test/trades/allTradesService', 'service_meta::external::store::service::tests::store::TradeProductServiceStore'], + ['Lambda', 'meta::external::store::service::tests::domain::S_Trade', 'pack_meta::external::store::service::tests::domain'],'[S_Trade.s_tradeDetails: [], S_Trade.s_tradeId: [], S_Trade.s_traderDetails: []]',$lineage); +} \ No newline at end of file diff --git a/legend-engine-xts-serviceStore/legend-engine-xt-serviceStore-pure/src/main/resources/core_servicestore/tests/mapping.pure b/legend-engine-xts-serviceStore/legend-engine-xt-serviceStore-pure/src/main/resources/core_servicestore/tests/mapping.pure index 627a80855c0..91c4fc986f0 100644 --- a/legend-engine-xts-serviceStore/legend-engine-xt-serviceStore-pure/src/main/resources/core_servicestore/tests/mapping.pure +++ b/legend-engine-xts-serviceStore/legend-engine-xt-serviceStore-pure/src/main/resources/core_servicestore/tests/mapping.pure @@ -144,18 +144,13 @@ function meta::external::store::service::tests::mapping::serviceStoreMapping():M let serviceStore = meta::external::store::service::tests::store::TradeProductServiceStore(); - //Generated in compilation phase using schema binding details - let tradeSetPropMappings= [ - ^ServiceStorePropertyMapping(property = S_Trade.properties->filter(p | $p.name == 's_tradeId')->toOne(), sourceSetImplementationId = 's_trade_set', targetSetImplementationId = ''), - ^ServiceStorePropertyMapping(property = S_Trade.properties->filter(p | $p.name == 's_traderDetails')->toOne(), sourceSetImplementationId = 's_trade_set', targetSetImplementationId = ''), - ^ServiceStorePropertyMapping(property = S_Trade.properties->filter(p | $p.name == 's_tradeDetails')->toOne(), sourceSetImplementationId = 's_trade_set', targetSetImplementationId = '') - ]; - let tradeServiceSetImpl = ^RootServiceInstanceSetImplementation( +let tradeServiceSetImpl = ^RootServiceInstanceSetImplementation( id = 's_trade_set', root = true, class = meta::external::store::service::tests::domain::S_Trade, - parent = $baseMapping, - propertyMappings = $tradeSetPropMappings); + parent = $baseMapping + ); + let tradeService = $serviceStore.getServiceStoreElementById('TradeServices')->cast(@ServiceGroup).getServiceStoreElementById('AllTradeService')->cast(@Service); let transform3 = {| 'CSV'}; @@ -169,6 +164,14 @@ function meta::external::store::service::tests::mapping::serviceStoreMapping():M requestBodyBuildInfo = $requestBodyBuildInfo)); $tradeServiceSetImpl->mutateAdd('servicesMapping', $tradeServicesMapping); + let tradeSetPropMappings= [ + ^ServiceStorePropertyMapping(property = S_Trade.properties->filter(p | $p.name == 's_tradeId')->toOne(), sourceSetImplementationId = 's_trade_set', targetSetImplementationId = '', owner =$tradeServiceSetImpl), + ^ServiceStorePropertyMapping(property = S_Trade.properties->filter(p | $p.name == 's_traderDetails')->toOne(), sourceSetImplementationId = 's_trade_set', targetSetImplementationId = '',owner=$tradeServiceSetImpl), + ^ServiceStorePropertyMapping(property = S_Trade.properties->filter(p | $p.name == 's_tradeDetails')->toOne(), sourceSetImplementationId = 's_trade_set', targetSetImplementationId = '',owner=$tradeServiceSetImpl) + ]; + $tradeServiceSetImpl->mutateAdd('propertyMappings', $tradeSetPropMappings); + + let prodServiceSetImpl = ^RootServiceInstanceSetImplementation( id = 's_prod_set', root = true,