Skip to content

Commit 9d1f7e0

Browse files
ray-roestenburg-daDA Automationstephencompall-DA
authored
Added /v2/updates (#1018)
* [ci] Added v2/updates/ Signed-off-by: DA Automation <splice-maintainers@digitalasset.com> * Update apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/ScanHistoryBackfillingIntegrationTest.scala Co-authored-by: Stephen Compall <stephen.compall@digitalasset.com> Signed-off-by: Raymond Roestenburg <98821776+ray-roestenburg-da@users.noreply.github.com> * Update apps/scan/src/main/openapi/scan.yaml Co-authored-by: Stephen Compall <stephen.compall@digitalasset.com> Signed-off-by: Raymond Roestenburg <98821776+ray-roestenburg-da@users.noreply.github.com> * [ci] Fix formatting. Signed-off-by: DA Automation <splice-maintainers@digitalasset.com> --------- Signed-off-by: DA Automation <splice-maintainers@digitalasset.com> Signed-off-by: Raymond Roestenburg <98821776+ray-roestenburg-da@users.noreply.github.com> Co-authored-by: DA Automation <splice-maintainers@digitalasset.com> Co-authored-by: Stephen Compall <stephen.compall@digitalasset.com>
1 parent 1c2d8c4 commit 9d1f7e0

File tree

9 files changed

+484
-144
lines changed

9 files changed

+484
-144
lines changed

apps/app/src/main/scala/org/lfdecentralizedtrust/splice/console/ScanAppReference.scala

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ import org.lfdecentralizedtrust.splice.codegen.java.splice.ans.AnsRules
2525
import org.lfdecentralizedtrust.splice.config.NetworkAppClientConfig
2626
import org.lfdecentralizedtrust.splice.environment.SpliceConsoleEnvironment
2727
import org.lfdecentralizedtrust.splice.http.v0.definitions
28-
import org.lfdecentralizedtrust.splice.http.v0.definitions.GetDsoInfoResponse
28+
import org.lfdecentralizedtrust.splice.http.v0.definitions.{
29+
GetDsoInfoResponse,
30+
UpdateHistoryItem,
31+
UpdateHistoryItemV2,
32+
}
2933
import org.lfdecentralizedtrust.splice.scan.{ScanApp, ScanAppBootstrap}
3034
import org.lfdecentralizedtrust.splice.scan.automation.ScanAutomationService
3135
import org.lfdecentralizedtrust.splice.scan.admin.api.client.commands.HttpScanAppClient
@@ -457,24 +461,39 @@ abstract class ScanAppReference(
457461
count: Int,
458462
after: Option[(Long, String)],
459463
lossless: Boolean,
460-
) = {
464+
): Seq[UpdateHistoryItem] = {
461465
consoleEnvironment.run {
462466
httpCommand(
463467
HttpScanAppClient.GetUpdateHistoryV0(count, after, lossless)
464468
)
465469
}
466470
}
471+
472+
@deprecated(message = "Use getUpdateHistory instead", since = "0.4.2")
473+
def getUpdateHistoryV1(
474+
count: Int,
475+
after: Option[(Long, String)],
476+
encoding: definitions.DamlValueEncoding,
477+
): Seq[UpdateHistoryItem] = {
478+
consoleEnvironment.run {
479+
httpCommand(
480+
HttpScanAppClient.GetUpdateHistoryV1(count, after, encoding)
481+
)
482+
}
483+
}
484+
467485
def getUpdateHistory(
468486
count: Int,
469487
after: Option[(Long, String)],
470488
encoding: definitions.DamlValueEncoding,
471-
) = {
489+
): Seq[UpdateHistoryItemV2] = {
472490
consoleEnvironment.run {
473491
httpCommand(
474-
HttpScanAppClient.GetUpdateHistory(count, after, encoding)
492+
HttpScanAppClient.GetUpdateHistoryV2(count, after, encoding)
475493
)
476494
}
477495
}
496+
478497
def getUpdate(updateId: String, encoding: definitions.DamlValueEncoding) = {
479498
consoleEnvironment.run {
480499
httpCommand(

apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/plugins/UpdateHistorySanityCheckPlugin.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import org.lfdecentralizedtrust.splice.config.SpliceConfig
77
import org.lfdecentralizedtrust.splice.console.ScanAppBackendReference
88
import org.lfdecentralizedtrust.splice.environment.SpliceEnvironment
99
import org.lfdecentralizedtrust.splice.http.v0.definitions.DamlValueEncoding.members.CompactJson
10-
import org.lfdecentralizedtrust.splice.http.v0.definitions.{AcsResponse, UpdateHistoryItem}
11-
import org.lfdecentralizedtrust.splice.http.v0.definitions.UpdateHistoryItem.members
10+
import org.lfdecentralizedtrust.splice.http.v0.definitions.{AcsResponse, UpdateHistoryItemV2}
11+
import org.lfdecentralizedtrust.splice.http.v0.definitions.UpdateHistoryItemV2.members
1212
import org.lfdecentralizedtrust.splice.http.v0.definitions.UpdateHistoryReassignment.Event.members as reassignmentMembers
1313
import org.lfdecentralizedtrust.splice.integration.tests.SpliceTests.SpliceTestConsoleEnvironment
1414
import org.lfdecentralizedtrust.splice.scan.automation.AcsSnapshotTrigger
@@ -90,13 +90,13 @@ class UpdateHistorySanityCheckPlugin(
9090
private def paginateHistory(
9191
scan: ScanAppBackendReference,
9292
after: Option[(Long, String)],
93-
acc: Chain[UpdateHistoryItem],
94-
): Chain[UpdateHistoryItem] = {
93+
acc: Chain[UpdateHistoryItemV2],
94+
): Chain[UpdateHistoryItemV2] = {
9595
val result = scan.getUpdateHistory(10, after, encoding = CompactJson)
9696
val newAcc = acc ++ Chain.fromSeq(result)
9797
result.lastOption match {
9898
case None => acc // done
99-
case Some(members.UpdateHistoryTransaction(last)) =>
99+
case Some(members.UpdateHistoryTransactionV2(last)) =>
100100
paginateHistory(
101101
scan,
102102
Some((last.migrationId, last.recordTime)),

apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/ScanHistoryBackfillingIntegrationTest.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,24 @@ class ScanHistoryBackfillingIntegrationTest
377377
}
378378

379379
clue("Compare scan histories with each other using the v1 HTTP endpoint") {
380+
// The v1 endpoint is deprecated, but we still have users using it
381+
@nowarn("cat=deprecation")
382+
val sv1HttpUpdates =
383+
sv1ScanBackend.getUpdateHistoryV1(1000, None, encoding = CompactJson)
384+
385+
@nowarn("cat=deprecation")
386+
val sv2HttpUpdates =
387+
sv2ScanBackend.getUpdateHistoryV1(1000, None, encoding = CompactJson)
388+
389+
// Compare common prefix, as there might be concurrent activity
390+
val commonLength = sv1HttpUpdates.length min sv2HttpUpdates.length
391+
commonLength should be > 10
392+
val sv1Items = sv1HttpUpdates.take(commonLength)
393+
val sv2Items = sv2HttpUpdates.take(commonLength)
394+
sv1Items should contain theSameElementsInOrderAs sv2Items
395+
}
396+
397+
clue("Compare scan histories with each other using the v2 HTTP endpoint") {
380398
val sv1HttpUpdates =
381399
readUpdateHistoryFromScan(sv1ScanBackend)
382400
val sv2HttpUpdates =

apps/scan/src/main/openapi/scan.yaml

Lines changed: 170 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,10 +267,79 @@ paths:
267267
application/json:
268268
schema:
269269
"$ref": "#/components/schemas/GetOpenAndIssuingMiningRoundsResponse"
270+
/v2/updates:
271+
post:
272+
tags: [external, scan]
273+
x-jvm-package: scan
274+
operationId: "getUpdateHistoryV2"
275+
description: |
276+
Returns the update history in ascending order, paged, from ledger begin or optionally starting after a record time.
277+
Compared to `/v1/updates`, the `/v2/updates` removes the `offset` field in responses,
278+
which was hardcoded to 1 in `/v1/updates` for compatibility, and is now removed.
279+
`/v2/updates` sorts events lexicographically in `events_by_id` by `ID` for convenience, which should not be confused with the
280+
order of events in the transaction, for this you should rely on the order of `root_event_ids` and `child_event_ids`.
281+
Updates are ordered lexicographically by `(migration id, record time)`.
282+
For a given migration id, each update has a unique record time.
283+
The record time ranges of different migrations may overlap, i.e.,
284+
it is not guaranteed that the maximum record time of one migration is smaller than the minimum record time of the next migration,
285+
and there may be two updates with the same record time but different migration ids.
286+
requestBody:
287+
required: true
288+
content:
289+
application/json:
290+
schema:
291+
"$ref": "#/components/schemas/UpdateHistoryRequestV2"
292+
responses:
293+
"200":
294+
description: ok
295+
content:
296+
application/json:
297+
schema:
298+
$ref: "#/components/schemas/UpdateHistoryResponseV2"
299+
"400":
300+
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/400"
301+
"500":
302+
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/500"
303+
304+
/v2/updates/{update_id}:
305+
get:
306+
tags: [external, scan]
307+
x-jvm-package: scan
308+
operationId: "getUpdateByIdV2"
309+
description: |
310+
Returns the update with the given update_id.
311+
Compared to `/v1/updates/{update_id}`, the `/v2/updates/{update_id}` removes the `offset` field in responses,
312+
which was hardcoded to 1 in `/v1/updates/{update_id}` for compatibility, and is now removed.
313+
`/v2/updates/{update_id}` sorts events lexicographically in `events_by_id` by `ID` for convenience, which should not be confused with the
314+
order of events in the transaction, for this you should rely on the order of `root_event_ids` and `child_event_ids`.
315+
parameters:
316+
- name: "update_id"
317+
in: "path"
318+
required: true
319+
schema:
320+
type: string
321+
- name: "daml_value_encoding"
322+
in: "query"
323+
schema:
324+
$ref: "#/components/schemas/DamlValueEncoding"
325+
responses:
326+
"200":
327+
description: ok
328+
content:
329+
application/json:
330+
schema:
331+
$ref: "#/components/schemas/UpdateHistoryItemV2"
332+
"400":
333+
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/400"
334+
"404":
335+
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/404"
336+
"500":
337+
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/500"
270338

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

304374
/v1/updates/{update_id}:
305375
get:
306-
tags: [external, scan]
376+
deprecated: true
377+
tags: [ deprecated ]
307378
x-jvm-package: scan
308379
operationId: "getUpdateByIdV1"
309380
description: |
310381
Returns the update with the given update_id.
311382
Unlike /v0/updates/{update_id}, this endpoint returns responses that are consistent across different
312383
scan instances. Event ids returned by this endpoint are not comparable to event ids returned by /v0/updates.
384+
The order of items in events_by_id is not defined.
313385
parameters:
314386
- name: "update_id"
315387
in: "path"
@@ -1387,7 +1459,7 @@ paths:
13871459
x-jvm-package: scan
13881460
operationId: "getUpdateHistory"
13891461
description: |
1390-
**Deprecated**, use /v1/updates instead.
1462+
**Deprecated**, use /v2/updates instead.
13911463
Returns the update history in ascending order, paged, from ledger begin or optionally starting after a record time.
13921464
requestBody:
13931465
required: true
@@ -1414,7 +1486,7 @@ paths:
14141486
x-jvm-package: scan
14151487
operationId: "getUpdateById"
14161488
description: |
1417-
**Deprecated**, use /v1/updates/{update_id} instead.
1489+
**Deprecated**, use /v2/updates/{update_id} instead.
14181490
parameters:
14191491
- name: "update_id"
14201492
in: "path"
@@ -2035,6 +2107,33 @@ components:
20352107
format: int32
20362108
daml_value_encoding:
20372109
$ref: "#/components/schemas/DamlValueEncoding"
2110+
UpdateHistoryRequestV2:
2111+
type: object
2112+
required:
2113+
- page_size
2114+
properties:
2115+
after:
2116+
$ref: "#/components/schemas/UpdateHistoryRequestAfter"
2117+
description: |
2118+
The transactions returned will either have a higher migration id or
2119+
the same migration id and a record_time greater than the migration id and record time
2120+
specified.
2121+
page_size:
2122+
description: |
2123+
The maximum number of transactions returned for this request.
2124+
type: integer
2125+
format: int32
2126+
daml_value_encoding:
2127+
$ref: "#/components/schemas/DamlValueEncoding"
2128+
UpdateHistoryResponseV2:
2129+
type: object
2130+
required:
2131+
- transactions
2132+
properties:
2133+
transactions:
2134+
type: array
2135+
items:
2136+
$ref: "#/components/schemas/UpdateHistoryItemV2"
20382137
UpdateHistoryResponse:
20392138
type: object
20402139
required:
@@ -2044,6 +2143,13 @@ components:
20442143
type: array
20452144
items:
20462145
$ref: "#/components/schemas/UpdateHistoryItem"
2146+
UpdateHistoryItemV2:
2147+
type: object
2148+
description: |
2149+
An individual item in the update history. May be a transaction or a contract reassignment.
2150+
oneOf:
2151+
- $ref: "#/components/schemas/UpdateHistoryTransactionV2"
2152+
- $ref: "#/components/schemas/UpdateHistoryReassignment"
20472153
UpdateHistoryItem:
20482154
type: object
20492155
description: |
@@ -2228,6 +2334,66 @@ components:
22282334
type: object
22292335
additionalProperties:
22302336
$ref: "#/components/schemas/TreeEvent"
2337+
UpdateHistoryTransactionV2:
2338+
type: object
2339+
required:
2340+
- update_id
2341+
- migration_id
2342+
- workflow_id
2343+
- command_id
2344+
- record_time
2345+
- synchronizer_id
2346+
- effective_at
2347+
- root_event_ids
2348+
- events_by_id
2349+
properties:
2350+
update_id:
2351+
description: |
2352+
The id of the update. This is not comparable to other updates; it's
2353+
meant for correlating with server logs.
2354+
type: string
2355+
migration_id:
2356+
description: |
2357+
The migration id of the synchronizer.
2358+
type: integer
2359+
format: int64
2360+
workflow_id:
2361+
description: |
2362+
This transaction's Daml workflow ID; a workflow ID can be associated
2363+
with multiple transactions. If empty, no workflow ID was set.
2364+
type: string
2365+
record_time:
2366+
description: |
2367+
The time at which the transaction was sequenced, with microsecond
2368+
resolution, using ISO-8601 representation.
2369+
type: string
2370+
synchronizer_id:
2371+
description: |
2372+
The id of the synchronizer through which this transaction was sequenced.
2373+
type: string
2374+
effective_at:
2375+
description: |
2376+
Ledger effective time, using ISO-8601 representation. This is the time
2377+
returned by `getTime` for all Daml executed as part of this transaction,
2378+
both by the submitting participant and all confirming participants.
2379+
type: string
2380+
root_event_ids:
2381+
description: |
2382+
Roots of the transaction tree. These are guaranteed to occur as keys
2383+
of the `events_by_id` object.
2384+
type: array
2385+
items:
2386+
type: string
2387+
events_by_id:
2388+
x-scala-map-type: "scala.collection.immutable.SortedMap"
2389+
description: |
2390+
Changes to the ledger that were caused by this transaction, keyed by ID and sorted lexicographically by ID for display consistency.
2391+
Values are nodes of the transaction tree.
2392+
Within a transaction, IDs may be referenced by `root_event_ids` or
2393+
`child_event_ids` in `ExercisedEvent` herein, which are sorted in the order as they occurred in the transaction.
2394+
type: object
2395+
additionalProperties:
2396+
$ref: "#/components/schemas/TreeEvent"
22312397
TreeEvent:
22322398
type: object
22332399
description: |

apps/scan/src/main/scala/org/lfdecentralizedtrust/splice/scan/admin/api/client/commands/HttpScanAppClient.scala

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1258,7 +1258,41 @@ object HttpScanAppClient {
12581258
}
12591259
}
12601260

1261-
case class GetUpdateHistory(
1261+
case class GetUpdateHistoryV2(
1262+
count: Int,
1263+
after: Option[(Long, String)],
1264+
damlValueEncoding: definitions.DamlValueEncoding,
1265+
) extends InternalBaseCommand[http.GetUpdateHistoryV2Response, Seq[
1266+
definitions.UpdateHistoryItemV2
1267+
]] {
1268+
override def submitRequest(
1269+
client: http.ScanClient,
1270+
headers: List[HttpHeader],
1271+
): EitherT[Future, Either[
1272+
Throwable,
1273+
HttpResponse,
1274+
], http.GetUpdateHistoryV2Response] = {
1275+
client.getUpdateHistoryV2(
1276+
// the request is the same as for V1
1277+
definitions.UpdateHistoryRequestV2(
1278+
after = after.map { case (migrationId, recordTime) =>
1279+
definitions.UpdateHistoryRequestAfter(migrationId, recordTime)
1280+
},
1281+
pageSize = count,
1282+
damlValueEncoding = Some(damlValueEncoding),
1283+
),
1284+
headers,
1285+
)
1286+
}
1287+
1288+
override def handleOk()(implicit decoder: TemplateJsonDecoder) = {
1289+
case http.GetUpdateHistoryV2Response.OK(response) =>
1290+
Right(response.transactions)
1291+
}
1292+
}
1293+
1294+
@deprecated(message = "Use GetUpdateHistory instead", since = "0.4.2")
1295+
case class GetUpdateHistoryV1(
12621296
count: Int,
12631297
after: Option[(Long, String)],
12641298
damlValueEncoding: definitions.DamlValueEncoding,

0 commit comments

Comments
 (0)