All Downloads are FREE. Search and download functionalities are using the official Maven repository.

core_servicestore.executionPlan.executionPlan_generation.pure Maven / Gradle / Ivy

The newest version!
// 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.

import meta::pure::executionPlan::*;
import meta::pure::functions::collection::*;

import meta::pure::graphFetch::*;
import meta::pure::graphFetch::execution::*;
import meta::pure::graphFetch::executionPlan::*;
import meta::pure::graphFetch::routing::*;

import meta::pure::metamodel::path::*;

import meta::pure::mapping::*;
import meta::external::store::model::*;
import meta::pure::mapping::modelToModel::executionContext::*;
import meta::pure::mapping::modelToModel::graphFetch::executionPlan::*;
import meta::pure::mapping::xStore::*;

import meta::pure::extension::*;

import meta::core::runtime::*;
import meta::pure::router::store::metamodel::*;
import meta::pure::router::store::metamodel::clustering::*;
import meta::pure::router::utils::*;

import meta::pure::store::*;

import meta::external::format::shared::binding::*;
import meta::external::format::shared::binding::validation::*;
import meta::external::format::shared::executionPlan::*;
import meta::external::format::shared::router::extension::*;
import meta::external::format::shared::utils::*;

import meta::external::store::service::executionPlan::nodes::*;
import meta::external::store::service::executionPlan::generation::*;
import meta::external::store::service::metamodel::*;
import meta::external::store::service::metamodel::runtime::*;
import meta::external::store::service::metamodel::mapping::*;
import meta::external::store::service::functions::pureToServiceStoreQuery::*;
import meta::external::store::service::router::systemMapping::*;
import meta::external::store::service::utils::helperFunction::*;

function meta::external::store::service::executionPlan::generation::planRootGraphFetchExecutionServiceStore(sq: StoreQuery[1], ext: RoutedValueSpecification[0..1], clusteredTree: StoreMappingClusteredGraphFetchTree[1], orderedPaths: String[*], mapping: Mapping[1], runtime: Runtime[1], exeCtx: meta::pure::runtime::ExecutionContext[1], enableConstraints: Boolean[1], checked: Boolean[1], extensions: meta::pure::extension::Extension[*], debug: DebugContext[1]): InMemoryRootGraphFetchExecutionNode[1]
{
   let store       = $sq.store;
   assert($store->instanceOf(ServiceStore), | 'Expected a ServiceStore. Found - ' + $store->cast(@Store)->elementToPath());

   let fe          = $sq.fe->evaluateAndDeactivate();

   let rootTree    = $clusteredTree->byPassClusteringInfo()->cast(@RoutedRootGraphFetchTree);
   let batchSize   = if($fe.func == graphFetch_T_MANY__RootGraphFetchTree_1__Integer_1__T_MANY_ || $fe.func == meta::pure::graphFetch::execution::graphFetchChecked_T_MANY__RootGraphFetchTree_1__Integer_1__Checked_MANY_,
                      | $fe->instanceValuesAtParameter(2, $sq.inScopeVars)->toOne()->cast(@Integer),
                      | 1);

   let connection  = $runtime->connectionByElement($store);

   let sourceTree         = calculateSourceTree($rootTree, $mapping, $extensions);
   let sourceTreeExtended = if($enableConstraints, | $sourceTree->ensureConstraintsRequirements(), | $sourceTree);

   let serviceSetImpl  =  $rootTree.sets->toOne()->cast(@RootServiceInstanceSetImplementation);

   let setsProcessed   = if($ext->isNotEmpty() && $ext->toOne()->instanceOf(StoreMappingRoutedValueSpecification), | $ext->cast(@StoreMappingRoutedValueSpecification).processedChainSets, |[]);
   let map             = resolveParamMapForChainProcessing($setsProcessed, $sq.inScopeVars, $debug);
   let serviceStoreQueryProcessedState = $sq->processServiceStoreQuery($map, $debug);

   let sourceNode          = $connection->meta::external::store::service::executionPlan::generation::nodeFromServiceStoreConnection($sourceTreeExtended, $serviceSetImpl, $serviceStoreQueryProcessedState, $enableConstraints, $checked, $extensions);
   let sourceNodeDecorator = {sourceNode:ExecutionNode[1] | if($serviceStoreQueryProcessedState.recordsToBeRead->isEmpty(),
                                                               |$sourceNode,
                                                               |assert($setsProcessed->cast(@PureInstanceSetImplementation).filter->isEmpty(), | 'Service Store does not support ->take when filters added in chained M2m mapping');
                                                                let fromCluster = ^StoreMappingClusteredValueSpecification(
                                                                                      val = $fe,
                                                                                      store = $store,
                                                                                      s = meta::external::store::service::contract::serviceStoreStoreContract(),
                                                                                      mapping = $mapping,
                                                                                      executable=true,
                                                                                      multiplicity = $fe.multiplicity,
                                                                                      genericType  = $fe.genericType,
                                                                                      openVars = $sq.inScopeVars
                                                                                  );
                                                                ^LimitExecutionNode(limit          = $serviceStoreQueryProcessedState.recordsToBeRead->toOne(),
                                                                                    resultType     = $sourceNode.resultType,
                                                                                    executionNodes = ^$sourceNode(fromCluster = $fromCluster));)};

   ^InMemoryRootGraphFetchExecutionNode
   (
      resultType        = $rootTree->resultTypeFromGraphFetchTree(),
      nodeIndex         = 0,
      graphFetchTree    = $rootTree,
      batchSize         = $batchSize,
      checked           = $checked,
      children          = $rootTree->generateInMemoryChildGraphNodes($rootTree->nodePathName(), $orderedPaths, $debug),
      executionNodes    = $sourceNodeDecorator->eval($sourceNode)
   );
}

