/
EagerServiceWithStaticParameters.scala
141 lines (117 loc) · 5.56 KB
/
EagerServiceWithStaticParameters.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package pl.touk.nussknacker.engine.util.service
import cats.data.Validated.Valid
import cats.data.ValidatedNel
import pl.touk.nussknacker.engine.api.context.transformation.{
DefinedSingleParameter,
NodeDependencyValue,
SingleInputDynamicComponent,
WithStaticParameters
}
import pl.touk.nussknacker.engine.api.context.{ProcessCompilationError, ValidationContext}
import pl.touk.nussknacker.engine.api.definition.{
NodeDependency,
OutputVariableNameDependency,
Parameter,
TypedNodeDependency
}
import pl.touk.nussknacker.engine.api.process.ComponentUseCase
import pl.touk.nussknacker.engine.api.test.InvocationCollectors
import pl.touk.nussknacker.engine.api.typed.typing.{Typed, TypingResult, Unknown}
import pl.touk.nussknacker.engine.api._
import pl.touk.nussknacker.engine.api.NodeId
import pl.touk.nussknacker.engine.api.parameter.ParameterName
import scala.concurrent.{ExecutionContext, Future}
import scala.runtime.BoxedUnit
/*
This is helper trait for creating Service which has parameter definitions fixed in designer (i.e. no parameters depending on each other)
but parameter definitions which are not fixed/known at compile time. Good example are services which take parameter
list from external source (e.g. configuration, OpenAPI definition, database).
For dynamic parameters use SingleInputDynamicComponent, for parameters known at compile time - use @MethodToInvoke
*/
trait EagerServiceWithStaticParameters
extends EagerService
with SingleInputDynamicComponent[ServiceInvoker]
with WithStaticParameters {
override type State = TypingResult
private val metaData = TypedNodeDependency[MetaData]
override def staticParameters: List[Parameter] = parameters
def parameters: List[Parameter]
def hasOutput: Boolean
def createServiceInvoker(
eagerParameters: Map[ParameterName, Any],
lazyParameters: Map[ParameterName, LazyParameter[AnyRef]],
typingResult: TypingResult,
metaData: MetaData
): ServiceInvoker
def returnType(
validationContext: ValidationContext,
parameters: Map[ParameterName, DefinedSingleParameter]
): ValidatedNel[ProcessCompilationError, TypingResult]
override def contextTransformation(context: ValidationContext, dependencies: List[NodeDependencyValue])(
implicit nodeId: NodeId
): ContextTransformationDefinition = {
case TransformationStep(Nil, _) if parameters.nonEmpty => NextParameters(parameters)
case TransformationStep(list, _) if hasOutput =>
val output = returnType(context, list.toMap)
FinalResults.forValidation(context, output.swap.map(_.toList).getOrElse(Nil))(
_.withVariable(OutputVariableNameDependency.extract(dependencies), output.getOrElse(Unknown), None)
)
case TransformationStep(_, _) => FinalResults(context, Nil)
}
override def nodeDependencies: List[NodeDependency] =
if (hasOutput) List(OutputVariableNameDependency, metaData) else List(metaData)
override final def implementation(
params: Params,
dependencies: List[NodeDependencyValue],
finalState: Option[TypingResult]
): ServiceInvoker =
createServiceInvoker(
params.nameToValueMap.filterNot { case (_, param) => param.isInstanceOf[LazyParameter[_]] },
params.nameToValueMap.collect { case (name, param: LazyParameter[AnyRef]) => (name, param) },
finalState.getOrElse(Unknown),
metaData.extract(dependencies)
)
}
/*
Like in EagerServiceWithStaticParameters, but for simpler case, when return type is also known in designer (i.e. it does not depend on parameters)
*/
trait EagerServiceWithStaticParametersAndReturnType extends EagerServiceWithStaticParameters {
def returnType: TypingResult
// TODO: This method should be removed - instead, developers should deliver it's own ServiceInvoker to avoid
// mixing implementation logic with definition logic. Before that we should fix EagerService Lifecycle handling.
// See notice next to EagerService
def invoke(eagerParameters: Map[ParameterName, Any])(
implicit ec: ExecutionContext,
collector: InvocationCollectors.ServiceInvocationCollector,
contextId: ContextId,
metaData: MetaData,
componentUseCase: ComponentUseCase
): Future[Any]
override final def createServiceInvoker(
eagerParameters: Map[ParameterName, Any],
lazyParameters: Map[ParameterName, LazyParameter[AnyRef]],
typingResult: TypingResult,
metaData: MetaData
): ServiceInvoker = new ServiceInvokerImplementation(eagerParameters, lazyParameters, metaData)
override def hasOutput: Boolean = !List(Typed[Void], Typed[Unit], Typed[BoxedUnit]).contains(returnType)
override def returnType(
validationContext: ValidationContext,
parameters: Map[ParameterName, DefinedSingleParameter]
): ValidatedNel[ProcessCompilationError, TypingResult] = Valid(returnType)
private class ServiceInvokerImplementation(
eagerParameters: Map[ParameterName, Any],
lazyParameters: Map[ParameterName, LazyParameter[AnyRef]],
metaData: MetaData
) extends ServiceInvoker {
override def invoke(context: Context)(
implicit ec: ExecutionContext,
collector: InvocationCollectors.ServiceInvocationCollector,
componentUseCase: ComponentUseCase
): Future[Any] = {
implicit val contextId: ContextId = ContextId(context.id)
implicit val metaImplicit: MetaData = metaData
val evaluatedLazyParameters = lazyParameters.map { case (name, value) => (name, value.evaluate(context)) }
EagerServiceWithStaticParametersAndReturnType.this.invoke(eagerParameters ++ evaluatedLazyParameters)
}
}
}