Make DAML REPL work without a ledger (#6838)

This PR makes the ``--ledger-host`` and ``--ledger-port`` parameters
optional so DAML REPL works without a ledger which is useful now that
we have better. support for pure expressions. This just piggybacks on
DAML Script’s multiparticipant support so there are no significant
changes on the service.

Docs are updated and we have a testcase.

Side note: What is still missing is `let x = …` in DAML REPL. I’ll
tackle that in a separate PR.

changelog_begin
changelog_end
This commit is contained in:
Moritz Kiefer 2020-07-23 12:23:24 +02:00 committed by GitHub
parent 4b1438276c
commit 9881ff5231
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 79 additions and 24 deletions

View File

@ -264,8 +264,10 @@ cmdRepl numProcessors =
-- This is useful for tests and `bazel run`.
<*> many (strArgument (help "DAR to load in the repl" <> metavar "DAR"))
<*> many packageImport
<*> strOption (long "ledger-host" <> help "Host of the ledger API")
<*> strOption (long "ledger-port" <> help "Port of the ledger API")
<*> optional
((,) <$> strOption (long "ledger-host" <> help "Host of the ledger API")
<*> strOption (long "ledger-port" <> help "Port of the ledger API")
)
<*> accessTokenFileFlag
<*> sslConfig
<*> optional
@ -599,13 +601,13 @@ execRepl
:: ProjectOpts
-> Options
-> FilePath -> [FilePath] -> [(LF.PackageName, Maybe LF.PackageVersion)]
-> String -> String
-> Maybe (String, String)
-> Maybe FilePath
-> Maybe ReplClient.ClientSSLConfig
-> Maybe ReplClient.MaxInboundMessageSize
-> ReplClient.ReplTimeMode
-> Command
execRepl projectOpts opts scriptDar dars importPkgs ledgerHost ledgerPort mbAuthToken mbSslConf mbMaxInboundMessageSize timeMode = Command Repl (Just projectOpts) effect
execRepl projectOpts opts scriptDar dars importPkgs mbLedgerConfig mbAuthToken mbSslConf mbMaxInboundMessageSize timeMode = Command Repl (Just projectOpts) effect
where effect = do
-- We change directory so make this absolute
dars <- mapM makeAbsolute dars
@ -616,7 +618,7 @@ execRepl projectOpts opts scriptDar dars importPkgs ledgerHost ledgerPort mbAuth
logger <- getLogger opts "repl"
runfilesDir <- locateRunfiles (mainWorkspace </> "compiler/repl-service/server")
let jar = runfilesDir </> "repl-service.jar"
ReplClient.withReplClient (ReplClient.Options jar ledgerHost ledgerPort mbAuthToken mbSslConf mbMaxInboundMessageSize timeMode Inherit) $ \replHandle _stdout _ph ->
ReplClient.withReplClient (ReplClient.Options jar mbLedgerConfig mbAuthToken mbSslConf mbMaxInboundMessageSize timeMode Inherit) $ \replHandle _stdout _ph ->
withTempDir $ \dir ->
withCurrentDirectory dir $ do
sdkVer <- fromMaybe SdkVersion.sdkVersion <$> lookupEnv sdkVersionEnvVar

View File

@ -64,6 +64,7 @@ main = do
, mbLedgerId = Just testLedgerId
} $ \getSandboxPort ->
importTests damlc scriptDar testDar getSandboxPort
, noLedgerTests damlc scriptDar
]
withTokenFile :: (IO FilePath -> TestTree) -> TestTree
@ -168,6 +169,28 @@ noPackageTests damlc scriptDar getSandboxPort = testGroup "static-time"
, scriptDar
]
noLedgerTests :: FilePath -> FilePath -> TestTree
noLedgerTests damlc scriptDar = testGroup "no ledger"
[ testCase "no ledger" $ do
out <- readCreateProcess cp $ unlines
[ "1 + 1"
, "listKnownParties"
, "2 + 2"
]
out @?= unlines
[ "daml> 2"
, "daml> java.lang.RuntimeException: No default participant"
, "daml> 4"
, "daml> Goodbye."
]
]
where
cp = proc damlc
[ "repl"
, "--script-lib"
, scriptDar
]
testSetTime
:: FilePath
-> FilePath

View File

@ -63,7 +63,7 @@ main = do
withTempFile $ \portFile ->
withBinaryFile nullDevice WriteMode $ \devNull ->
bracket (createSandbox portFile devNull defaultSandboxConf { dars = testDars }) destroySandbox $ \SandboxResource{sandboxPort} ->
ReplClient.withReplClient (ReplClient.Options replJar "localhost" (show sandboxPort) Nothing Nothing Nothing ReplClient.ReplWallClock CreatePipe) $ \replHandle mbServiceOut processHandle ->
ReplClient.withReplClient (ReplClient.Options replJar (Just ("localhost", show sandboxPort)) Nothing Nothing Nothing ReplClient.ReplWallClock CreatePipe) $ \replHandle mbServiceOut processHandle ->
-- TODO We could share some of this setup with the actual repl code in damlc.
withTempDir $ \dir ->
withCurrentDirectory dir $ do