function meta::external::store::service::executionPlan::generation::planCrossStoreGraphFetchExecutionServiceStore(clusteredTree: StoreMappingClusteredGraphFetchTree[1], orderedPaths: String[*], parentPath: String[1], inScopeVars: Map>[1], mapping: Mapping[1], runtime: Runtime[1], exeCtx: meta::pure::runtime::ExecutionContext[1], enableConstraints: Boolean[1], checked: Boolean[1], extensions: Extension[*], debug: DebugContext[1]): InMemoryCrossStoreGraphFetchExecutionNode[1]
{
   let store            = $clusteredTree.store;
   assert($store->instanceOf(ServiceStore), | 'Expected a ServiceStore. Found - ' + $store->cast(@Store)->elementToPath());

   let rootTree         = $clusteredTree->byPassClusteringInfo()->cast(@RoutedPropertyGraphFetchTree);
   let updatedRootTree  = ^RoutedRootGraphFetchTree
                           (
                              subTrees = $rootTree.subTrees,
                              class = $rootTree->typeFromGraphFetchTree()->cast(@Class),
                              sets = $rootTree.sets,
                              requiredQualifiedProperties = $rootTree.requiredQualifiedProperties
                           );
   let rootPath         = $parentPath + '.' + $rootTree->nodePathName();

   /* Assertions */
   assert($parentPath->in($orderedPaths), | 'Unknown path ' + $parentPath + '; known are: ' + $orderedPaths->joinStrings('[', '; ', ']'));
   assert($rootPath->in($orderedPaths),   | 'Unknown path ' + $rootPath   + '; known are: ' + $orderedPaths->joinStrings('[', '; ', ']'));
   assertFalse($checked, | 'graphFetchChecked is not supported with Service Store');

   let parentIdx             = $orderedPaths->indexOf($parentPath);
   let currentIdx            = $orderedPaths->indexOf($rootPath);

   let xStorePropertyMapping = $rootTree.propertyMapping->toOne()->cast(@XStorePropertyMapping);

   let connection            = $runtime->connectionByElement($store);

   let sourceTree            = calculateSourceTree($updatedRootTree, $mapping, $extensions);
   let sourceTreeExtended    = if($enableConstraints, | $sourceTree->ensureConstraintsRequirements(), | $sourceTree);

   let xStorePropInScope     = $xStorePropertyMapping->getPopulatedXStorePropertiesInScope($rootTree->typeFromGraphFetchTree()->cast(@Class));
   assert($xStorePropInScope->forAll(prop | $prop.multiplicity->hasToOneUpperBound()), 'All properties in XStore relationship should have 1 as multiplicty upper bound for service store.');
   let paramMap              = $xStorePropInScope->getParamMapFromXStoreProperties($rootTree->typeFromGraphFetchTree()->cast(@Class));
   let serviceSetImpl        = $rootTree.sets->toOne()->cast(@RootServiceInstanceSetImplementation);

   ^InMemoryCrossStoreGraphFetchExecutionNode
   (
      resultType        = $rootTree->resultTypeFromGraphFetchTree(),
      parentIndex       = $parentIdx,
      nodeIndex         = $currentIdx,
      graphFetchTree    = $rootTree,
      checked           = $checked,
      xStorePropertyMapping = $xStorePropertyMapping,
      supportsBatching  = $xStorePropInScope->querySupportsBatching($paramMap, $serviceSetImpl),
      children          = $rootTree->generateInMemoryChildGraphNodes($rootPath, $orderedPaths, $debug),
      executionNodes    = $connection->meta::external::store::service::executionPlan::generation::nodeFromServiceStoreConnection($sourceTreeExtended, $serviceSetImpl, ^ServiceStoreQueryProcessedState(paramMap = $paramMap), $enableConstraints, $checked, $extensions)
   );
}


