Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ import org.lfdecentralizedtrust.splice.codegen.java.splice.ans.AnsRules
import org.lfdecentralizedtrust.splice.config.NetworkAppClientConfig
import org.lfdecentralizedtrust.splice.environment.SpliceConsoleEnvironment
import org.lfdecentralizedtrust.splice.http.v0.definitions
import org.lfdecentralizedtrust.splice.http.v0.definitions.GetDsoInfoResponse
import org.lfdecentralizedtrust.splice.http.v0.definitions.{
GetDsoInfoResponse,
UpdateHistoryItem,
UpdateHistoryItemV2,
}
import org.lfdecentralizedtrust.splice.scan.{ScanApp, ScanAppBootstrap}
import org.lfdecentralizedtrust.splice.scan.automation.ScanAutomationService
import org.lfdecentralizedtrust.splice.scan.admin.api.client.commands.HttpScanAppClient
Expand Down Expand Up @@ -457,24 +461,39 @@ abstract class ScanAppReference(
count: Int,
after: Option[(Long, String)],
lossless: Boolean,
) = {
): Seq[UpdateHistoryItem] = {
consoleEnvironment.run {
httpCommand(
HttpScanAppClient.GetUpdateHistoryV0(count, after, lossless)
)
}
}

@deprecated(message = "Use getUpdateHistory instead", since = "0.4.2")
def getUpdateHistoryV1(
count: Int,
after: Option[(Long, String)],
encoding: definitions.DamlValueEncoding,
): Seq[UpdateHistoryItem] = {
consoleEnvironment.run {
httpCommand(
HttpScanAppClient.GetUpdateHistoryV1(count, after, encoding)
)
}
}

def getUpdateHistory(
count: Int,
after: Option[(Long, String)],
encoding: definitions.DamlValueEncoding,
) = {
): Seq[UpdateHistoryItemV2] = {
consoleEnvironment.run {
httpCommand(
HttpScanAppClient.GetUpdateHistory(count, after, encoding)
HttpScanAppClient.GetUpdateHistoryV2(count, after, encoding)
)
}
}

