Add errors to API results, fix AquaConfig, fix types (#620)

This commit is contained in:
Dima 2022-12-29 20:03:57 +03:00 committed by GitHub
parent 91169e2928
commit 269c68ad59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 237 additions and 114 deletions

View File

@ -2,6 +2,7 @@ import type {FunctionCallDef, ServiceDef} from "@fluencelabs/fluence/dist/intern
export class AquaConfig { export class AquaConfig {
constructor(logLevel: string, constants: string[], noXor: boolean, noRelay: boolean); constructor(logLevel: string, constants: string[], noXor: boolean, noRelay: boolean);
logLevel?: string logLevel?: string
constants?: string[] constants?: string[]
noXor?: boolean noXor?: boolean
@ -14,14 +15,36 @@ export class AquaFunction {
} }
export class CompilationResult { export class CompilationResult {
services: ServiceDef[] services: Record<string, ServiceDef>
functions: Record<string, AquaFunction> functions: Record<string, AquaFunction>
functionCall?: AquaFunction
errors: string[]
}
export class Input {
constructor(input: string);
input: string
}
export class Path {
constructor(path: string);
path: string
}
export class Call {
constructor(functionCall: string,
arguments: any,
input: Input | Path);
functionCall: string
arguments: any
input: Input | Path
} }
export class Compiler { export class Compiler {
compileRun(functionStr: string, arguments: any, path: string, imports: string[], config?: AquaConfig): Promise<AquaFunction>; compile(input: Input | Path | Call, imports: string[], config?: AquaConfig): Promise<CompilationResult>;
compile(path: string, imports: string[], config?: AquaConfig): Promise<CompilationResult>;
compileString(input: string, imports: string[], config?: AquaConfig): Promise<CompilationResult>;
} }
export var Aqua: Compiler; export var Aqua: Compiler;

View File

@ -1,6 +1,7 @@
package aqua.api package aqua.api
import aqua.ErrorRendering.showError import aqua.ErrorRendering.showError
import aqua.api.types.{AquaAPIConfig, AquaConfig, AquaFunction, CompilationResult, Input}
import aqua.backend.{AirFunction, Backend, Generated} import aqua.backend.{AirFunction, Backend, Generated}
import aqua.compiler.* import aqua.compiler.*
import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId} import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
@ -18,7 +19,7 @@ import aqua.{AquaIO, SpanParser}
import aqua.model.transform.{Transform, TransformConfig} import aqua.model.transform.{Transform, TransformConfig}
import aqua.backend.api.APIBackend import aqua.backend.api.APIBackend
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec} import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
import cats.data.Validated.{invalidNec, validNec, Invalid, Valid} import cats.data.Validated.{Invalid, Valid, invalidNec, validNec}
import cats.syntax.applicative.* import cats.syntax.applicative.*
import cats.syntax.apply.* import cats.syntax.apply.*
import cats.syntax.flatMap.* import cats.syntax.flatMap.*
@ -32,10 +33,11 @@ import scribe.Logging
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future import scala.concurrent.Future
import scala.scalajs.js.|
import scala.scalajs.js import scala.scalajs.js
import scala.scalajs.js.JSConverters.* import scala.scalajs.js.JSConverters.*
import scala.scalajs.js.annotation.* import scala.scalajs.js.annotation.*
import scala.scalajs.js.{undefined, UndefOr} import scala.scalajs.js.{UndefOr, undefined}
import aqua.js.{FunctionDefJs, ServiceDefJs, VarJson} import aqua.js.{FunctionDefJs, ServiceDefJs, VarJson}
import aqua.model.AquaContext import aqua.model.AquaContext
import aqua.raw.ops.CallArrowRawTag import aqua.raw.ops.CallArrowRawTag
@ -43,53 +45,6 @@ import aqua.raw.value.{LiteralRaw, VarRaw}
import aqua.res.AquaRes import aqua.res.AquaRes
import cats.Applicative import cats.Applicative
@JSExportTopLevel("AquaFunction")
case class AquaFunction(
@JSExport
funcDef: FunctionDefJs,
@JSExport
script: String
)
case class AquaAPIConfig(
logLevel: String = "info",
constants: List[String] = Nil,
noXor: Boolean = false,
noRelay: Boolean = false
)
object AquaAPIConfig {
def fromJS(cjs: AquaConfig): AquaAPIConfig = {
AquaAPIConfig(
cjs.logLevel.getOrElse("info"),
cjs.constants.map(_.toList).getOrElse(Nil),
cjs.noXor.getOrElse(false),
cjs.noRelay.getOrElse(false)
)
}
}
@JSExportTopLevel("AquaConfig")
case class AquaConfig(
@JSExport
logLevel: js.UndefOr[String],
@JSExport
constants: js.UndefOr[js.Array[String]],
@JSExport
noXor: js.UndefOr[Boolean],
@JSExport
noRelay: js.UndefOr[Boolean]
)
@JSExportTopLevel("CompilationResult")
case class CompilationResult(
@JSExport
services: js.Array[ServiceDefJs],
@JSExport
functions: js.Dictionary[AquaFunction]
)
@JSExportTopLevel("Aqua") @JSExportTopLevel("Aqua")
object AquaAPI extends App with Logging { object AquaAPI extends App with Logging {
@ -102,86 +57,121 @@ object AquaAPI extends App with Logging {
} }
@JSExport @JSExport
def compileRun( def compile(
input: types.Input | types.Path | types.Call,
imports: js.Array[String],
aquaConfigJS: js.UndefOr[AquaConfig]
) = {
val aquaConfig: AquaAPIConfig =
aquaConfigJS.toOption.map(cjs => AquaAPIConfig.fromJS(cjs)).getOrElse(AquaAPIConfig())
val importsList = imports.toList
input match {
case i: types.Input => compileString(i.input, importsList, aquaConfig)
case p: types.Path => compilePath(p.path, importsList, aquaConfig)
case c: types.Call =>
val path = c.input match {
case i: types.Input => i.input
case p: types.Path => p.path
}
compileCall(c.functionCall, c.arguments, path, importsList, aquaConfig)
}
}
def compileCall(
functionStr: String, functionStr: String,
arguments: js.Dynamic, arguments: js.Dynamic,
pathStr: String, pathStr: String,
imports: js.Array[String], imports: List[String],
aquaConfigJS: js.UndefOr[AquaConfig] aquaConfig: AquaAPIConfig
): js.Promise[AquaFunction] = { ): js.Promise[CompilationResult] = {
implicit val aio: AquaIO[IO] = new AquaFilesIO[IO] implicit val aio: AquaIO[IO] = new AquaFilesIO[IO]
val aquaConfig: AquaAPIConfig = (
aquaConfigJS.toOption.map(cjs => AquaAPIConfig.fromJS(cjs)).getOrElse(AquaAPIConfig()) LogLevels.levelFromString(aquaConfig.logLevel),
LogFormatter.initLogger(Some(LogLevels.levelFromString(aquaConfig.logLevel).toOption.get)) Constants.parse(aquaConfig.constants)
val transformConfig = TransformConfig() ).mapN { (level, constants) =>
new FuncCompiler[IO]( val transformConfig = aquaConfig.getTransformConfig.copy(constants = constants)
Some(RelativePath(Path(pathStr))),
imports.toList.map(Path.apply), LogFormatter.initLogger(Some(level))
transformConfig
).compile().map { contextV => new FuncCompiler[IO](
contextV.andThen { context => Some(RelativePath(Path(pathStr))),
CliFunc.fromString(functionStr).leftMap(errs => NonEmptyChain.fromNonEmptyList(errs)).andThen { cliFunc => imports.toList.map(Path.apply),
FuncCompiler.findFunction(context, cliFunc).andThen { arrow => transformConfig
VarJson.checkDataGetServices(cliFunc.args, Some(arguments)).andThen { ).compile()
case (argsWithTypes, _) => .map { contextV =>
val func = cliFunc.copy(args = argsWithTypes) contextV.andThen { context =>
val preparer = new RunPreparer( CliFunc
func, .fromString(functionStr)
arrow, .leftMap(errs => NonEmptyChain.fromNonEmptyList(errs))
transformConfig .andThen { cliFunc =>
) FuncCompiler.findFunction(context, cliFunc).andThen { arrow =>
preparer.prepare().map { ci => VarJson.checkDataGetServices(cliFunc.args, Some(arguments)).andThen {
AquaFunction(FunctionDefJs(ci.definitions), ci.air) case (argsWithTypes, _) =>
val func = cliFunc.copy(args = argsWithTypes)
val preparer = new RunPreparer(
func,
arrow,
transformConfig
)
preparer.prepare().map { ci =>
AquaFunction(FunctionDefJs(ci.definitions), ci.air)
}
}
} }
} }
} }
} }
} .flatMap {
}.flatMap { case Valid(result) => IO.pure(CompilationResult.result(js.Dictionary(), js.Dictionary(), Some(result)))
case Valid(result) => IO.pure(result) case Invalid(err) =>
case Invalid(err) => val errs = err.map(_.show).distinct.toChain.toList
err.map(_.show).distinct.map(OutputPrinter.errorF[IO]).sequence IO.pure(CompilationResult.errs(errs))
IO.raiseError[AquaFunction](new Error("Compilation failed.")) }
}.unsafeToFuture().toJSPromise .unsafeToFuture()
.toJSPromise
} match {
case Valid(pr) => pr
case Invalid(err) => js.Promise.resolve(CompilationResult.errs(err.toList))
}
} }
@JSExport def compilePath(
def compile(
pathStr: String, pathStr: String,
imports: js.Array[String], imports: List[String],
aquaConfigJS: js.UndefOr[AquaConfig] aquaConfig: AquaAPIConfig
): js.Promise[CompilationResult] = { ): js.Promise[CompilationResult] = {
implicit val aio: AquaIO[IO] = new AquaFilesIO[IO] implicit val aio: AquaIO[IO] = new AquaFilesIO[IO]
val path = Path(pathStr) val path = Path(pathStr)
val sources = new AquaFileSources[IO](path, imports.toList.map(Path.apply)) val sources = new AquaFileSources[IO](path, imports.map(Path.apply))
compileRaw(aquaConfigJS, sources) compileRaw(aquaConfig, sources)
} }
@JSExport
def compileString( def compileString(
input: String, input: String,
imports: js.Array[String], imports: List[String],
aquaConfigJS: js.UndefOr[AquaConfig] aquaConfig: AquaAPIConfig
): js.Promise[CompilationResult] = { ): js.Promise[CompilationResult] = {
implicit val aio: AquaIO[IO] = new AquaFilesIO[IO] implicit val aio: AquaIO[IO] = new AquaFilesIO[IO]
val path = Path("") val path = Path("")
val strSources: AquaFileSources[IO] = val strSources: AquaFileSources[IO] =
new AquaFileSources[IO](path, imports.toList.map(Path.apply)) { new AquaFileSources[IO](path, imports.map(Path.apply)) {
override def sources: IO[ValidatedNec[AquaFileError, Chain[(FileModuleId, String)]]] = { override def sources: IO[ValidatedNec[AquaFileError, Chain[(FileModuleId, String)]]] = {
IO.pure(Valid(Chain.one((FileModuleId(path), input)))) IO.pure(Valid(Chain.one((FileModuleId(path), input))))
} }
} }
compileRaw(aquaConfigJS, strSources) compileRaw(aquaConfig, strSources)
} }
def compileRaw( def compileRaw(
aquaConfigJS: js.UndefOr[AquaConfig], aquaConfig: AquaAPIConfig,
sources: AquaSources[IO, AquaFileError, FileModuleId] sources: AquaSources[IO, AquaFileError, FileModuleId]
): js.Promise[CompilationResult] = { ): js.Promise[CompilationResult] = {
val aquaConfig =
aquaConfigJS.toOption.map(cjs => AquaAPIConfig.fromJS(cjs)).getOrElse(AquaAPIConfig())
( (
LogLevels.levelFromString(aquaConfig.logLevel), LogLevels.levelFromString(aquaConfig.logLevel),
@ -191,7 +181,7 @@ object AquaAPI extends App with Logging {
LogFormatter.initLogger(Some(level)) LogFormatter.initLogger(Some(level))
val config = AquaCompilerConf(constants) val config = AquaCompilerConf(constants)
val transformConfig = TransformConfig() val transformConfig = aquaConfig.getTransformConfig
val proc = for { val proc = for {
res <- CompilerAPI res <- CompilerAPI
@ -214,15 +204,21 @@ object AquaAPI extends App with Logging {
jsResult <- res match { jsResult <- res match {
case Valid(compiled) => case Valid(compiled) =>
val allGenerated: List[Generated] = compiled.toList.flatMap(_.compiled) val allGenerated: List[Generated] = compiled.toList.flatMap(_.compiled)
val serviceDefs = allGenerated.flatMap(_.services).map(s => ServiceDefJs(s)).toJSArray val serviceDefs = allGenerated.flatMap(_.services).map(s => s.name -> ServiceDefJs(s))
val functions = allGenerated.flatMap( val functions = allGenerated.flatMap(
_.air.map(as => (as.name, AquaFunction(FunctionDefJs(as.funcDef), as.air))) _.air.map(as => (as.name, AquaFunction(FunctionDefJs(as.funcDef), as.air)))
) )
IO.pure(CompilationResult(serviceDefs, js.Dictionary.apply(functions: _*))) IO.pure(
CompilationResult.result(
js.Dictionary.apply(serviceDefs: _*),
js.Dictionary.apply(functions: _*),
None
)
)
case Invalid(errChain) => case Invalid(errChain) =>
errChain.map(_.show).distinct.map(OutputPrinter.errorF[IO]).sequence val errors = errChain.map(_.show).distinct.toChain.toList
IO.raiseError[CompilationResult](new Error("Compilation failed.")) IO.pure[CompilationResult](CompilationResult.errs(errors))
} }
} yield { } yield {
jsResult jsResult
@ -231,7 +227,7 @@ object AquaAPI extends App with Logging {
proc.unsafeToFuture().toJSPromise proc.unsafeToFuture().toJSPromise
} match { } match {
case Valid(pr) => pr case Valid(pr) => pr
case Invalid(err) => js.Promise.reject(err) case Invalid(err) => js.Promise.resolve(CompilationResult.errs(err.toList))
} }
} }
} }

View File

@ -0,0 +1,103 @@
package aqua.api.types
import aqua.api.*
import aqua.js.{FunctionDefJs, ServiceDefJs}
import aqua.model.transform.TransformConfig
import scala.scalajs.js
import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel}
import scala.scalajs.js.|
import scala.scalajs.js.JSConverters.*
@JSExportTopLevel("AquaFunction")
case class AquaFunction(
@JSExport
funcDef: FunctionDefJs,
@JSExport
script: String
)
@JSExportTopLevel("Input")
class Input(
@JSExport
val input: String
)
@JSExportTopLevel("Path")
class Path(
@JSExport
val path: String
)
@JSExportTopLevel("Call")
class Call(
@JSExport
val functionCall: String,
@JSExport
val arguments: js.Dynamic,
@JSExport
val input: Path | Input
)
@JSExportTopLevel("AquaConfig")
class AquaConfig(
@JSExport
val logLevel: js.UndefOr[String],
@JSExport
val constants: js.UndefOr[js.Array[String]],
@JSExport
val noXor: js.UndefOr[Boolean],
@JSExport
val noRelay: js.UndefOr[Boolean]
)
@JSExportTopLevel("CompilationResult")
class CompilationResult(
@JSExport
val services: js.Dictionary[ServiceDefJs],
@JSExport
val functions: js.Dictionary[AquaFunction],
@JSExport
val functionCall: js.UndefOr[AquaFunction],
@JSExport
val errors: js.Array[String]
)
object CompilationResult {
def result(
services: js.Dictionary[ServiceDefJs],
functions: js.Dictionary[AquaFunction],
call: Option[AquaFunction]
): CompilationResult =
new CompilationResult(services, functions, call.orNull, js.Array())
def errs(
errors: List[String]
): CompilationResult =
CompilationResult(js.Dictionary(), js.Dictionary(), null, errors.toJSArray)
}
case class AquaAPIConfig(
logLevel: String = "info",
constants: List[String] = Nil,
noXor: Boolean = false,
noRelay: Boolean = false
) {
def getTransformConfig: TransformConfig =
if (noRelay) TransformConfig(relayVarName = None, wrapWithXor = !noXor)
else TransformConfig(wrapWithXor = !noXor)
}
object AquaAPIConfig {
def fromJS(cjs: AquaConfig): AquaAPIConfig = {
AquaAPIConfig(
cjs.logLevel.getOrElse("info"),
cjs.constants.map(_.toList).getOrElse(Nil),
cjs.noXor.getOrElse(false),
cjs.noRelay.getOrElse(false)
)
}
}

View File

@ -17,7 +17,7 @@ object APIBackend extends Backend {
srv.members.map { case (n, a) => (n, ArrowTypeDef(a)) } srv.members.map { case (n, a) => (n, ArrowTypeDef(a)) }
) )
ServiceDef(srv.defaultId.map(s => s.replace("\"", "")), functions) ServiceDef(srv.defaultId.map(s => s.replace("\"", "")), functions, srv.name)
}.toList }.toList
Generated("", "", airGenerated.flatMap(_.air).toList, services) :: Nil Generated("", "", airGenerated.flatMap(_.air).toList, services) :: Nil