// Helper functions to parse query and generate required allocationNodes & param map

function meta::external::store::service::executionPlan::generation::getParamMapFromXStoreProperties(properties: AbstractProperty[*], rootClass:Class[1]):Map[1]
{
   $properties->map(p | pathAsString(^Path(path=^PropertyPathElement(property=$p), start = ^GenericType(rawType = $rootClass))))
              ->map(p | pair($p, $p))->newMap();
}

Class <> meta::external::store::service::executionPlan::generation::ServiceStoreQueryProcessedState
{
   executionNodes  : ExecutionNode[*];
   paramMap        : Map[1];
   recordsToBeRead : Integer[0..1];
}

function meta::external::store::service::executionPlan::generation::processServiceStoreQuery(sq:StoreQuery[1], initialMap:Map[1], debug: DebugContext[1]):ServiceStoreQueryProcessedState[1]
{
   let serviceStoreQuery = $sq.fe->toServiceStoreQuery($sq.inScopeVars, $debug);
   let paramValues       = $serviceStoreQuery.processedParamValueMap->keyValues();

   let literalParams     = $paramValues->filter(p | $p.second->instanceOf(LiteralValue));
   let varParams         = $paramValues->filter(p | $p.second->instanceOf(VariableValue));

   let allocationNodes   = $literalParams->map(param | ^AllocationExecutionNode(varName        = $param.first,
                                                                                executionNodes = ^ConstantExecutionNode(
                                                                                                     //TODO: This is valid now because we don't support in for service store but would need to be fixed when in is supported.
                                                                                                     //When value is a list it get translated to CList with CString values which cause issues in parsing at execution time.
                                                                                                     values     = $param.second->cast(@LiteralValue).value->toOne(),
                                                                                                     resultType = ^ResultType(type = Any)
                                                                                                  ),
                                                                                resultType     = ^ResultType(type = Any)
                                                                             ));

   let paramMap          = $paramValues->map(p | let varName = $p.second->match([
                                                                  v:VariableValue[1] | $v.var.name,
                                                                  l:LiteralValue[1]  | $p.first
                                                               ]);
                                                 pair($p.first, $varName);)->newMap();

   ^ServiceStoreQueryProcessedState(executionNodes = $allocationNodes,
                                    paramMap = $initialMap->putAll($paramMap),
                                    recordsToBeRead = $serviceStoreQuery.recordsToBeRead);
}

// Helper Functions to analyze query supports batching

function meta::external::store::service::executionPlan::generation::querySupportsBatching(xStorePropInScope:AbstractProperty[*], paramMap:Map[1], serviceSetImpl:RootServiceInstanceSetImplementation[1]):Boolean[1]
{
   let xStorePropertyIsLocal = $xStorePropInScope->exists(prop | $prop.owner->instanceOf(MappingClass));
   let serviceMappingInScope = $serviceSetImpl->getServiceMappingForParameters($paramMap->keys());
   let serParamForXStoreProp = $serviceMappingInScope.requestBuildInfo.requestParametersBuildInfo.parameterBuildInfoList->filter(pm | $pm.transform.expressionSequence->evaluateAndDeactivate()->toOne()->findPropertiesInValueSpecification()->exists(p | $p->in($xStorePropInScope))).serviceParameter;
   !$xStorePropertyIsLocal && $serParamForXStoreProp.type->forAll(t | $t.list);
}

// Helper Functions to generate node for connection with query context

