feat(compiler): Generate empty calls to responseHandlerSrv [LNG-286] (#979)

* Add noEmptyResponse

* Fix tests
This commit is contained in:
InversionSpaces
2023-11-17 11:43:13 +01:00
committed by GitHub
parent 11c8970fd8
commit cee444862a
10 changed files with 85 additions and 48 deletions

View File

@ -37,6 +37,8 @@ type CommonArgs = {
targetType?: "ts" | "js" | "air" | undefined; targetType?: "ts" | "js" | "air" | undefined;
/** Compile aqua in tracing mode (for debugging purposes). Default: false */ /** Compile aqua in tracing mode (for debugging purposes). Default: false */
tracing?: boolean | undefined; tracing?: boolean | undefined;
/** Do not generate response call if there are no returned values */
noEmptyResponse?: boolean | undefined;
}; };
type CodeString = { type CodeString = {

View File

@ -7,6 +7,7 @@ function getConfig({
noXor = false, noXor = false,
targetType = "air", targetType = "air",
tracing = false, tracing = false,
noEmptyResponse = false,
}) { }) {
return new AquaConfig( return new AquaConfig(
logLevel, logLevel,
@ -19,6 +20,7 @@ function getConfig({
air: "air", air: "air",
}[targetType], }[targetType],
tracing, tracing,
noEmptyResponse,
); );
} }

View File

@ -4,9 +4,9 @@ import aqua.api.AquaAPIConfig
import aqua.api.TargetType.* import aqua.api.TargetType.*
import aqua.js.{FunctionDefJs, ServiceDefJs} import aqua.js.{FunctionDefJs, ServiceDefJs}
import aqua.model.transform.TransformConfig import aqua.model.transform.TransformConfig
import cats.data.Validated.{invalidNec, validNec} import cats.data.Validated.{invalidNec, validNec}
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec} import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
import scala.scalajs.js import scala.scalajs.js
import scala.scalajs.js.JSConverters.* import scala.scalajs.js.JSConverters.*
import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel} import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel}
@ -47,7 +47,9 @@ class AquaConfig(
@JSExport @JSExport
val targetType: js.UndefOr[String], val targetType: js.UndefOr[String],
@JSExport @JSExport
val tracing: js.UndefOr[Boolean] val tracing: js.UndefOr[Boolean],
@JSExport
val noEmptyResponse: js.UndefOr[Boolean]
) )
object AquaConfig { object AquaConfig {
@ -69,7 +71,8 @@ object AquaConfig {
constants = cjs.constants.map(_.toList).getOrElse(Nil), constants = cjs.constants.map(_.toList).getOrElse(Nil),
noXor = cjs.noXor.getOrElse(false), noXor = cjs.noXor.getOrElse(false),
noRelay = cjs.noRelay.getOrElse(false), noRelay = cjs.noRelay.getOrElse(false),
tracing = cjs.tracing.getOrElse(false) tracing = cjs.tracing.getOrElse(false),
noEmptyResponse = cjs.noEmptyResponse.getOrElse(false)
) )
} }
} }

View File

@ -10,12 +10,14 @@ case class AquaAPIConfig(
constants: List[String] = Nil, constants: List[String] = Nil,
noXor: Boolean = false, // TODO: Remove noXor: Boolean = false, // TODO: Remove
noRelay: Boolean = false, noRelay: Boolean = false,
tracing: Boolean = false tracing: Boolean = false,
noEmptyResponse: Boolean = false
) { ) {
def getTransformConfig: TransformConfig = { def getTransformConfig: TransformConfig = {
val config = TransformConfig( val config = TransformConfig(
tracing = Option.when(tracing)(TransformConfig.TracingConfig.default) tracing = Option.when(tracing)(TransformConfig.TracingConfig.default),
noEmptyResponse = noEmptyResponse
) )
if (noRelay) config.copy(relayVarName = None) if (noRelay) config.copy(relayVarName = None)

View File

@ -1,12 +1,15 @@
package aqua.compiler package aqua.compiler
import aqua.model.{CallModel, ForModel, FunctorModel, LiteralModel, ValueModel, VarModel} import aqua.model.AquaContext
import aqua.model.CallServiceModel
import aqua.model.FlattenModel
import aqua.model.transform.ModelBuilder import aqua.model.transform.ModelBuilder
import aqua.model.transform.TransformConfig
import aqua.model.transform.Transform import aqua.model.transform.Transform
import aqua.parser.ParserError import aqua.model.transform.TransformConfig
import aqua.model.{CallModel, ForModel, FunctorModel, LiteralModel, ValueModel, VarModel}
import aqua.parser.Ast import aqua.parser.Ast
import aqua.parser.Parser import aqua.parser.Parser
import aqua.parser.ParserError
import aqua.parser.lift.Span import aqua.parser.lift.Span
import aqua.parser.lift.Span.S import aqua.parser.lift.Span.S
import aqua.raw.ConstantRaw import aqua.raw.ConstantRaw
@ -18,15 +21,12 @@ import aqua.types.{ArrayType, CanonStreamType, LiteralType, ScalarType, StreamTy
import cats.Id import cats.Id
import cats.data.{Chain, NonEmptyChain, NonEmptyMap, Validated, ValidatedNec} import cats.data.{Chain, NonEmptyChain, NonEmptyMap, Validated, ValidatedNec}
import cats.instances.string.* import cats.instances.string.*
import cats.syntax.show.*
import cats.syntax.option.*
import cats.syntax.either.* import cats.syntax.either.*
import cats.syntax.option.*
import cats.syntax.show.*
import org.scalatest.Inside
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.Inside
import aqua.model.AquaContext
import aqua.model.FlattenModel
import aqua.model.CallServiceModel
class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside { class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
import ModelBuilder.* import ModelBuilder.*
@ -358,7 +358,8 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
join(VarModel(streamName, streamType), arg), join(VarModel(streamName, streamType), arg),
decrement decrement
) )
) ),
emptyRespCall(transformCfg, initPeer)
), ),
errorCall(transformCfg, 0, initPeer) errorCall(transformCfg, 0, initPeer)
) )

