diff --git a/daml-assistant/integration-tests/src/DA/Daml/Assistant/IntegrationTests.hs b/daml-assistant/integration-tests/src/DA/Daml/Assistant/IntegrationTests.hs index 40e6d3e485..e9dc59c7e6 100644 --- a/daml-assistant/integration-tests/src/DA/Daml/Assistant/IntegrationTests.hs +++ b/daml-assistant/integration-tests/src/DA/Daml/Assistant/IntegrationTests.hs @@ -192,6 +192,41 @@ packagingTests = testGroup "packaging" , "setup' = setup" ] withCurrentDirectory (tmpDir "proj") $ callCommandSilent "daml build" + , testCase "DAML Script --input-file and --output-file" $ withTempDir $ \projDir -> do + writeFileUTF8 (projDir "daml.yaml") $ unlines + [ "sdk-version: " <> sdkVersion + , "name: proj" + , "version: 0.0.1" + , "source: ." + , "dependencies: [daml-prim, daml-stdlib, daml-script]" + ] + writeFileUTF8 (projDir "Main.daml") $ unlines + [ "module Main where" + , "import Daml.Script" + , "test : Int -> Script (Int, Int)" + , "test x = pure (x, x + 1)" + ] + withCurrentDirectory projDir $ do + callCommandSilent "daml build -o script.dar" + writeFileUTF8 (projDir "input.json") "0" + p :: Int <- fromIntegral <$> getFreePort + withDevNull $ \devNull -> + withCreateProcess (shell $ unwords ["daml sandbox --port " <> show p]) { std_out = UseHandle devNull } $ \_ _ _ _ -> do + waitForConnectionOnPort (threadDelay 100000) p + callCommand $ unwords + [ "daml script" + ,"--wall-clock-time" + , "--dar script.dar --script-name Main:test" + , "--input-file input.json --output-file output.json" + , "--ledger-host localhost --ledger-port " <> show p + ] + contents <- readFileUTF8 (projDir "output.json") + lines contents @?= + [ "{" + , " \"_1\": 0," + , " \"_2\": 1" + , "}" + ] , testCase "Run init-script" $ withTempDir $ \tmpDir -> do let projDir = tmpDir "init-script-example" createDirectoryIfMissing True (projDir "daml") diff --git a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/RunnerConfig.scala b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/RunnerConfig.scala index de9d7cbdb0..513d6ad629 100644 --- a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/RunnerConfig.scala +++ b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/RunnerConfig.scala @@ -20,6 +20,7 @@ case class RunnerConfig( timeProviderType: TimeProviderType, commandTtl: Duration, inputFile: Option[File], + outputFile: Option[File], accessTokenFile: Option[Path], tlsConfig: Option[TlsConfiguration], jsonApi: Boolean, @@ -83,6 +84,12 @@ object RunnerConfig { } .text("Path to a file containing the input value for the script in JSON format.") + opt[File]("output-file") + .action { (t, c) => + c.copy(outputFile = Some(t)) + } + .text("Path to a file where the result of the script will be written to in JSON format.") + opt[String]("access-token-file") .action { (f, c) => c.copy(accessTokenFile = Some(Paths.get(f))) @@ -159,6 +166,7 @@ object RunnerConfig { timeProviderType = null, commandTtl = Duration.ofSeconds(30L), inputFile = None, + outputFile = None, accessTokenFile = None, tlsConfig = None, jsonApi = false, diff --git a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/RunnerMain.scala b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/RunnerMain.scala index 1f738b7ffb..b22fb5ef6a 100644 --- a/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/RunnerMain.scala +++ b/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/RunnerMain.scala @@ -9,6 +9,7 @@ import akka.stream._ import java.nio.file.Files import java.time.Instant import java.util.stream.Collectors +import scala.collection.JavaConverters._ import scala.concurrent.{Await, ExecutionContext, Future} import scala.concurrent.duration.Duration import scala.io.Source @@ -116,7 +117,14 @@ object RunnerMain { ) Runner.connect(participantParams, clientConfig) } - _ <- Runner.run(dar, scriptId, inputValue, clients, applicationId, timeProvider) + result <- Runner.run(dar, scriptId, inputValue, clients, applicationId, timeProvider) + _ <- Future { + config.outputFile.foreach { outputFile => + val jsVal = LfValueCodec.apiValueToJsValue( + result.toValue.assertNoRelCid(rcoid => s"Unexpected relative contract id $rcoid")) + Files.write(outputFile.toPath, Seq(jsVal.prettyPrint).asJava) + } + } } yield () flow.onComplete(_ => diff --git a/docs/source/daml-script/index.rst b/docs/source/daml-script/index.rst index 27d187ffd9..c2af4cabb1 100644 --- a/docs/source/daml-script/index.rst +++ b/docs/source/daml-script/index.rst @@ -191,6 +191,11 @@ We can then initialize our ledger passing in the json file via ``--input-file``. If you open Navigator, you can now see the contracts that have been created. +While we will not use it here, there is also an ``--output-file`` +option that you can use to write the result of a script to a file +using the DAML-LF JSON encoding. This is particularly useful if you need to consume +the result from another program. + .. _script-ledger-initialization: Using DAML Script for Ledger Initialization