function meta::external::store::service::executionPlan::generation::nodeFromServiceStoreConnection(c:Connection[1], tree:RootGraphFetchTree[1], serviceSetImpl:RootServiceInstanceSetImplementation[1], serviceStoreQueryProcessedState:ServiceStoreQueryProcessedState[1], enableConstraints:Boolean[1], checked:Boolean[1], extensions:meta::pure::extension::Extension[*]):ExecutionNode[1]
{
  let serviceMapping = $serviceSetImpl->getServiceMappingForParameters($serviceStoreQueryProcessedState.paramMap->keys());
  let s              = $c->cast(@ServiceStoreConnection);
  let serviceParamResolutionNode = getServiceParametersResolutionExecutionNode($serviceMapping.requestBuildInfo.requestParametersBuildInfo, $serviceStoreQueryProcessedState.paramMap);

  assert($serviceMapping.service.requestBody->isEmpty() || $serviceMapping.service.requestBody->toOne()->instanceOf(ComplexTypeReference), | 'Request Body with primitive type not supported yet!!');
  let serviceBodyResolutionNode  = getServiceRequestBodyResolutionExecutionNode($serviceMapping, $serviceStoreQueryProcessedState.paramMap, $extensions);
  let requestBodyDescription     = if($serviceBodyResolutionNode->isEmpty(),
                                      | [],
                                      |^RequestBodyDescription(mimeType = $serviceMapping.service.requestBody->cast(@ComplexTypeReference).binding.contentType->toOne(),
                                                               resultKey = $serviceBodyResolutionNode->toOne()->cast(@AllocationExecutionNode).varName));

  let mappedParameters           = $serviceMapping.requestBuildInfo.requestParametersBuildInfo.parameterBuildInfoList.serviceParameter;
  let serviceStoreNode           = ^RestServiceExecutionNode(url                    = $s.baseUrl + $serviceMapping.service.resolveFullPathRecursively(),
                                                             method                 = $serviceMapping.service.method,
                                                             mimeType               = $serviceMapping.service.response.binding.contentType,
                                                             params                 = $serviceMapping.service.parameters,
                                                             // Combining Security schemes(with logical AND) is not supported yet.
                                                             authenticationSchemes  = $serviceMapping.service.security->map(a | ^SingleAuthenticationSchemeRequirement(securityScheme = $a->cast(@SingleSecuritySchemeRequirement).securityScheme, authenticationSpecification = $s.authenticationSpecifications->get($a->cast(@SingleSecuritySchemeRequirement).id))),
                                                             requestBodyDescription = $requestBodyDescription,
                                                             requiredVariableInputs = $mappedParameters.name->concatenate($requestBodyDescription.resultKey)->map(v | ^VariableInput(name = $v, type = Any, multiplicity = ZeroMany)),
                                                             resultType             = ^DataTypeResultType(type = String));

  let allNodes = $serviceStoreQueryProcessedState.executionNodes->concatenate($serviceParamResolutionNode)
                                                                ->concatenate($serviceBodyResolutionNode)
                                                                ->concatenate($serviceStoreNode);
  let node     = if($allNodes->size() > 1,
                    | ^SequenceExecutionNode(executionNodes = $allNodes, resultType = $serviceStoreNode.resultType),
                    | $allNodes)->toOne();

  let config   = if($serviceMapping.pathOffset->isNotEmpty(),
                    |assert($serviceMapping.service.response.binding.contentType->in(meta::external::format::json::contract::jsonSchemaFormatContract().contentTypes), 'PathOffset is supported with json only!');
                      meta::external::format::json::metamodel::internalize::generateJsonSchemaInternalizeConfig($serviceMapping.pathOffset->toOne());,
                    |[]);

  ^ExternalFormatInternalizeExecutionNode
  (
      resultType        = ^PartialClassResultType(
                            type = $tree.class,
                            propertiesWithParameters = $tree.subTrees->cast(@PropertyGraphFetchTree)->map(x | $x->map(x | ^PropertyWithParameters(property = $x.property, parameters = $x.parameters)))
                          ),
      resultSizeRange   = ZeroMany,
      tree              = $tree,
      binding           = $serviceMapping.service.response.binding,
      executionNodes    = $node,
      config            = $config,
      enableConstraints = $enableConstraints,
      checked           = $checked
  );
}

