mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-19 16:57:40 +03:00
ts/ledger: package management (#8115)
This PR adds coverage for the package management endpoints of the JSON API. CHANGELOG_BEGIN - [JavaScript Client Libraries] The Ledger object (returned by `useLedger` through the React bindings) has three new methods covering the package management API: `listPackages` returns a list of all known packageIDs, `getPackage` returns the binary data of the corresponding DALF, and `uploadDarFile` takes binary data and uploads it to the ledger. Note that `uploadDarFile` requires admin access. CHANGELOG_END
This commit is contained in:
parent
0df1a79ee3
commit
5114fadd1d
@ -85,6 +85,7 @@ sh_test(
|
||||
"$(location //language-support/ts/daml-types:npm_package)",
|
||||
"$(location //language-support/ts/daml-ledger:npm_package)",
|
||||
sdk_version,
|
||||
"$(location //ledger/test-common:model-tests.dar)",
|
||||
],
|
||||
data = [
|
||||
"//:java",
|
||||
@ -93,6 +94,7 @@ sh_test(
|
||||
"//ledger/sandbox-classic:sandbox-classic-binary_deploy.jar",
|
||||
"//ledger-service/http-json:http-json-binary_deploy.jar",
|
||||
":build-and-lint.dar",
|
||||
"//ledger/test-common:model-tests.dar",
|
||||
"//language-support/ts/daml-types:npm_package",
|
||||
"//language-support/ts/daml-ledger:npm_package",
|
||||
] + glob(
|
||||
|
@ -46,6 +46,7 @@ TS_DIR=$(dirname $PACKAGE_JSON)
|
||||
DAML_TYPES=$(rlocation "$TEST_WORKSPACE/$8")
|
||||
DAML_LEDGER=$(rlocation "$TEST_WORKSPACE/$9")
|
||||
SDK_VERSION=${10}
|
||||
UPLOAD_DAR=$(rlocation "$TEST_WORKSPACE/${11}")
|
||||
|
||||
TMP_DAML_TYPES=$TMP_DIR/daml-types
|
||||
TMP_DAML_LEDGER=$TMP_DIR/daml-ledger
|
||||
@ -69,4 +70,4 @@ $YARN run build
|
||||
$YARN run lint
|
||||
# Invoke 'yarn test'. Control is thereby passed to
|
||||
# 'language-support/ts/codegen/tests/ts/build-and-lint-test/src/__tests__/test.ts'.
|
||||
JAVA=$JAVA SANDBOX=$SANDBOX JSON_API=$JSON_API DAR=$DAR $YARN test
|
||||
JAVA=$JAVA SANDBOX=$SANDBOX JSON_API=$JSON_API DAR=$DAR UPLOAD_DAR=$UPLOAD_DAR $YARN test
|
||||
|
@ -530,3 +530,41 @@ test('party API', async () => {
|
||||
expect(_.sortBy(allPartiesAfter)).toEqual(_.sortBy(["Alice", "Bob", "Dave", newParty1.identifier, newParty2.identifier]));
|
||||
|
||||
});
|
||||
|
||||
test('package API', async () => {
|
||||
// expect().toThrow does not seem to work with async thunk
|
||||
const expectFail = async <T>(p: Promise<T>): Promise<void> => {
|
||||
try {
|
||||
await p;
|
||||
expect(true).toBe(false);
|
||||
} catch (exc) {
|
||||
expect(exc.status).toBe(500);
|
||||
expect(exc.errors.length).toBe(1);
|
||||
}
|
||||
};
|
||||
const ledger = new Ledger({token: ALICE_TOKEN, httpBaseUrl: httpBaseUrl()});
|
||||
|
||||
const packagesBefore = await ledger.listPackages();
|
||||
|
||||
expect(packagesBefore).toEqual(expect.arrayContaining([buildAndLint.packageId]));
|
||||
expect(packagesBefore.length > 1).toBe(true);
|
||||
|
||||
const nonSense = Uint8Array.from([1, 2, 3, 4]);
|
||||
|
||||
await expectFail(ledger.uploadDarFile(nonSense));
|
||||
|
||||
const upDar = await fs.readFile(getEnv('UPLOAD_DAR'));
|
||||
// throws on error
|
||||
await ledger.uploadDarFile(upDar);
|
||||
|
||||
const packagesAfter = await ledger.listPackages();
|
||||
|
||||
expect(packagesAfter).toEqual(expect.arrayContaining([buildAndLint.packageId]));
|
||||
expect(packagesAfter.length > packagesBefore.length).toBe(true);
|
||||
expect(packagesAfter).toEqual(expect.arrayContaining(packagesBefore));
|
||||
|
||||
await expectFail(ledger.getPackage("non-sense"));
|
||||
|
||||
const downSuc = await ledger.getPackage(buildAndLint.packageId);
|
||||
expect(downSuc.byteLength > 0).toBe(true);
|
||||
});
|
||||
|
@ -112,18 +112,34 @@ format of the JSON API. See the [JSON API docs] for details.
|
||||
`getParties`
|
||||
------------
|
||||
|
||||
For a given list of party identifiers, returns full information, or null if
|
||||
For a given list of party identifiers, return full information, or null if
|
||||
the party doesn't exist.
|
||||
|
||||
`listKnownParties`
|
||||
------------------
|
||||
|
||||
Returns an array of PartyInfo for all parties on the ledger.
|
||||
Return an array of PartyInfo for all parties on the ledger.
|
||||
|
||||
`allocateParty`
|
||||
---------------
|
||||
|
||||
Allocates a new party.
|
||||
Allocate a new party.
|
||||
|
||||
`listPackages`
|
||||
--------------
|
||||
|
||||
Fetch a list of all known package IDs.
|
||||
|
||||
`getPackage`
|
||||
------------
|
||||
|
||||
Given a package ID, fetch the binary data for the corresponding DALF.
|
||||
|
||||
`uploadDarFile`
|
||||
---------------
|
||||
|
||||
Upload a given byte array as a DAR to the ledger. Note that this requires a
|
||||
token with admin access.
|
||||
|
||||
## Source
|
||||
|
||||
|
@ -24,6 +24,8 @@ const partyInfoDecoder: jtv.Decoder<PartyInfo> =
|
||||
isLocal: jtv.boolean(),
|
||||
});
|
||||
|
||||
export type PackageId = string;
|
||||
|
||||
const decode = <R>(decoder: jtv.Decoder<R>, data: unknown): R => {
|
||||
return jtv.Result.withException(decoder.run(data));
|
||||
}
|
||||
@ -313,6 +315,24 @@ class Ledger {
|
||||
this.reconnectThreshold = reconnectThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private auth(): {[headers: string]: string} {
|
||||
return {'Authorization': 'Bearer ' + this.token};
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private async throwOnError(r: Response): Promise<void> {
|
||||
if (!r.ok) {
|
||||
const json = await r.json();
|
||||
console.log(json);
|
||||
throw decode(decodeLedgerError, json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
@ -322,16 +342,13 @@ class Ledger {
|
||||
const httpResponse = await fetch(this.httpBaseUrl + endpoint, {
|
||||
body: JSON.stringify(payload),
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + this.token,
|
||||
...this.auth(),
|
||||
'Content-type': 'application/json'
|
||||
},
|
||||
method,
|
||||
});
|
||||
await this.throwOnError(httpResponse);
|
||||
const json = await httpResponse.json();
|
||||
if (!httpResponse.ok) {
|
||||
console.log(json);
|
||||
throw jtv.Result.withException(decodeLedgerError.run(json));
|
||||
}
|
||||
const ledgerResponse = jtv.Result.withException(decodeLedgerResponse.run(json));
|
||||
if (ledgerResponse.warnings) {
|
||||
console.warn(ledgerResponse.warnings);
|
||||
@ -953,6 +970,51 @@ class Ledger {
|
||||
return decode(partyInfoDecoder, json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a list of all package IDs from the ledger.
|
||||
*
|
||||
* @returns List of package IDs.
|
||||
*
|
||||
*/
|
||||
async listPackages(): Promise<PackageId[]> {
|
||||
const json = await this.submit('v1/packages', undefined, 'get');
|
||||
return decode(jtv.array(jtv.string()), json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a binary package.
|
||||
*
|
||||
* @returns The content of the package as a raw ArrayBuffer.
|
||||
*
|
||||
*/
|
||||
async getPackage(id: PackageId): Promise<ArrayBuffer> {
|
||||
const httpResponse = await fetch(this.httpBaseUrl + 'v1/packages/' + id, {
|
||||
headers: this.auth(),
|
||||
method: 'get',
|
||||
});
|
||||
await this.throwOnError(httpResponse);
|
||||
return await httpResponse.arrayBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a binary archive. Note that this requires admin privileges.
|
||||
*
|
||||
* @returns No return value on success; throws on error.
|
||||
*
|
||||
*/
|
||||
async uploadDarFile(abuf: ArrayBuffer): Promise<void> {
|
||||
const httpResponse = await fetch(this.httpBaseUrl + 'v1/packages', {
|
||||
body: abuf,
|
||||
headers: {
|
||||
...this.auth(),
|
||||
'Content-type': 'application/octet-stream'
|
||||
},
|
||||
method: 'post',
|
||||
});
|
||||
await this.throwOnError(httpResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Ledger;
|
||||
|
Loading…
Reference in New Issue
Block a user