View File

@ -68,7 +68,7 @@ object TypeDefinition {
) )
} }
implicit val encodeServiceDefType: Encoder[ServiceDef] = { case ServiceDef(sId, functions) => implicit val encodeServiceDefType: Encoder[ServiceDef] = { case ServiceDef(sId, functions, name) =>
Json.obj( Json.obj(
("defaultServiceId", sId.asJson), ("defaultServiceId", sId.asJson),
("functions", encodeProdDefType(functions)) ("functions", encodeProdDefType(functions))
@ -195,7 +195,7 @@ case class NamesConfig(
) )
// Describes service // Describes service
case class ServiceDef(defaultServiceId: Option[String], functions: LabeledProductTypeDef) case class ServiceDef(defaultServiceId: Option[String], functions: LabeledProductTypeDef, name: String)
// Describes top-level function // Describes top-level function
case class FunctionDef( case class FunctionDef(

View File

@ -22,7 +22,7 @@ case class OutputService(srv: ServiceRes, types: Types) {
srv.members.map { case (n, a) => (n, ArrowTypeDef(a)) } srv.members.map { case (n, a) => (n, ArrowTypeDef(a)) }
) )
val serviceDef = ServiceDef(srv.defaultId.map(s => s.replace("\"", "")), functions) val serviceDef = ServiceDef(srv.defaultId.map(s => s.replace("\"", "")), functions, srv.name)
s""" s"""
|${serviceTypes.generate} |${serviceTypes.generate}

View File

@ -17,7 +17,7 @@ val scribeV = "3.7.1"
name := "aqua-hll" name := "aqua-hll"
val commons = Seq( val commons = Seq(
baseAquaVersion := "0.9.0", baseAquaVersion := "0.9.1",
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"), version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
scalaVersion := dottyVersion, scalaVersion := dottyVersion,
libraryDependencies ++= Seq( libraryDependencies ++= Seq(

View File

@ -27,7 +27,8 @@ class Service(serviceId: String, functions: NonEmptyList[AquaFunction]) extends
handlers.toList, handlers.toList,
ServiceDef( ServiceDef(
None, None,
defs defs,
""
) )
) )
} }

View File

@ -49,7 +49,7 @@ case class Plugin(name: String, functions: List[Function]) {
peer, peer,
name, name,
handlers, handlers,
ServiceDef(Some(name), LabeledProductTypeDef(funcTypes)) ServiceDef(Some(name), LabeledProductTypeDef(funcTypes), "")
) )
} }
} }