View File

@ -1,25 +1,25 @@
package aqua.model.transform package aqua.model.transform
import aqua.model.*
import aqua.model.inline.ArrowInliner import aqua.model.inline.ArrowInliner
import aqua.model.inline.state.InliningState import aqua.model.inline.state.InliningState
import aqua.model.transform.TransformConfig.TracingConfig
import aqua.model.transform.funcop.* import aqua.model.transform.funcop.*
import aqua.model.transform.pre.* import aqua.model.transform.pre.*
import aqua.model.transform.pre.{CallbackErrorHandler, ErrorHandler}
import aqua.model.transform.topology.Topology import aqua.model.transform.topology.Topology
import aqua.model.*
import aqua.raw.ops.RawTag import aqua.raw.ops.RawTag
import aqua.raw.value.VarRaw import aqua.raw.value.VarRaw
import aqua.res.* import aqua.res.*
import aqua.types.ScalarType import aqua.types.ScalarType
import aqua.model.transform.TransformConfig.TracingConfig
import aqua.model.transform.pre.{CallbackErrorHandler, ErrorHandler}
import cats.Eval import cats.Eval
import cats.data.Chain import cats.data.Chain
import cats.free.Cofree import cats.free.Cofree
import cats.instances.list.*
import cats.syntax.option.* import cats.syntax.option.*
import cats.syntax.show.* import cats.syntax.show.*
import cats.syntax.traverse.* import cats.syntax.traverse.*
import cats.instances.list.*
import scribe.Logging import scribe.Logging
// API for transforming RawTag to Res // API for transforming RawTag to Res
@ -90,7 +90,8 @@ object Transform extends Logging {
val resultsHandler: ResultsHandler = CallbackResultsHandler( val resultsHandler: ResultsHandler = CallbackResultsHandler(
callbackSrvId = conf.callbackSrvId, callbackSrvId = conf.callbackSrvId,
funcName = conf.respFuncName funcName = conf.respFuncName,
noEmptyResponse = conf.noEmptyResponse
) )
val errorHandler: ErrorHandler = CallbackErrorHandler( val errorHandler: ErrorHandler = CallbackErrorHandler(

View File

@ -1,19 +1,33 @@
package aqua.model.transform package aqua.model.transform
import aqua.model.{AquaContext, LiteralModel, ValueModel, VarModel} import aqua.model.{AquaContext, LiteralModel, ValueModel, VarModel}
import aqua.raw.{ConstantRaw, RawContext, RawPart}
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw} import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
import aqua.raw.{ConstantRaw, RawContext, RawPart}
import aqua.types.ScalarType import aqua.types.ScalarType
import cats.data.Chain import cats.data.Chain
import cats.kernel.Monoid import cats.kernel.Monoid
// TODO docs /**
* Configuration for function pre transformer
*
* @param getDataService - name of the service that provides arguments
* @param callbackService - name of the service that provides callbacks
* @param errorHandlingService - name of the service that handles errors
* @param errorFuncName - name of the function that handles errors (in errorHandlingService)
* @param respFuncName - name of the function that handles responses (in getDataService)
* @param noEmptyResponse - if true, do not generate response call if there is no return values
* @param relayVarName - name of the relay variable
* @param tracing - tracing configuration
* @param constants - list of constants
*/
case class TransformConfig( case class TransformConfig(
getDataService: String = "getDataSrv", getDataService: String = "getDataSrv",
callbackService: String = "callbackSrv", callbackService: String = "callbackSrv",
errorHandlingService: String = "errorHandlingSrv", errorHandlingService: String = "errorHandlingSrv",
errorFuncName: String = "error", errorFuncName: String = "error",
respFuncName: String = "response", respFuncName: String = "response",
noEmptyResponse: Boolean = false,
relayVarName: Option[String] = Some("-relay-"), relayVarName: Option[String] = Some("-relay-"),
tracing: Option[TransformConfig.TracingConfig] = None, tracing: Option[TransformConfig.TracingConfig] = None,
constants: List[ConstantRaw] = Nil constants: List[ConstantRaw] = Nil

View File

@ -1,8 +1,8 @@
package aqua.model.transform.pre package aqua.model.transform.pre
import aqua.types.Type
import aqua.raw.ops.{Call, CallArrowRawTag, RawTag} import aqua.raw.ops.{Call, CallArrowRawTag, RawTag}
import aqua.raw.value.{ValueRaw, VarRaw} import aqua.raw.value.{ValueRaw, VarRaw}
import aqua.types.Type
import cats.syntax.option.* import cats.syntax.option.*
@ -10,11 +10,14 @@ trait ResultsHandler {
def handleResults(results: List[(String, Type)]): Option[RawTag.Tree] def handleResults(results: List[(String, Type)]): Option[RawTag.Tree]
} }
case class CallbackResultsHandler(callbackSrvId: ValueRaw, funcName: String) case class CallbackResultsHandler(
extends ResultsHandler { callbackSrvId: ValueRaw,
funcName: String,
noEmptyResponse: Boolean
) extends ResultsHandler {
override def handleResults(results: List[(String, Type)]): Option[RawTag.Tree] = override def handleResults(results: List[(String, Type)]): Option[RawTag.Tree] =
if (results.isEmpty) none if (results.isEmpty && noEmptyResponse) none
else { else {
val resultVars = results.map(VarRaw.apply.tupled) val resultVars = results.map(VarRaw.apply.tupled)
val call = Call( val call = Call(

View File

@ -1,22 +1,22 @@
package aqua.model.transform package aqua.model.transform
import aqua.model.* import aqua.model.*
import aqua.model.FailModel
import aqua.model.IntoIndexModel
import aqua.model.OnModel
import aqua.model.inline.raw.StreamGateInliner
import aqua.raw.ops.Call import aqua.raw.ops.Call
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw} import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
import aqua.{model, res}
import aqua.res.{CallRes, CallServiceRes, MakeRes}
import aqua.types.{ArrayType, LiteralType, ScalarType}
import aqua.types.StreamType
import aqua.model.IntoIndexModel
import aqua.model.inline.raw.StreamGateInliner
import aqua.model.OnModel
import aqua.model.FailModel
import aqua.res.ResolvedOp import aqua.res.ResolvedOp
import aqua.res.{CallRes, CallServiceRes, MakeRes}
import aqua.types.StreamType
import aqua.types.{ArrayType, LiteralType, ScalarType}
import aqua.{model, res}
import scala.language.implicitConversions
import cats.data.Chain import cats.data.Chain
import cats.data.Chain.==: import cats.data.Chain.==:
import cats.syntax.option.* import cats.syntax.option.*
import scala.language.implicitConversions
object ModelBuilder { object ModelBuilder {
implicit def rawToValue(raw: ValueRaw): ValueModel = ValueModel.fromRaw(raw) implicit def rawToValue(raw: ValueRaw): ValueModel = ValueModel.fromRaw(raw)
@ -88,15 +88,24 @@ object ModelBuilder {
) )
.leaf .leaf
def respCall(bc: TransformConfig, value: ValueModel, on: ValueModel = initPeer) = def respCallImpl(
res config: TransformConfig,
.CallServiceRes( arguments: List[ValueModel],
ValueModel.fromRaw(bc.callbackSrvId), on: ValueModel = initPeer
bc.respFuncName, ) = res
CallRes(value :: Nil, None), .CallServiceRes(
on ValueModel.fromRaw(config.callbackSrvId),
) config.respFuncName,
.leaf CallRes(arguments, None),
on
)
.leaf
def respCall(config: TransformConfig, value: ValueModel, on: ValueModel = initPeer) =
respCallImpl(config, value :: Nil, on)
def emptyRespCall(config: TransformConfig, on: ValueModel = initPeer) =
respCallImpl(config, Nil)
def dataCall(bc: TransformConfig, name: String, on: ValueModel = initPeer) = def dataCall(bc: TransformConfig, name: String, on: ValueModel = initPeer) =
res res

View File

@ -4,15 +4,15 @@ import aqua.model.transform.ModelBuilder
import aqua.model.transform.{Transform, TransformConfig} import aqua.model.transform.{Transform, TransformConfig}
import aqua.model.{CallModel, FuncArrow, LiteralModel, VarModel} import aqua.model.{CallModel, FuncArrow, LiteralModel, VarModel}
import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp, OnTag, RawTag, SeqTag} import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp, OnTag, RawTag, SeqTag}
import aqua.raw.value.{LiteralRaw, VarRaw}
import aqua.types.{ArrowType, NilType, ProductType, ScalarType}
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw} import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
import aqua.raw.value.{LiteralRaw, VarRaw}
import aqua.res.{CallRes, CallServiceRes, MakeRes, SeqRes, XorRes} import aqua.res.{CallRes, CallServiceRes, MakeRes, SeqRes, XorRes}
import aqua.types.{ArrowType, NilType, ProductType, ScalarType}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import cats.data.Chain import cats.data.Chain
import cats.syntax.show.* import cats.syntax.show.*
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class TransformSpec extends AnyFlatSpec with Matchers { class TransformSpec extends AnyFlatSpec with Matchers {