function meta::external::store::service::executionPlan::generation::getServiceMappingForParameters(serviceSetImpl:RootServiceInstanceSetImplementation[1], availablePropPaths:String[*]):ServiceMapping[1]
{
   let propToServiceMappingMap = $serviceSetImpl.servicesMapping->map(sm | let reqPropPaths = $sm.requestBuildInfo.requestParametersBuildInfo.parameterBuildInfoList
                                                                                                                   ->map(pb | $pb.transform.expressionSequence->toOne()->findAndReplacePropertyPathsInValueSpecification([]).second.values)
                                                                                                                   ->concatenate($sm.requestBuildInfo.requestBodyBuildInfo.transform.expressionSequence->map(x | $x->findAndReplacePropertyPathsInValueSpecification([]).second.values));
                                                                           let propPaths    = if($reqPropPaths->isEmpty(), |'', |$reqPropPaths)->removeDuplicates();
                                                                           pair($propPaths->sort()->joinStrings(','), $sm);)->newMap();

   let reqServiceMapping       = $propToServiceMappingMap->get($availablePropPaths->sort()->joinStrings(','));
   assert($reqServiceMapping->isNotEmpty(), |'No service mapping found for available parameters. Available params - ' + $availablePropPaths->sort()->joinStrings('[', ', ', ']') + '. Available paths - ' +  $propToServiceMappingMap->keys()->joinStrings('[', ', ', ']'));

   $reqServiceMapping->toOne();
}

function meta::external::store::service::executionPlan::generation::getServiceParametersResolutionExecutionNode(parametersBuildInfo:ServiceRequestParametersBuildInfo[0..1], paramMap:Map[1]):ServiceParametersResolutionExecutionNode[0..1]
{
   let parameterBuildInfoList = $parametersBuildInfo.parameterBuildInfoList;
   if($parameterBuildInfoList->isEmpty(),
      |[],
      |let updatedMappingAndSources = $parameterBuildInfoList->map(pb | let transform                = $pb.transform;
                                                                        let updatedTransformAndPaths = $transform.expressionSequence->toOne()->findAndReplacePropertyPathsInValueSpecification([], $paramMap)->toOne();
                                                                        let requiredVariableInputs   = $updatedTransformAndPaths.second.values->map(path | ^VariableInput(name = $paramMap->get(pathAsString($path))->toOne(),
                                                                                                                                                                          type = $path.path->at(0)->cast(@PropertyPathElement).property->functionReturnType().rawType->toOne(),
                                                                                                                                                                          multiplicity = $path.path->at(0)->cast(@PropertyPathElement).property->functionReturnMultiplicity()));
                                                                        pair(^$pb(transform = ^$transform(expressionSequence = $updatedTransformAndPaths.first->cast(@ValueSpecification))), ^List(values = $requiredVariableInputs)););

       ^ServiceParametersResolutionExecutionNode(requiredVariableInputs     = $updatedMappingAndSources.second.values,
                                                 requestParametersBuildInfo = ^ServiceRequestParametersBuildInfo(parameterBuildInfoList = $updatedMappingAndSources.first),
                                                 resultType                 = ^DataTypeResultType(type = Map)););
}

