mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
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:
parent
4b1438276c
commit
9881ff5231
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
==================
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user