mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 09:17:43 +03:00
daml2ts: Support lookupByKey (#3987)
* daml2ts: Support lookupByKey We'll add `fetchByKey` and `exerciseByKey` in a separate PR. We'll remove the `pseudo*` methods in another PR after that one. CHANGELOG_BEGIN CHANGELOG_END * daml2ts: make `lookupByKey` inaccessible for templates without key
This commit is contained in:
parent
49b90bfb36
commit
20ad5263d4
@ -168,12 +168,19 @@ genDefDataType curModName tpls def = case unTypeConName (dataTypeCon def) of
|
||||
, let (rtyp, rser) = genType curModName rLf
|
||||
, let argRefs = Set.setOf typeModuleRef tLf
|
||||
]
|
||||
(keyTypeTs, keySer) = case tplKey tpl of
|
||||
Nothing -> ("undefined", "() => jtv.constant(undefined)")
|
||||
Just key ->
|
||||
let (keyTypeTs, keySer) = genType curModName (tplKeyType key)
|
||||
in
|
||||
(keyTypeTs, "() => " <> keySer <> ".decoder()")
|
||||
dict =
|
||||
["export const " <> conName <> ": daml.Template<" <> conName <> "> & {"] ++
|
||||
["export const " <> conName <> ": daml.Template<" <> conName <> ", " <> keyTypeTs <> "> & {"] ++
|
||||
[" " <> x <> ": daml.Choice<" <> conName <> ", " <> t <> ", " <> rtyp <> " >;" | (x, t, rtyp, _) <- chcs] ++
|
||||
["} = {"
|
||||
] ++
|
||||
[" templateId: templateId('" <> conName <> "'),"
|
||||
," keyDecoder: " <> keySer <> ","
|
||||
] ++
|
||||
map (" " <>) (onHead ("decoder: " <>) serDesc) ++
|
||||
concat
|
||||
|
@ -38,6 +38,9 @@ template Person with
|
||||
where
|
||||
signatory party
|
||||
|
||||
key (party, age): (Party, Int)
|
||||
maintainer key._1
|
||||
|
||||
choice Birthday: ContractId Person
|
||||
controller party
|
||||
do
|
||||
|
@ -32,8 +32,9 @@ export type TemplateId = {
|
||||
* Interface for objects representing DAML templates. It is similar to the
|
||||
* `Template` type class in DAML.
|
||||
*/
|
||||
export interface Template<T extends object> extends Serializable<T> {
|
||||
export interface Template<T extends object, K = unknown> extends Serializable<T> {
|
||||
templateId: TemplateId;
|
||||
keyDecoder: () => jtv.Decoder<K>;
|
||||
Archive: Choice<T, {}, {}>;
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,13 @@ import { Choice, ContractId, List, Party, Template, TemplateId, Text, lookupTemp
|
||||
import * as jtv from '@mojotech/json-type-validation';
|
||||
import fetch from 'cross-fetch';
|
||||
|
||||
export type CreateEvent<T extends object> = {
|
||||
export type CreateEvent<T extends object, K = unknown> = {
|
||||
templateId: TemplateId;
|
||||
contractId: ContractId<T>;
|
||||
signatories: List<Party>;
|
||||
observers: List<Party>;
|
||||
agreementText: Text;
|
||||
key: unknown;
|
||||
key: K;
|
||||
payload: T;
|
||||
}
|
||||
|
||||
@ -19,8 +19,8 @@ export type ArchiveEvent<T extends object> = {
|
||||
contractId: ContractId<T>;
|
||||
}
|
||||
|
||||
export type Event<T extends object> =
|
||||
| { created: CreateEvent<T> }
|
||||
export type Event<T extends object, K = unknown> =
|
||||
| { created: CreateEvent<T, K> }
|
||||
| { archived: ArchiveEvent<T> }
|
||||
|
||||
const decodeTemplateId: jtv.Decoder<TemplateId> = jtv.object({
|
||||
@ -29,13 +29,13 @@ const decodeTemplateId: jtv.Decoder<TemplateId> = jtv.object({
|
||||
entityName: jtv.string(),
|
||||
});
|
||||
|
||||
const decodeCreateEvent = <T extends object>(template: Template<T>): jtv.Decoder<CreateEvent<T>> => jtv.object({
|
||||
const decodeCreateEvent = <T extends object, K>(template: Template<T, K>): jtv.Decoder<CreateEvent<T, K>> => jtv.object({
|
||||
templateId: decodeTemplateId,
|
||||
contractId: ContractId(template).decoder(),
|
||||
signatories: List(Party).decoder(),
|
||||
observers: List(Party).decoder(),
|
||||
agreementText: Text.decoder(),
|
||||
key: jtv.unknownJson(),
|
||||
key: template.keyDecoder(),
|
||||
payload: template.decoder(),
|
||||
});
|
||||
|
||||
@ -132,7 +132,7 @@ class Ledger {
|
||||
* https://github.com/digital-asset/daml/blob/master/docs/source/json-api/search-query-language.rst
|
||||
* for a description of the query language.
|
||||
*/
|
||||
async query<T extends object>(template: Template<T>, query: Query<T>): Promise<CreateEvent<T>[]> {
|
||||
async query<T extends object, K>(template: Template<T, K>, query: Query<T>): Promise<CreateEvent<T, K>[]> {
|
||||
const payload = {"%templates": [template.templateId], ...query};
|
||||
const json = await this.submit('contracts/search', payload);
|
||||
return jtv.Result.withException(jtv.array(decodeCreateEvent(template)).run(json));
|
||||
@ -141,15 +141,24 @@ class Ledger {
|
||||
/**
|
||||
* Retrieve all contracts for a given template.
|
||||
*/
|
||||
async fetchAll<T extends object>(template: Template<T>): Promise<CreateEvent<T>[]> {
|
||||
async fetchAll<T extends object, K>(template: Template<T, K>): Promise<CreateEvent<T, K>[]> {
|
||||
return this.query(template, {} as Query<T>);
|
||||
}
|
||||
|
||||
async lookupByKey<T extends object, K>(template: Template<T, K>, key: K extends undefined ? never : K): Promise<CreateEvent<T, K> | null> {
|
||||
const payload = {
|
||||
templateId: template.templateId,
|
||||
key,
|
||||
};
|
||||
const json = await this.submit('contracts/lookup', payload);
|
||||
return jtv.Result.withException(jtv.oneOf(jtv.constant(null), decodeCreateEvent(template)).run(json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimic DAML's `lookupByKey`. The `key` must be a formulation of the
|
||||
* contract key as a query.
|
||||
*/
|
||||
async pseudoLookupByKey<T extends object>(template: Template<T>, key: Query<T>): Promise<CreateEvent<T> | undefined> {
|
||||
async pseudoLookupByKey<T extends object, K>(template: Template<T, K>, key: Query<T>): Promise<CreateEvent<T, K> | undefined> {
|
||||
const contracts = await this.query(template, key);
|
||||
if (contracts.length > 1) {
|
||||
throw Error("pseudoLookupByKey: query returned multiple contracts");
|
||||
@ -161,7 +170,7 @@ class Ledger {
|
||||
* Mimic DAML's `fetchByKey`. The `key` must be a formulation of the
|
||||
* contract key as a query.
|
||||
*/
|
||||
async pseudoFetchByKey<T extends object>(template: Template<T>, key: Query<T>): Promise<CreateEvent<T>> {
|
||||
async pseudoFetchByKey<T extends object, K>(template: Template<T, K>, key: Query<T>): Promise<CreateEvent<T, K>> {
|
||||
const contract = await this.pseudoLookupByKey(template, key);
|
||||
if (contract === undefined) {
|
||||
throw Error("pseudoFetchByKey: query returned no contract");
|
||||
@ -172,7 +181,7 @@ class Ledger {
|
||||
/**
|
||||
* Create a contract for a given template.
|
||||
*/
|
||||
async create<T extends object>(template: Template<T>, argument: T): Promise<CreateEvent<T>> {
|
||||
async create<T extends object, K>(template: Template<T, K>, argument: T): Promise<CreateEvent<T, K>> {
|
||||
const payload = {
|
||||
templateId: template.templateId,
|
||||
argument,
|
||||
|
@ -72,13 +72,21 @@ test('create + fetch & exercise', async () => {
|
||||
party: ALICE_PARTY,
|
||||
age: '5',
|
||||
};
|
||||
const aliceKey = {_1: alice.party, _2: alice.age};
|
||||
const aliceContract = await ledger.create(Main.Person, alice);
|
||||
expect(aliceContract.payload).toEqual(alice);
|
||||
expect(aliceContract.key).toEqual(aliceKey);
|
||||
|
||||
const personContracts = await ledger.fetchAll(Main.Person);
|
||||
expect(personContracts).toHaveLength(1);
|
||||
expect(personContracts[0]).toEqual(aliceContract);
|
||||
|
||||
const aliceContractByKey = await ledger.lookupByKey(Main.Person, aliceKey);
|
||||
expect(aliceContractByKey).toEqual(aliceContract);
|
||||
|
||||
const bobByKey = await ledger.lookupByKey(Main.Person, {_1: 'Bob', _2: '4'});
|
||||
expect(bobByKey).toBeNull();
|
||||
|
||||
// Alice has a birthday.
|
||||
const [er, es] = await ledger.exercise(Main.Person.Birthday, aliceContract.contractId, {});
|
||||
// Resulting in her old record being archived and replaced with a new one.
|
||||
@ -114,6 +122,7 @@ test('create + fetch & exercise', async () => {
|
||||
};
|
||||
const allTypesContract = await ledger.create(Main.AllTypes, allTypes);
|
||||
expect(allTypesContract.payload).toEqual(allTypes);
|
||||
expect(allTypesContract.key).toBeUndefined();
|
||||
|
||||
const allTypesContracts = await ledger.fetchAll(Main.AllTypes);
|
||||
expect(allTypesContracts).toHaveLength(1);
|
||||
|
Loading…
Reference in New Issue
Block a user