def getUpdate(updateId: String, encoding: definitions.DamlValueEncoding) = {
consoleEnvironment.run {
httpCommand(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import org.lfdecentralizedtrust.splice.config.SpliceConfig
import org.lfdecentralizedtrust.splice.console.ScanAppBackendReference
import org.lfdecentralizedtrust.splice.environment.SpliceEnvironment
import org.lfdecentralizedtrust.splice.http.v0.definitions.DamlValueEncoding.members.CompactJson
import org.lfdecentralizedtrust.splice.http.v0.definitions.{AcsResponse, UpdateHistoryItem}
import org.lfdecentralizedtrust.splice.http.v0.definitions.UpdateHistoryItem.members
import org.lfdecentralizedtrust.splice.http.v0.definitions.{AcsResponse, UpdateHistoryItemV2}
import org.lfdecentralizedtrust.splice.http.v0.definitions.UpdateHistoryItemV2.members
import org.lfdecentralizedtrust.splice.http.v0.definitions.UpdateHistoryReassignment.Event.members as reassignmentMembers
import org.lfdecentralizedtrust.splice.integration.tests.SpliceTests.SpliceTestConsoleEnvironment
import org.lfdecentralizedtrust.splice.scan.automation.AcsSnapshotTrigger
Expand Down Expand Up @@ -90,13 +90,13 @@ class UpdateHistorySanityCheckPlugin(
private def paginateHistory(
scan: ScanAppBackendReference,
after: Option[(Long, String)],
acc: Chain[UpdateHistoryItem],
): Chain[UpdateHistoryItem] = {
acc: Chain[UpdateHistoryItemV2],
): Chain[UpdateHistoryItemV2] = {
val result = scan.getUpdateHistory(10, after, encoding = CompactJson)
val newAcc = acc ++ Chain.fromSeq(result)
result.lastOption match {
case None => acc // done
case Some(members.UpdateHistoryTransaction(last)) =>
case Some(members.UpdateHistoryTransactionV2(last)) =>
paginateHistory(
scan,
Some((last.migrationId, last.recordTime)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,24 @@ class ScanHistoryBackfillingIntegrationTest
}

clue("Compare scan histories with each other using the v1 HTTP endpoint") {
// The v1 endpoint is deprecated, but we still have users using it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really want to run both endpoints at the end of every test? Seems a bit overkill

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not notice that this runs at the end of every test? there were versions of this for v0 and v1 so I (lazily) added v2 for some coverage

@nowarn("cat=deprecation")
val sv1HttpUpdates =
sv1ScanBackend.getUpdateHistoryV1(1000, None, encoding = CompactJson)

@nowarn("cat=deprecation")
val sv2HttpUpdates =
sv2ScanBackend.getUpdateHistoryV1(1000, None, encoding = CompactJson)

// Compare common prefix, as there might be concurrent activity
val commonLength = sv1HttpUpdates.length min sv2HttpUpdates.length
commonLength should be > 10
val sv1Items = sv1HttpUpdates.take(commonLength)
val sv2Items = sv2HttpUpdates.take(commonLength)
sv1Items should contain theSameElementsInOrderAs sv2Items
}

clue("Compare scan histories with each other using the v2 HTTP endpoint") {
val sv1HttpUpdates =
readUpdateHistoryFromScan(sv1ScanBackend)
val sv2HttpUpdates =
Expand Down
174 changes: 170 additions & 4 deletions apps/scan/src/main/openapi/scan.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,79 @@ paths:
application/json:
schema:
"$ref": "#/components/schemas/GetOpenAndIssuingMiningRoundsResponse"
/v2/updates:
post:
tags: [external, scan]
x-jvm-package: scan
operationId: "getUpdateHistoryV2"
description: |
Returns the update history in ascending order, paged, from ledger begin or optionally starting after a record time.
Compared to `/v1/updates`, the `/v2/updates` removes the `offset` field in responses,
which was hardcoded to 1 in `/v1/updates` for compatibility, and is now removed.
`/v2/updates` sorts events lexicographically in `events_by_id` by `ID` for convenience, which should not be confused with the
order of events in the transaction, for this you should rely on the order of `root_event_ids` and `child_event_ids`.
Updates are ordered lexicographically by `(migration id, record time)`.
For a given migration id, each update has a unique record time.
The record time ranges of different migrations may overlap, i.e.,
it is not guaranteed that the maximum record time of one migration is smaller than the minimum record time of the next migration,
and there may be two updates with the same record time but different migration ids.
requestBody:
required: true
content:
application/json:
schema:
"$ref": "#/components/schemas/UpdateHistoryRequestV2"
responses:
"200":
description: ok
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateHistoryResponseV2"
"400":
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/400"
"500":
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/500"

/v2/updates/{update_id}:
get:
tags: [external, scan]
x-jvm-package: scan
operationId: "getUpdateByIdV2"
description: |
Returns the update with the given update_id.
Compared to `/v1/updates/{update_id}`, the `/v2/updates/{update_id}` removes the `offset` field in responses,
which was hardcoded to 1 in `/v1/updates/{update_id}` for compatibility, and is now removed.
`/v2/updates/{update_id}` sorts events lexicographically in `events_by_id` by `ID` for convenience, which should not be confused with the
order of events in the transaction, for this you should rely on the order of `root_event_ids` and `child_event_ids`.
parameters:
- name: "update_id"
in: "path"
required: true
schema:
type: string
- name: "daml_value_encoding"
in: "query"
schema:
$ref: "#/components/schemas/DamlValueEncoding"
responses:
"200":
description: ok
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateHistoryItemV2"
"400":
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/400"
"404":
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/404"
"500":
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/500"

/v1/updates:
post:
tags: [external, scan]
deprecated: true
tags: [deprecated]
x-jvm-package: scan
operationId: "getUpdateHistoryV1"
description: |
Expand All @@ -283,6 +352,7 @@ paths:
The record time ranges of different migrations may overlap, i.e.,
it is not guaranteed that the maximum record time of one migration is smaller than the minimum record time of the next migration,
and there may be two updates with the same record time but different migration ids.
The order of items in events_by_id is not defined.
requestBody:
required: true
content:
Expand All @@ -303,13 +373,15 @@ paths:

/v1/updates/{update_id}:
get:
tags: [external, scan]
deprecated: true
tags: [ deprecated ]
x-jvm-package: scan
operationId: "getUpdateByIdV1"
description: |
Returns the update with the given update_id.
Unlike /v0/updates/{update_id}, this endpoint returns responses that are consistent across different
scan instances. Event ids returned by this endpoint are not comparable to event ids returned by /v0/updates.
The order of items in events_by_id is not defined.
parameters:
- name: "update_id"
in: "path"
Expand Down Expand Up @@ -1387,7 +1459,7 @@ paths:
x-jvm-package: scan
operationId: "getUpdateHistory"
description: |
**Deprecated**, use /v1/updates instead.
**Deprecated**, use /v2/updates instead.
Returns the update history in ascending order, paged, from ledger begin or optionally starting after a record time.
requestBody:
required: true
Expand All @@ -1414,7 +1486,7 @@ paths:
x-jvm-package: scan
operationId: "getUpdateById"
description: |
**Deprecated**, use /v1/updates/{update_id} instead.
**Deprecated**, use /v2/updates/{update_id} instead.
parameters:
- name: "update_id"
in: "path"
Expand Down Expand Up @@ -2035,6 +2107,33 @@ components:
format: int32
daml_value_encoding:
$ref: "#/components/schemas/DamlValueEncoding"
UpdateHistoryRequestV2:
type: object
required:
- page_size
properties:
after:
$ref: "#/components/schemas/UpdateHistoryRequestAfter"
description: |
The transactions returned will either have a higher migration id or
the same migration id and a record_time greater than the migration id and record time
specified.
page_size:
description: |
The maximum number of transactions returned for this request.
type: integer
format: int32
daml_value_encoding:
$ref: "#/components/schemas/DamlValueEncoding"
UpdateHistoryResponseV2:
type: object
required:
- transactions
properties:
transactions:
type: array
items:
$ref: "#/components/schemas/UpdateHistoryItemV2"
UpdateHistoryResponse:
type: object
required:
Expand All @@ -2044,6 +2143,13 @@ components:
type: array
items:
$ref: "#/components/schemas/UpdateHistoryItem"
UpdateHistoryItemV2:
type: object
description: |
An individual item in the update history. May be a transaction or a contract reassignment.
oneOf:
- $ref: "#/components/schemas/UpdateHistoryTransactionV2"
- $ref: "#/components/schemas/UpdateHistoryReassignment"
UpdateHistoryItem:
type: object
description: |
Expand Down Expand Up @@ -2228,6 +2334,66 @@ components:
type: object
additionalProperties:
$ref: "#/components/schemas/TreeEvent"
UpdateHistoryTransactionV2:
type: object
required:
- update_id
- migration_id
- workflow_id
- command_id
- record_time
- synchronizer_id
- effective_at
- root_event_ids
- events_by_id
properties:
update_id:
description: |
The id of the update. This is not comparable to other updates; it's
meant for correlating with server logs.
type: string
migration_id:
description: |
The migration id of the synchronizer.
type: integer
format: int64
workflow_id:
description: |
This transaction's Daml workflow ID; a workflow ID can be associated
with multiple transactions. If empty, no workflow ID was set.
type: string
record_time:
description: |
The time at which the transaction was sequenced, with microsecond
resolution, using ISO-8601 representation.
type: string
synchronizer_id:
description: |
The id of the synchronizer through which this transaction was sequenced.
type: string
effective_at:
description: |
Ledger effective time, using ISO-8601 representation. This is the time
returned by `getTime` for all Daml executed as part of this transaction,
both by the submitting participant and all confirming participants.
type: string
root_event_ids:
description: |
Roots of the transaction tree. These are guaranteed to occur as keys
of the `events_by_id` object.
type: array
items:
type: string
events_by_id:
x-scala-map-type: "scala.collection.immutable.SortedMap"
description: |
Changes to the ledger that were caused by this transaction, keyed by ID and sorted lexicographically by ID for display consistency.
Values are nodes of the transaction tree.
Within a transaction, IDs may be referenced by `root_event_ids` or
`child_event_ids` in `ExercisedEvent` herein, which are sorted in the order as they occurred in the transaction.
type: object
additionalProperties:
$ref: "#/components/schemas/TreeEvent"
TreeEvent:
type: object
description: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1258,7 +1258,41 @@ object HttpScanAppClient {
}
}

case class GetUpdateHistory(
case class GetUpdateHistoryV2(
count: Int,
after: Option[(Long, String)],
damlValueEncoding: definitions.DamlValueEncoding,
) extends InternalBaseCommand[http.GetUpdateHistoryV2Response, Seq[
definitions.UpdateHistoryItemV2
]] {
override def submitRequest(
client: http.ScanClient,
headers: List[HttpHeader],
): EitherT[Future, Either[
Throwable,
HttpResponse,
], http.GetUpdateHistoryV2Response] = {
client.getUpdateHistoryV2(
// the request is the same as for V1
definitions.UpdateHistoryRequestV2(
after = after.map { case (migrationId, recordTime) =>
definitions.UpdateHistoryRequestAfter(migrationId, recordTime)
},
pageSize = count,
damlValueEncoding = Some(damlValueEncoding),
),
headers,
)
}

override def handleOk()(implicit decoder: TemplateJsonDecoder) = {
case http.GetUpdateHistoryV2Response.OK(response) =>
Right(response.transactions)
}
}

@deprecated(message = "Use GetUpdateHistory instead", since = "0.4.2")
case class GetUpdateHistoryV1(
count: Int,
after: Option[(Long, String)],
damlValueEncoding: definitions.DamlValueEncoding,
Expand Down
Loading