From 49f37b8e67cd81f2c88a4218b972a0eef0019e7d Mon Sep 17 00:00:00 2001 From: Kamil Bozek <72492440+kamil-da@users.noreply.github.com> Date: Mon, 31 Jan 2022 10:04:02 +0100 Subject: [PATCH] Allow defining ledger-begin and ledger-end offset conditions in ledger-api-bench-tool [DPP-836] (#12521) * Allow defining ledger-begin and ledger-end offset conditions for transactions and transaction-trees streams in ledger-api-bench-tool CHANGELOG_BEGIN - [Integration Kit] - ledger-api-bench-tool allow to define ledger-begin and ledger-end offset boundaries for streams. CHANGELOG_END * Print ledger-api-bench-tool config in black&white to avoid colouring special characters printed * Reduce duplication * Add more unit tests for offset --- bazel-java-deps.bzl | 2 +- ledger/ledger-api-bench-tool/BUILD.bazel | 1 + .../api/benchtool/LedgerApiBenchTool.scala | 3 +- .../ledger/api/benchtool/config/Cli.scala | 77 +++-- .../api/benchtool/config/WorkflowConfig.scala | 2 +- .../config/WorkflowConfigParser.scala | 9 +- .../ledger/api/benchtool/config/CliSpec.scala | 306 ++++++++++++++++++ .../config/WorkflowConfigParserSpec.scala | 38 ++- maven_install_2.13.json | 118 +++---- 9 files changed, 461 insertions(+), 95 deletions(-) create mode 100644 ledger/ledger-api-bench-tool/src/test/suite/scala/com/daml/ledger/api/benchtool/config/CliSpec.scala diff --git a/bazel-java-deps.bzl b/bazel-java-deps.bzl index bdb79e3c3fe3..18772f3c4c2f 100644 --- a/bazel-java-deps.bzl +++ b/bazel-java-deps.bzl @@ -52,7 +52,7 @@ def install_java_deps(): "com.google.code.gson:gson:2.8.2", "com.google.guava:guava:29.0-jre", "com.h2database:h2:2.1.210", - "com.lihaoyi:pprint_{}:0.6.0".format(scala_major_version), + "com.lihaoyi:pprint_{}:0.7.1".format(scala_major_version), "com.lihaoyi:sjsonnet_{}:0.3.0".format(scala_major_version), "commons-io:commons-io:2.5", "com.oracle.database.jdbc:ojdbc8:19.8.0.0", diff --git a/ledger/ledger-api-bench-tool/BUILD.bazel b/ledger/ledger-api-bench-tool/BUILD.bazel index cc5fba9b817d..43a79dd1c5b1 100644 --- a/ledger/ledger-api-bench-tool/BUILD.bazel +++ b/ledger/ledger-api-bench-tool/BUILD.bazel @@ -89,6 +89,7 @@ da_scala_test_suite( deps = [ ":ledger-api-bench-tool-lib", "//language-support/scala/bindings", + "//ledger/ledger-api-common", "//ledger/metrics", "@maven//:com_typesafe_config", "@maven//:io_dropwizard_metrics_metrics_core", diff --git a/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/LedgerApiBenchTool.scala b/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/LedgerApiBenchTool.scala index 1916117302ac..631bdcd6aa9e 100644 --- a/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/LedgerApiBenchTool.scala +++ b/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/LedgerApiBenchTool.scala @@ -11,7 +11,6 @@ import com.daml.ledger.resources.{ResourceContext, ResourceOwner} import io.grpc.Channel import io.grpc.netty.{NegotiationType, NettyChannelBuilder} import org.slf4j.{Logger, LoggerFactory} -import pprint.PPrinter import java.util.concurrent.{ ArrayBlockingQueue, @@ -146,6 +145,6 @@ object LedgerApiBenchTool { ) private val logger: Logger = LoggerFactory.getLogger(getClass) - private val printer: PPrinter = pprint.PPrinter(200, 1000) + private val printer = pprint.PPrinter.BlackWhite private def prettyPrint(x: Any): String = printer(x).toString() } diff --git a/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/Cli.scala b/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/Cli.scala index e06e4449c460..7e6b0e1fb39e 100644 --- a/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/Cli.scala +++ b/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/Cli.scala @@ -170,7 +170,33 @@ object Cli { } def offset(stringValue: String): LedgerOffset = - LedgerOffset.defaultInstance.withAbsolute(stringValue) + stringValue match { + case "ledger-begin" => + LedgerOffset.defaultInstance.withBoundary(LedgerOffset.LedgerBoundary.LEDGER_BEGIN) + case "ledger-end" => + LedgerOffset.defaultInstance.withBoundary(LedgerOffset.LedgerBoundary.LEDGER_END) + case _ => + LedgerOffset.defaultInstance.withAbsolute(stringValue) + } + + def transactionObjectives( + maxDelaySeconds: Option[Long], + minConsumptionSpeed: Option[Double], + minItemRate: Option[Double], + maxItemRate: Option[Double], + ): Option[WorkflowConfig.StreamConfig.TransactionObjectives] = + (maxDelaySeconds, minConsumptionSpeed, minItemRate, maxItemRate) match { + case (None, None, None, None) => None + case _ => + Some( + WorkflowConfig.StreamConfig.TransactionObjectives( + maxDelaySeconds = maxDelaySeconds, + minConsumptionSpeed = minConsumptionSpeed, + minItemRate = minItemRate, + maxItemRate = maxItemRate, + ) + ) + } def transactionsConfig : Either[String, WorkflowConfig.StreamConfig.TransactionsStreamConfig] = for { @@ -187,14 +213,8 @@ object Cli { filters = filters, beginOffset = beginOffset, endOffset = endOffset, - objectives = Some( - WorkflowConfig.StreamConfig.TransactionObjectives( - maxDelaySeconds = maxDelaySeconds, - minConsumptionSpeed = minConsumptionSpeed, - minItemRate = minItemRate, - maxItemRate = maxItemRate, - ) - ), + objectives = + transactionObjectives(maxDelaySeconds, minConsumptionSpeed, minItemRate, maxItemRate), ) def transactionTreesConfig @@ -213,16 +233,25 @@ object Cli { filters = filters, beginOffset = beginOffset, endOffset = endOffset, - objectives = Some( - WorkflowConfig.StreamConfig.TransactionObjectives( - maxDelaySeconds = maxDelaySeconds, - minConsumptionSpeed = minConsumptionSpeed, - minItemRate = minItemRate, - maxItemRate = maxItemRate, - ) - ), + objectives = + transactionObjectives(maxDelaySeconds, minConsumptionSpeed, minItemRate, maxItemRate), ) + def rateObjectives( + minItemRate: Option[Double], + maxItemRate: Option[Double], + ): Option[WorkflowConfig.StreamConfig.RateObjectives] = + (minItemRate, maxItemRate) match { + case (None, None) => None + case _ => + Some( + WorkflowConfig.StreamConfig.RateObjectives( + minItemRate = minItemRate, + maxItemRate = maxItemRate, + ) + ) + } + def activeContractsConfig : Either[String, WorkflowConfig.StreamConfig.ActiveContractsStreamConfig] = for { name <- stringField("name") @@ -232,12 +261,7 @@ object Cli { } yield WorkflowConfig.StreamConfig.ActiveContractsStreamConfig( name = name, filters = filters, - objectives = Some( - WorkflowConfig.StreamConfig.RateObjectives( - minItemRate = minItemRate, - maxItemRate = maxItemRate, - ) - ), + objectives = rateObjectives(minItemRate, maxItemRate), ) def completionsConfig: Either[String, WorkflowConfig.StreamConfig.CompletionsStreamConfig] = @@ -253,12 +277,7 @@ object Cli { party = party, applicationId = applicationId, beginOffset = beginOffset, - objectives = Some( - WorkflowConfig.StreamConfig.RateObjectives( - minItemRate = minItemRate, - maxItemRate = maxItemRate, - ) - ), + objectives = rateObjectives(minItemRate, maxItemRate), ) val config = stringField("stream-type").flatMap[String, WorkflowConfig.StreamConfig] { diff --git a/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/WorkflowConfig.scala b/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/WorkflowConfig.scala index de105a475c38..bdb0d8664793 100644 --- a/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/WorkflowConfig.scala +++ b/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/WorkflowConfig.scala @@ -28,7 +28,7 @@ object WorkflowConfig { ) } - sealed trait StreamConfig { + sealed trait StreamConfig extends Product with Serializable { def name: String } diff --git a/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/WorkflowConfigParser.scala b/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/WorkflowConfigParser.scala index 6841fc4de1aa..9886e9b18d62 100644 --- a/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/WorkflowConfigParser.scala +++ b/ledger/ledger-api-bench-tool/src/main/scala/com/daml/ledger/api/benchtool/config/WorkflowConfigParser.scala @@ -38,8 +38,13 @@ object WorkflowConfigParser { "max_item_rate", )(StreamConfig.RateObjectives.apply) - implicit val offsetDecoder: Decoder[LedgerOffset] = - Decoder.decodeString.map(LedgerOffset.defaultInstance.withAbsolute) + implicit val offsetDecoder: Decoder[LedgerOffset] = { + Decoder.decodeString.map { + case "ledger-begin" => LedgerOffset().withBoundary(LedgerOffset.LedgerBoundary.LEDGER_BEGIN) + case "ledger-end" => LedgerOffset().withBoundary(LedgerOffset.LedgerBoundary.LEDGER_END) + case absolute => LedgerOffset.defaultInstance.withAbsolute(absolute) + } + } implicit val partyFilterDecoder: Decoder[StreamConfig.PartyFilter] = Decoder.forProduct2( diff --git a/ledger/ledger-api-bench-tool/src/test/suite/scala/com/daml/ledger/api/benchtool/config/CliSpec.scala b/ledger/ledger-api-bench-tool/src/test/suite/scala/com/daml/ledger/api/benchtool/config/CliSpec.scala new file mode 100644 index 000000000000..0f1df6594afa --- /dev/null +++ b/ledger/ledger-api-bench-tool/src/test/suite/scala/com/daml/ledger/api/benchtool/config/CliSpec.scala @@ -0,0 +1,306 @@ +// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.daml.ledger.api.benchtool.config + +import com.daml.ledger.api.v1.ledger_offset.LedgerOffset +import org.scalatest.OptionValues +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import org.scalatest.wordspec.AnyWordSpec + +import scala.concurrent.duration._ +import java.io.File + +class CliSpec extends AnyWordSpec with Matchers with OptionValues with TableDrivenPropertyChecks { + + "Cli" should { + "produce the default config when no arguments defined" in { + parse() shouldBe Config.Default + } + + "parse ledger API endpoint" in { + val endpoint = "foo:123" + val expectedConfig = Config.Default.copy( + ledger = Config.Ledger( + hostname = "foo", + port = 123, + ) + ) + parse("--endpoint", endpoint) shouldBe expectedConfig + parse("-e", endpoint) shouldBe expectedConfig + } + + "parse workflow config location" in { + val workflowFile = "/some/path/to/file" + val expectedConfig = Config.Default.copy(workflowConfigFile = Some(new File(workflowFile))) + parse("--workflow-config", workflowFile) shouldBe expectedConfig + parse("-w", workflowFile) shouldBe expectedConfig + } + + "parse maximum number of in-flight commands parameter" in { + val maxCommands = 123 + val expectedConfig = Config.Default.copy(maxInFlightCommands = maxCommands) + parse("--max-in-flight-commands", maxCommands.toString) shouldBe expectedConfig + } + + "parse submission batch size" in { + val batchSize = 1234 + val expectedConfig = Config.Default.copy(submissionBatchSize = batchSize) + parse("--submission-batch-size", batchSize.toString) shouldBe expectedConfig + } + + "parse log interval" in { + val cases = Table( + "cli value" -> "duration", + "1s" -> 1.second, + "123millis" -> 123.millis, + "5m" -> 5.minutes, + ) + forAll(cases) { (argument, intervalDuration) => + val expectedConfig = Config.Default.copy(reportingPeriod = intervalDuration) + parse("--log-interval", argument) shouldBe expectedConfig + parse("-r", argument) shouldBe expectedConfig + } + } + + "parse thread pool executor's core pool size" in { + val size = 123 + val expectedConfig = + Config.Default.copy(concurrency = Config.Default.concurrency.copy(corePoolSize = size)) + parse("--core-pool-size", size.toString) shouldBe expectedConfig + } + + "parse thread pool executor's max pool size" in { + val size = 123 + val expectedConfig = + Config.Default.copy(concurrency = Config.Default.concurrency.copy(maxPoolSize = size)) + parse("--max-pool-size", size.toString) shouldBe expectedConfig + } + + "parse stream type" in { + import WorkflowConfig.StreamConfig._ + val name = "streamname" + val party = "dummy" + val appId = "appid" + val cases = Table( + "cli argument" -> "stream config", + s"stream-type=transactions,name=$name,filters=$party" -> TransactionsStreamConfig( + name = name, + filters = List(PartyFilter(party, Nil)), + beginOffset = None, + endOffset = None, + objectives = None, + ), + s"stream-type=transaction-trees,name=$name,filters=$party" -> TransactionTreesStreamConfig( + name = name, + filters = List(PartyFilter(party, Nil)), + beginOffset = None, + endOffset = None, + objectives = None, + ), + s"stream-type=active-contracts,name=$name,filters=$party" -> ActiveContractsStreamConfig( + name = name, + filters = List(PartyFilter(party, Nil)), + objectives = None, + ), + s"stream-type=completions,name=$name,party=$party,application-id=$appId" -> CompletionsStreamConfig( + name = name, + party = party, + applicationId = appId, + beginOffset = None, + objectives = None, + ), + ) + forAll(cases) { (argument, config) => + val expectedConfig = + Config.Default.copy(workflow = Config.Default.workflow.copy(streams = List(config))) + parse("--consume-stream", argument) shouldBe expectedConfig + parse("-s", argument) shouldBe expectedConfig + } + } + + "parse stream filters" in { + import WorkflowConfig.StreamConfig._ + val name = "streamname" + val party1 = "alice" + val party2 = "bob" + val party3 = "david" + val template1 = "packageid:Foo:Foo1" + val template2 = "packageid2:Foo:Foo2" + // each party filter separated by '+' and each template in a filter separated by '@' + val filters = s"$party1+$party2@$template1@$template2+$party3@$template2" + val filtersList = List( + PartyFilter(party1, List()), + PartyFilter(party2, List(template1, template2)), + PartyFilter(party3, List(template2)), + ) + val cases = Table( + "cli argument" -> "stream config", + s"stream-type=transactions,name=$name,filters=$filters" -> TransactionsStreamConfig( + name = name, + filters = filtersList, + beginOffset = None, + endOffset = None, + objectives = None, + ), + s"stream-type=transaction-trees,name=$name,filters=$filters" -> TransactionTreesStreamConfig( + name = name, + filters = filtersList, + beginOffset = None, + endOffset = None, + objectives = None, + ), + s"stream-type=active-contracts,name=$name,filters=$filters" -> ActiveContractsStreamConfig( + name = name, + filters = filtersList, + objectives = None, + ), + ) + forAll(cases) { (argument, config) => + val expectedConfig = + Config.Default.copy(workflow = Config.Default.workflow.copy(streams = List(config))) + parse("--consume-stream", argument) shouldBe expectedConfig + parse("-s", argument) shouldBe expectedConfig + } + } + + "parse begin offset" in { + import WorkflowConfig.StreamConfig._ + val name = "streamname" + val party = "dummy" + val cases = Table( + "cli parameter" -> "offset", + "abcdef" -> LedgerOffset.defaultInstance.withAbsolute("abcdef"), + "ledger-begin" -> LedgerOffset.defaultInstance.withBoundary( + LedgerOffset.LedgerBoundary.LEDGER_BEGIN + ), + "ledger-end" -> LedgerOffset.defaultInstance.withBoundary( + LedgerOffset.LedgerBoundary.LEDGER_END + ), + ) + forAll(cases) { (argument, offset) => + val streamConfig = TransactionsStreamConfig( + name = name, + filters = List(PartyFilter(party, Nil)), + beginOffset = Some(offset), + endOffset = None, + objectives = None, + ) + val expectedConfig = + Config.Default.copy(workflow = Config.Default.workflow.copy(streams = List(streamConfig))) + + parse( + "--consume-stream", + s"stream-type=transactions,name=$name,filters=$party,begin-offset=$argument", + ) shouldBe expectedConfig + } + } + + "parse end offset" in { + import WorkflowConfig.StreamConfig._ + val name = "streamname" + val party = "dummy" + val cases = Table( + "cli parameter" -> "offset", + "abcdef" -> LedgerOffset.defaultInstance.withAbsolute("abcdef"), + "ledger-begin" -> LedgerOffset.defaultInstance.withBoundary( + LedgerOffset.LedgerBoundary.LEDGER_BEGIN + ), + "ledger-end" -> LedgerOffset.defaultInstance.withBoundary( + LedgerOffset.LedgerBoundary.LEDGER_END + ), + ) + forAll(cases) { (argument, offset) => + val streamConfig = TransactionsStreamConfig( + name = name, + filters = List(PartyFilter(party, Nil)), + beginOffset = None, + endOffset = Some(offset), + objectives = None, + ) + val expectedConfig = + Config.Default.copy(workflow = Config.Default.workflow.copy(streams = List(streamConfig))) + + parse( + "--consume-stream", + s"stream-type=transactions,name=$name,filters=$party,end-offset=$argument", + ) shouldBe expectedConfig + } + } + + "parse transaction objectives" in { + import WorkflowConfig.StreamConfig._ + val name = "streamname" + val party = "dummy" + val cases = Table( + "cli parameter" -> "objectives", + "max-delay=5" -> TransactionObjectives(maxDelaySeconds = Some(5), None, None, None), + "min-consumption-speed=1.23" -> TransactionObjectives( + None, + minConsumptionSpeed = Some(1.23), + None, + None, + ), + "min-item-rate=1234.5" -> TransactionObjectives( + None, + None, + minItemRate = Some(1234.5), + None, + ), + "max-item-rate=1234.5" -> TransactionObjectives( + None, + None, + None, + maxItemRate = Some(1234.5), + ), + ) + forAll(cases) { (argument, objectives) => + val streamConfig = TransactionsStreamConfig( + name = name, + filters = List(PartyFilter(party, Nil)), + beginOffset = None, + endOffset = None, + objectives = Some(objectives), + ) + val expectedConfig = + Config.Default.copy(workflow = Config.Default.workflow.copy(streams = List(streamConfig))) + + parse( + "--consume-stream", + s"stream-type=transactions,name=$name,filters=$party,$argument", + ) shouldBe expectedConfig + } + } + + "parse rate objectives" in { + import WorkflowConfig.StreamConfig._ + val name = "streamname" + val party = "dummy" + val cases = Table( + "cli parameter" -> "objectives", + "min-item-rate=1234.5" -> RateObjectives(minItemRate = Some(1234.5), None), + "max-item-rate=1234.5" -> RateObjectives(None, maxItemRate = Some(1234.5)), + ) + forAll(cases) { (argument, objectives) => + val streamConfig = ActiveContractsStreamConfig( + name = name, + filters = List(PartyFilter(party, Nil)), + objectives = Some(objectives), + ) + val expectedConfig = + Config.Default.copy(workflow = Config.Default.workflow.copy(streams = List(streamConfig))) + + parse( + "--consume-stream", + s"stream-type=active-contracts,name=$name,filters=$party,$argument", + ) shouldBe expectedConfig + } + } + + } + + private def parse(args: String*): Config = + Cli.config(args.toArray).value + +} diff --git a/ledger/ledger-api-bench-tool/src/test/suite/scala/com/daml/ledger/api/benchtool/config/WorkflowConfigParserSpec.scala b/ledger/ledger-api-bench-tool/src/test/suite/scala/com/daml/ledger/api/benchtool/config/WorkflowConfigParserSpec.scala index 487cc66e6ac3..9785cd833183 100644 --- a/ledger/ledger-api-bench-tool/src/test/suite/scala/com/daml/ledger/api/benchtool/config/WorkflowConfigParserSpec.scala +++ b/ledger/ledger-api-bench-tool/src/test/suite/scala/com/daml/ledger/api/benchtool/config/WorkflowConfigParserSpec.scala @@ -361,11 +361,47 @@ class WorkflowConfigParserSpec extends AnyWordSpec with Matchers { ) ) } + + "parse ledger-begin and ledger-end markers" in { + val yaml = + """streams: + | - type: transactions + | name: stream-1 + | filters: + | - party: Obs-2 + | templates: + | - Foo1 + | - Foo3 + | begin_offset: ledger-begin + | end_offset: ledger-end""".stripMargin + parseYaml(yaml) shouldBe Right( + WorkflowConfig( + submission = None, + streams = List( + WorkflowConfig.StreamConfig.TransactionsStreamConfig( + name = "stream-1", + filters = List( + WorkflowConfig.StreamConfig.PartyFilter( + party = "Obs-2", + templates = List("Foo1", "Foo3"), + ) + ), + beginOffset = Some(ledgerBeginOffset), + endOffset = Some(ledgerEndOffset), + objectives = None, + ) + ), + ) + ) + } } def parseYaml(yaml: String): Either[WorkflowConfigParser.ParserError, WorkflowConfig] = WorkflowConfigParser.parse(new StringReader(yaml)) def offset(str: String): LedgerOffset = LedgerOffset.defaultInstance.withAbsolute(str) - + private val ledgerBeginOffset = + LedgerOffset.defaultInstance.withBoundary(LedgerOffset.LedgerBoundary.LEDGER_BEGIN) + private val ledgerEndOffset = + LedgerOffset.defaultInstance.withBoundary(LedgerOffset.LedgerBoundary.LEDGER_END) } diff --git a/maven_install_2.13.json b/maven_install_2.13.json index 011bf3b7b440..3d7a8205f575 100644 --- a/maven_install_2.13.json +++ b/maven_install_2.13.json @@ -1,6 +1,6 @@ { "dependency_tree": { - "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": 1058100583, + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": 1297439326, "conflict_resolution": {}, "dependencies": [ { @@ -1101,44 +1101,44 @@ "url": "https://repo1.maven.org/maven2/com/h2database/h2/2.1.210/h2-2.1.210-sources.jar" }, { - "coord": "com.lihaoyi:fansi_2.13:0.2.9", + "coord": "com.lihaoyi:fansi_2.13:0.3.0", "dependencies": [ - "com.lihaoyi:sourcecode_2.13:0.2.1" + "com.lihaoyi:sourcecode_2.13:0.2.7" ], "directDependencies": [ - "com.lihaoyi:sourcecode_2.13:0.2.1" + "com.lihaoyi:sourcecode_2.13:0.2.7" ], - "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.2.9/fansi_2.13-0.2.9.jar", + "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.3.0/fansi_2.13-0.3.0.jar", "mirror_urls": [ - "https://repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.2.9/fansi_2.13-0.2.9.jar" + "https://repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.3.0/fansi_2.13-0.3.0.jar" ], - "sha256": "c347b6452152cf55d401090d3d3c230d96a5f9b6792d1bdb9b760e0d5187ed30", - "url": "https://repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.2.9/fansi_2.13-0.2.9.jar" + "sha256": "2f1d4cdd8971ef2cca8c37da8a04cb0ecf94bc89e59c6383185e88cde21f2e86", + "url": "https://repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.3.0/fansi_2.13-0.3.0.jar" }, { - "coord": "com.lihaoyi:fansi_2.13:jar:sources:0.2.9", + "coord": "com.lihaoyi:fansi_2.13:jar:sources:0.3.0", "dependencies": [ - "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.1" + "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.7" ], "directDependencies": [ - "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.1" + "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.7" ], - "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.2.9/fansi_2.13-0.2.9-sources.jar", + "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.3.0/fansi_2.13-0.3.0-sources.jar", "mirror_urls": [ - "https://repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.2.9/fansi_2.13-0.2.9-sources.jar" + "https://repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.3.0/fansi_2.13-0.3.0-sources.jar" ], - "sha256": "56e5c87e37267d4c2fcc955ddc95596dcd79bcbe13d343f0cdcc01e538492054", - "url": "https://repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.2.9/fansi_2.13-0.2.9-sources.jar" + "sha256": "9c3bc0d9cf55541edfffcfbce818fdda20bf60238d279327bb47d80767e0bea3", + "url": "https://repo1.maven.org/maven2/com/lihaoyi/fansi_2.13/0.3.0/fansi_2.13-0.3.0-sources.jar" }, { "coord": "com.lihaoyi:fastparse_2.13:2.3.0", "dependencies": [ - "com.lihaoyi:sourcecode_2.13:0.2.1", + "com.lihaoyi:sourcecode_2.13:0.2.7", "com.lihaoyi:geny_2.13:0.6.2" ], "directDependencies": [ "com.lihaoyi:geny_2.13:0.6.2", - "com.lihaoyi:sourcecode_2.13:0.2.1" + "com.lihaoyi:sourcecode_2.13:0.2.7" ], "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/fastparse_2.13/2.3.0/fastparse_2.13-2.3.0.jar", "mirror_urls": [ @@ -1151,11 +1151,11 @@ "coord": "com.lihaoyi:fastparse_2.13:jar:sources:2.3.0", "dependencies": [ "com.lihaoyi:geny_2.13:jar:sources:0.6.2", - "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.1" + "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.7" ], "directDependencies": [ "com.lihaoyi:geny_2.13:jar:sources:0.6.2", - "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.1" + "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.7" ], "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/fastparse_2.13/2.3.0/fastparse_2.13-2.3.0-sources.jar", "mirror_urls": [ @@ -1217,48 +1217,48 @@ "url": "https://repo1.maven.org/maven2/com/lihaoyi/os-lib_2.13/0.7.1/os-lib_2.13-0.7.1-sources.jar" }, { - "coord": "com.lihaoyi:pprint_2.13:0.6.0", + "coord": "com.lihaoyi:pprint_2.13:0.7.1", "dependencies": [ - "com.lihaoyi:sourcecode_2.13:0.2.1", - "com.lihaoyi:fansi_2.13:0.2.9" + "com.lihaoyi:sourcecode_2.13:0.2.7", + "com.lihaoyi:fansi_2.13:0.3.0" ], "directDependencies": [ - "com.lihaoyi:fansi_2.13:0.2.9", - "com.lihaoyi:sourcecode_2.13:0.2.1" + "com.lihaoyi:fansi_2.13:0.3.0", + "com.lihaoyi:sourcecode_2.13:0.2.7" ], - "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.6.0/pprint_2.13-0.6.0.jar", + "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.7.1/pprint_2.13-0.7.1.jar", "mirror_urls": [ - "https://repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.6.0/pprint_2.13-0.6.0.jar" + "https://repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.7.1/pprint_2.13-0.7.1.jar" ], - "sha256": "6bc908b7acb825bc0ce1148a1a417ab1b75335c98749e6e2d2ad3d09604e3701", - "url": "https://repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.6.0/pprint_2.13-0.6.0.jar" + "sha256": "261fd6fc54b5962e26295d37093100deae9d1fd6b4824326fcc439e17f9222a2", + "url": "https://repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.7.1/pprint_2.13-0.7.1.jar" }, { - "coord": "com.lihaoyi:pprint_2.13:jar:sources:0.6.0", + "coord": "com.lihaoyi:pprint_2.13:jar:sources:0.7.1", "dependencies": [ - "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.1", - "com.lihaoyi:fansi_2.13:jar:sources:0.2.9" + "com.lihaoyi:fansi_2.13:jar:sources:0.3.0", + "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.7" ], "directDependencies": [ - "com.lihaoyi:fansi_2.13:jar:sources:0.2.9", - "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.1" + "com.lihaoyi:fansi_2.13:jar:sources:0.3.0", + "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.7" ], - "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.6.0/pprint_2.13-0.6.0-sources.jar", + "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.7.1/pprint_2.13-0.7.1-sources.jar", "mirror_urls": [ - "https://repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.6.0/pprint_2.13-0.6.0-sources.jar" + "https://repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.7.1/pprint_2.13-0.7.1-sources.jar" ], - "sha256": "275253de7b118f809d45f63f73187d3e0ee47b80963e5557b9f404f319e0c4c1", - "url": "https://repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.6.0/pprint_2.13-0.6.0-sources.jar" + "sha256": "f5e9ff1ddb3708e39d93993b216309b61be6f59ce64f933dde01c2f66192e4ee", + "url": "https://repo1.maven.org/maven2/com/lihaoyi/pprint_2.13/0.7.1/pprint_2.13-0.7.1-sources.jar" }, { "coord": "com.lihaoyi:scalatags_2.13:0.9.1", "dependencies": [ - "com.lihaoyi:sourcecode_2.13:0.2.1", + "com.lihaoyi:sourcecode_2.13:0.2.7", "com.lihaoyi:geny_2.13:0.6.2" ], "directDependencies": [ "com.lihaoyi:geny_2.13:0.6.2", - "com.lihaoyi:sourcecode_2.13:0.2.1" + "com.lihaoyi:sourcecode_2.13:0.2.7" ], "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/scalatags_2.13/0.9.1/scalatags_2.13-0.9.1.jar", "mirror_urls": [ @@ -1271,11 +1271,11 @@ "coord": "com.lihaoyi:scalatags_2.13:jar:sources:0.9.1", "dependencies": [ "com.lihaoyi:geny_2.13:jar:sources:0.6.2", - "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.1" + "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.7" ], "directDependencies": [ "com.lihaoyi:geny_2.13:jar:sources:0.6.2", - "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.1" + "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.7" ], "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/scalatags_2.13/0.9.1/scalatags_2.13-0.9.1-sources.jar", "mirror_urls": [ @@ -1287,22 +1287,22 @@ { "coord": "com.lihaoyi:sjsonnet_2.13:0.3.0", "dependencies": [ - "com.lihaoyi:pprint_2.13:0.6.0", - "com.lihaoyi:sourcecode_2.13:0.2.1", - "com.lihaoyi:fansi_2.13:0.2.9", + "com.lihaoyi:pprint_2.13:0.7.1", + "com.lihaoyi:fansi_2.13:0.3.0", "com.github.scopt:scopt_2.13:4.0.0", "org.scala-lang:scala-library:2.13.6", "com.lihaoyi:os-lib_2.13:0.7.1", "org.tukaani:xz:1.8", "org.scala-lang.modules:scala-collection-compat_2.13:2.5.0", "com.lihaoyi:fastparse_2.13:2.3.0", + "com.lihaoyi:sourcecode_2.13:0.2.7", "com.lihaoyi:upickle-core_2.13:1.2.0", "com.lihaoyi:ujson_2.13:1.2.0", "com.lihaoyi:scalatags_2.13:0.9.1", "com.lihaoyi:geny_2.13:0.6.2" ], "directDependencies": [ - "com.lihaoyi:pprint_2.13:0.6.0", + "com.lihaoyi:pprint_2.13:0.7.1", "com.github.scopt:scopt_2.13:4.0.0", "com.lihaoyi:os-lib_2.13:0.7.1", "org.tukaani:xz:1.8", @@ -1321,17 +1321,17 @@ { "coord": "com.lihaoyi:sjsonnet_2.13:jar:sources:0.3.0", "dependencies": [ - "com.lihaoyi:fansi_2.13:jar:sources:0.2.9", "com.lihaoyi:fastparse_2.13:jar:sources:2.3.0", "com.lihaoyi:scalatags_2.13:jar:sources:0.9.1", "org.tukaani:xz:jar:sources:1.8", "org.scala-lang:scala-library:jar:sources:2.13.6", "com.lihaoyi:os-lib_2.13:jar:sources:0.7.1", "com.github.scopt:scopt_2.13:jar:sources:4.0.0", - "com.lihaoyi:pprint_2.13:jar:sources:0.6.0", + "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.7", + "com.lihaoyi:fansi_2.13:jar:sources:0.3.0", "com.lihaoyi:ujson_2.13:jar:sources:1.2.0", "com.lihaoyi:upickle-core_2.13:jar:sources:1.2.0", - "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.1", + "com.lihaoyi:pprint_2.13:jar:sources:0.7.1", "com.lihaoyi:geny_2.13:jar:sources:0.6.2", "org.scala-lang.modules:scala-collection-compat_2.13:jar:sources:2.5.0" ], @@ -1341,8 +1341,8 @@ "org.tukaani:xz:jar:sources:1.8", "com.lihaoyi:os-lib_2.13:jar:sources:0.7.1", "com.github.scopt:scopt_2.13:jar:sources:4.0.0", - "com.lihaoyi:pprint_2.13:jar:sources:0.6.0", "com.lihaoyi:ujson_2.13:jar:sources:1.2.0", + "com.lihaoyi:pprint_2.13:jar:sources:0.7.1", "org.scala-lang.modules:scala-collection-compat_2.13:jar:sources:2.5.0" ], "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/sjsonnet_2.13/0.3.0/sjsonnet_2.13-0.3.0-sources.jar", @@ -1353,26 +1353,26 @@ "url": "https://repo1.maven.org/maven2/com/lihaoyi/sjsonnet_2.13/0.3.0/sjsonnet_2.13-0.3.0-sources.jar" }, { - "coord": "com.lihaoyi:sourcecode_2.13:0.2.1", + "coord": "com.lihaoyi:sourcecode_2.13:0.2.7", "dependencies": [], "directDependencies": [], - "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.1/sourcecode_2.13-0.2.1.jar", + "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.7/sourcecode_2.13-0.2.7.jar", "mirror_urls": [ - "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.1/sourcecode_2.13-0.2.1.jar" + "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.7/sourcecode_2.13-0.2.7.jar" ], - "sha256": "f18739c68e4f71853540ffeb4f162b223258508215a36c3be1e12cd28b21af50", - "url": "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.1/sourcecode_2.13-0.2.1.jar" + "sha256": "a639a90e2d21bbafd8a5e213c65442aad200ee086951605cbda8835bc6ef11d3", + "url": "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.7/sourcecode_2.13-0.2.7.jar" }, { - "coord": "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.1", + "coord": "com.lihaoyi:sourcecode_2.13:jar:sources:0.2.7", "dependencies": [], "directDependencies": [], - "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.1/sourcecode_2.13-0.2.1-sources.jar", + "file": "v1/https/repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.7/sourcecode_2.13-0.2.7-sources.jar", "mirror_urls": [ - "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.1/sourcecode_2.13-0.2.1-sources.jar" + "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.7/sourcecode_2.13-0.2.7-sources.jar" ], - "sha256": "958af37ade319c9d1b8fa27d904d66578c4b5d86c917979d73a77f11fc76c1ad", - "url": "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.1/sourcecode_2.13-0.2.1-sources.jar" + "sha256": "e4cae365cd26b19ffb1491472cbc91ed853bb2a1cbbcc8176d0187affebd8bbe", + "url": "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_2.13/0.2.7/sourcecode_2.13-0.2.7-sources.jar" }, { "coord": "com.lihaoyi:ujson_2.13:1.2.0",