View File

@ -43,8 +43,7 @@ data ReplTimeMode = ReplWallClock | ReplStatic
data Options = Options
{ optServerJar :: FilePath
, optLedgerHost :: String
, optLedgerPort :: String
, optLedgerConfig :: Maybe (String, String)
, optMbAuthTokenFile :: Maybe FilePath
, optMbSslConfig :: Maybe ClientSSLConfig
, optMaxInboundMessageSize :: Maybe MaxInboundMessageSize
@ -80,8 +79,12 @@ withReplClient opts@Options{..} f = withTempFile $ \portFile -> do
replServer <- javaProc $ concat
[ [ "-jar", optServerJar
, "--port-file", portFile
, "--ledger-host", optLedgerHost
, "--ledger-port", optLedgerPort
]
, concat
[ [ "--ledger-host", host
, "--ledger-port", port
]
| Just (host, port) <- [optLedgerConfig]
]
, [ "--access-token-file=" <> tokenFile | Just tokenFile <- [optMbAuthTokenFile] ]
, do Just tlsConf <- [ optMbSslConfig ]

View File

@ -31,8 +31,8 @@ import scala.util.{Failure, Success, Try}
object ReplServiceMain extends App {
case class Config(
portFile: Path,
ledgerHost: String,
ledgerPort: Int,
ledgerHost: Option[String],
ledgerPort: Option[Int],
accessTokenFile: Option[Path],
maxInboundMessageSize: Int,
tlsConfig: Option[TlsConfiguration],
@ -60,12 +60,12 @@ object ReplServiceMain extends App {
.action((portFile, c) => c.copy(portFile = Paths.get(portFile)))
opt[String]("ledger-host")
.required()
.action((host, c) => c.copy(ledgerHost = host))
.optional()
.action((host, c) => c.copy(ledgerHost = Some(host)))
opt[Int]("ledger-port")
.required()
.action((port, c) => c.copy(ledgerPort = port))
.optional()
.action((port, c) => c.copy(ledgerPort = Some(port)))
opt[String]("access-token-file")
.optional()
@ -124,14 +124,23 @@ object ReplServiceMain extends App {
setTimeMode(c, ScriptTimeMode.Static)
}
.text("Use static time.")
checkConfig(c =>
(c.ledgerHost, c.ledgerPort) match {
case (Some(_), None) =>
failure("Must specified either both --ledger-host and --ledger-port or neither")
case (None, Some(_)) =>
failure("Must specified either both --ledger-host and --ledger-port or neither")
case _ => success
})
}
def parse(args: Array[String]): Option[Config] =
parser.parse(
args,
Config(
portFile = null,
ledgerHost = null,
ledgerPort = 0,
ledgerHost = None,
ledgerPort = None,
accessTokenFile = None,
tlsConfig = None,
maxInboundMessageSize = RunnerConfig.DefaultMaxInboundMessageSize,
@ -155,11 +164,12 @@ object ReplServiceMain extends App {
implicit val ec: ExecutionContext = system.dispatcher
val tokenHolder = config.accessTokenFile.map(new TokenHolder(_))
val defaultParticipant = (config.ledgerHost, config.ledgerPort) match {
case (Some(host), Some(port)) => Some(ApiParameters(host, port, tokenHolder.flatMap(_.token)))
case _ => None
}
val participantParams =
Participants(
Some(ApiParameters(config.ledgerHost, config.ledgerPort, tokenHolder.flatMap(_.token))),
Map.empty,
Map.empty)
Participants(defaultParticipant, Map.empty, Map.empty)
val applicationId = ApplicationId("daml repl")
val clients = Await.result(
Runner

View File

@ -60,8 +60,10 @@ two forms:
instance of ``Show`` and not ``()``.
2. A pure expression ``expr`` of type ``a`` for some type ``a`` where
``a`` is an instance of ``Show``. This will evaluate ``expr``
and print the result.
``a`` is an instance of ``Show``. This will evaluate ``expr`` and
print the result. If you are only interest in pure expressions you
can also use DAML REPL :ref:`without connecting to a ledger
<repl-no-ledger>`.
3. A binding of the form ``pat <- expr`` where ``pat`` is pattern, e.g.,
a variable name ``x`` to bind the result to
@ -130,6 +132,21 @@ You can use import declarations at the prompt to import additional modules.
daml> import DA.Time
daml> debug (days 1)
.. _repl-no-ledger:
Using DAML REPL without a Ledger
================================
If you are only interested in pure expressions, e.g., because you want
to test how some function behaves you can omit the ``--ledger-host``
and ``-ledger-port`` parameters. DAML REPL will work as usual but any
attempts to call DAML Script APIs that interact with the ledger, e.g.,
``submit`` will result in the following error:
.. code-block:: none
daml> java.lang.RuntimeException: No default participant
Connecting via TLS
==================