function meta::external::store::service::executionPlan::generation::getServiceRequestBodyResolutionExecutionNode(serviceMapping:ServiceMapping[1], paramMap:Map[1], extensions:meta::pure::extension::Extension[*]):ExecutionNode[0..1]
{
   let requestBodyBuildInfo = $serviceMapping.requestBuildInfo.requestBodyBuildInfo;
   let requestBodyType      = $serviceMapping.service.requestBody;

   if($requestBodyBuildInfo->isEmpty(),
      |[],
      |let requestBodyBuildInfoOne     = $requestBodyBuildInfo->toOne();
       let updatedTransformAndPropPath = $requestBodyBuildInfoOne.transform.expressionSequence->toOne()->findAndReplacePropertyPathsInValueSpecification([], $paramMap)->toOne();

       let resultType                  = if($requestBodyType->toOne()->instanceOf(ComplexTypeReference),
                                            |^ClassResultType(setImplementations = [], type = $requestBodyType->cast(@ComplexTypeReference).type->toOne()),
                                            |^DataTypeResultType(type = String));

       let requiredVariableInputs      = $updatedTransformAndPropPath.second.values->map(path | ^VariableInput(name = $paramMap->get(pathAsString($path))->toOne(),
                                                                                                               type = $path.path->at(0)->cast(@PropertyPathElement).property->functionReturnType().rawType->toOne(),
                                                                                                               multiplicity = $path.path->at(0)->cast(@PropertyPathElement).property->functionReturnMultiplicity()));
       let requestBodyResolutionNode   = ^PureExpressionPlatformExecutionNode(expression             = $updatedTransformAndPropPath.first->cast(@ValueSpecification),
                                                                              requiredVariableInputs = $requiredVariableInputs,
                                                                              resultType             = $resultType);

       let serializedRequestBody       = ^ExternalFormatExternalizeExecutionNode
                                          (
                                              resultType      = ^ResultType(type=String),
                                              resultSizeRange = PureOne,
                                              checked         = false,
                                              binding         = $requestBodyType->toOne()->cast(@ComplexTypeReference).binding,
                                              tree            = generateRootTreeFromBinding($requestBodyType->toOne()->cast(@ComplexTypeReference).type, $requestBodyType->toOne()->cast(@ComplexTypeReference).binding, $extensions),
                                              executionNodes  = $requestBodyResolutionNode
                                          );

       ^AllocationExecutionNode(varName        = $serviceMapping.service.owner->elementToPath('_') + '_' + $serviceMapping.service.resolveFullPathRecursively()->replace('/', '_') + '_requestBody',
                                executionNodes = $serializedRequestBody,
                                resultType     = ^VoidResultType(type = meta::pure::router::store::routing::Void));
   );
}

function <> meta::external::store::service::executionPlan::generation::generateRootTreeFromBinding(classIn:meta::pure::metamodel::type::Class[1], binding:Binding[1], extensions:Extension[*]): RootGraphFetchTree[1]
{
  let externalFormatContract = $extensions.availableExternalFormats->getExternalFormatContractForContentType($binding.contentType);
  let bindingDetail          = $externalFormatContract.validateBinding($binding);

  assert($bindingDetail->instanceOf(SuccessfulBindingDetail), | 'Binding validation failed.\n' + $bindingDetail->cast(@FailedBindingDetail).errorMessages->joinStrings('\n'));

  let propertiesInScope      = $bindingDetail->cast(@SuccessfulBindingDetail).mappedPropertiesForClass($classIn);
  let subTrees               = $propertiesInScope->map(p | $p->generatePropertyTreeForProperty($bindingDetail->cast(@SuccessfulBindingDetail)));

  ^RootGraphFetchTree(class=$classIn, subTrees = $subTrees);
}

function <> meta::external::store::service::executionPlan::generation::generatePropertyTreeForProperty(property:AbstractProperty[1], detail:SuccessfulBindingDetail[1]): PropertyGraphFetchTree[1]
{
   let returnType           = $property->functionReturnType().rawType->toOne();
   let isPropertyPrimitive  = $returnType->instanceOf(meta::pure::metamodel::type::PrimitiveType) || $returnType->instanceOf(meta::pure::metamodel::type::Enumeration) ;

   let subPropertiesInScope = if($isPropertyPrimitive, | [], | $detail.mappedPropertiesForClass($returnType->cast(@meta::pure::metamodel::type::Class)));
   let subTrees             = $subPropertiesInScope->map(p | $p->generatePropertyTreeForProperty($detail));

   ^PropertyGraphFetchTree(property=$property, subTrees = $subTrees);
}

function meta::external::store::service::executionPlan::generation::findAndReplacePropertyPathsInValueSpecification(vs:ValueSpecification[1], propertiesInCurrentPath:AbstractProperty[*]):Pair>[0..1]
{
   findAndReplacePropertyPathsInValueSpecification($vs, $propertiesInCurrentPath, newMap([])->cast(@Map))
      ->map(p | pair($p.first, ^List(values = $p.second.values->map(p | pathAsString($p)))));
}

function meta::external::store::service::executionPlan::generation::findAndReplacePropertyPathsInValueSpecification(vs:ValueSpecification[1], propertiesInCurrentPath:AbstractProperty[*], paramMap:Map[1]):Pair>>[0..1]
{
   $vs->deepByPassRouterInfo()
      ->match([fe:FunctionExpression[1] | let newPropertiesInCurrentPath = if ($fe.func->instanceOf(AbstractProperty),
                                                                               | $fe.func->cast(@AbstractProperty)->concatenate($propertiesInCurrentPath);,
                                                                               | [];);

                                          let res       = $fe.parametersValues->map(v | $v->findAndReplacePropertyPathsInValueSpecification($newPropertiesInCurrentPath, $paramMap));

                                          let propPaths = $newPropertiesInCurrentPath->map(p | ^PropertyPathElement(property=$p));
                                          let newPath   = if($newPropertiesInCurrentPath->isNotEmpty(),
                                                            | ^Path(path=$propPaths->toOneMany(), start=$fe.parametersValues->toOne().genericType),
                                                            | []);

                                          let updatedFe = if ($fe.func->instanceOf(AbstractProperty),
                                                                 |let newPathString = pathAsString($newPath->toOne());
                                                                 ^VariableExpression(name = if($paramMap->get($newPathString)->isEmpty(), |$newPathString, | $paramMap->get($newPathString)->toOne()),
                                                                                      genericType = ^GenericType(rawType = Any),
                                                                                      multiplicity = $fe.func->functionReturnMultiplicity())->wrapVariableExpressionInCast($fe.func->cast(@AbstractProperty));,
                                                                 |^$fe(parametersValues = $res.first->cast(@ValueSpecification)));

                                          let updatedPathList = if($newPropertiesInCurrentPath->isEmpty() || (($fe.parametersValues->size() == 1) && ($fe.parametersValues->at(0)->instanceOf(SimpleFunctionExpression)) && ($fe.parametersValues->at(0)->cast(@SimpleFunctionExpression).func->instanceOf(AbstractProperty))),
                                                                   | ^List>(values = $res.second.values),
                                                                   | ^List>(values = $res.second.values->concatenate($newPath)));
                                          pair($updatedFe, $updatedPathList);,
               i:InstanceValue[1] | let updatedValues = $i.values->map( v |
                                                                       $v->match([ a : ValueSpecification[1]  | $a->findAndReplacePropertyPathsInValueSpecification($propertiesInCurrentPath, $paramMap),
                                                                                   k : KeyExpression[1]       | let keyExpressionPair = $k.expression->findAndReplacePropertyPathsInValueSpecification($propertiesInCurrentPath, $paramMap)->toOne();
                                                                                                                pair(^KeyExpression(key = $k.key, expression = $keyExpressionPair.first->cast(@ValueSpecification)), $keyExpressionPair.second);,
                                                                                   l : LambdaFunction[1] | let results = $l.expressionSequence->map(e | $e->findAndReplacePropertyPathsInValueSpecification($propertiesInCurrentPath, $paramMap));
                                                                                                                pair(^$l(expressionSequence = $results.first->cast(@ValueSpecification)->toOneMany()), ^List>(values = $results.second.values));,
                                                                                   a : Any[1]                 | pair($a, ^List>(values = []))]));
                                    pair(^$i(values = $updatedValues.first), ^List>(values = $updatedValues.second.values));,
               ve:VariableExpression[1] | let updatedName = if($paramMap->get($ve.name)->isEmpty(),
                                                               | $ve.name,
                                                               | $paramMap->get($ve.name)->toOne());
                                          pair(^$ve(name = $updatedName), ^List>(values = []));
             ]
          );

}

function meta::external::store::service::executionPlan::generation::wrapVariableExpressionInCast(v:VariableExpression[1], prop:AbstractProperty[1]):SimpleFunctionExpression[1]
{
   ^SimpleFunctionExpression
   (
      func = cast_Any_m__T_1__T_m_,
      functionName = 'cast',
      multiplicity = $prop->functionReturnMultiplicity(),
      genericType = $prop->functionReturnType(),
      importGroup = system::imports::coreImport,
      parametersValues =
         [
            $v,
            ^InstanceValue
            (
               multiplicity = $prop->functionReturnMultiplicity(),
               genericType = $prop->functionReturnType()
            )
         ]
   );
}

function meta::external::store::service::executionPlan::generation::pathAsString(p:Path[1]):String[1]
{
   if($p.start.rawType->toOne()->instanceOf(MappingClass),
      |$p.start.rawType->cast(@MappingClass).generalizations.general.rawType.name->toOne(),
      |$p.start.rawType.name->toOne()) + '_' + $p.path->map(prop|$prop->cast(@PropertyPathElement).property.name)->joinStrings('_');
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy