From d8af733ea3ad5e29c8aa2872f863223d41815c1c Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 21 Nov 2024 13:37:26 +0100 Subject: [PATCH] feat: list canister snapshots (#765) # Motivation We continue with the [list_canister_snapshots](https://internetcomputer.org/docs/current/references/ic-interface-spec#ic-list_canister_snapshots). # Changes - Add `listCanisterSnapshots` to ic-mgmt --------- Signed-off-by: David Dal Busco Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- packages/ic-management/README.md | 50 +++++++++----- .../src/ic-management.canister.spec.ts | 67 +++++++++++++++++++ .../src/ic-management.canister.ts | 25 +++++++ packages/ic-management/src/index.ts | 1 + 4 files changed, 126 insertions(+), 17 deletions(-) diff --git a/packages/ic-management/README.md b/packages/ic-management/README.md index 97f1b3a0..e933eb0a 100644 --- a/packages/ic-management/README.md +++ b/packages/ic-management/README.md @@ -54,7 +54,7 @@ const { status, memory_size, ...rest } = await canisterStatus(YOUR_CANISTER_ID); ### :factory: ICManagementCanister -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L38) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L39) #### Methods @@ -74,6 +74,7 @@ const { status, memory_size, ...rest } = await canisterStatus(YOUR_CANISTER_ID); - [provisionalCreateCanisterWithCycles](#gear-provisionalcreatecanisterwithcycles) - [fetchCanisterLogs](#gear-fetchcanisterlogs) - [takeCanisterSnapshot](#gear-takecanistersnapshot) +- [listCanisterSnapshots](#gear-listcanistersnapshots) ##### :gear: create @@ -81,7 +82,7 @@ const { status, memory_size, ...rest } = await canisterStatus(YOUR_CANISTER_ID); | -------- | ---------------------------------------------------------------- | | `create` | `(options: ICManagementCanisterOptions) => ICManagementCanister` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L43) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L44) ##### :gear: createCanister @@ -91,7 +92,7 @@ Create a new canister | ---------------- | ------------------------------------------------------------------------------------- | | `createCanister` | `({ settings, senderCanisterVersion, }?: CreateCanisterParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L83) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L84) ##### :gear: updateSettings @@ -101,7 +102,7 @@ Update canister settings | ---------------- | ------------------------------------------------------------------------------------------- | | `updateSettings` | `({ canisterId, senderCanisterVersion, settings, }: UpdateSettingsParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L106) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L107) ##### :gear: installCode @@ -111,7 +112,7 @@ Install code to a canister | ------------- | ----------------------------------------------------------------------------------------------------- | | `installCode` | `({ mode, canisterId, wasmModule, arg, senderCanisterVersion, }: InstallCodeParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L131) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L132) ##### :gear: uploadChunk @@ -126,7 +127,7 @@ Parameters: - `params.canisterId`: The canister in which the chunks will be stored. - `params.chunk`: A chunk of Wasm module. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L159) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L160) ##### :gear: clearChunkStore @@ -140,7 +141,7 @@ Parameters: - `params.canisterId`: The canister in which the chunks are stored. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L179) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L180) ##### :gear: storedChunks @@ -154,7 +155,7 @@ Parameters: - `params.canisterId`: The canister in which the chunks are stored. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L198) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L199) ##### :gear: installChunkedCode @@ -174,7 +175,7 @@ Parameters: - `params.storeCanisterId`: Specifies the canister in whose chunk storage the chunks are stored (this parameter defaults to target_canister if not specified). - `params.wasmModuleHash`: The Wasm module hash as hex string. Used to check that the SHA-256 hash of wasm_module is equal to the wasm_module_hash parameter and can calls install_code with parameters. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L223) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L224) ##### :gear: uninstallCode @@ -184,7 +185,7 @@ Uninstall code from a canister | --------------- | -------------------------------------------------------------------------------- | | `uninstallCode` | `({ canisterId, senderCanisterVersion, }: UninstallCodeParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L256) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L257) ##### :gear: startCanister @@ -194,7 +195,7 @@ Start a canister | --------------- | ------------------------------------------ | | `startCanister` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L274) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L275) ##### :gear: stopCanister @@ -204,7 +205,7 @@ Stop a canister | -------------- | ------------------------------------------ | | `stopCanister` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L286) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L287) ##### :gear: canisterStatus @@ -214,7 +215,7 @@ Get canister details (memory size, status, etc.) | ---------------- | ------------------------------------------------------------ | | `canisterStatus` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L297) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L298) ##### :gear: deleteCanister @@ -224,7 +225,7 @@ Deletes a canister | ---------------- | ------------------------------------------ | | `deleteCanister` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L311) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L312) ##### :gear: provisionalCreateCanisterWithCycles @@ -234,7 +235,7 @@ Creates a canister. Only available on development instances. | ------------------------------------- | ------------------------------------------------------------------------------------------------------- | | `provisionalCreateCanisterWithCycles` | `({ settings, amount, canisterId, }?: ProvisionalCreateCanisterWithCyclesParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L326) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L327) ##### :gear: fetchCanisterLogs @@ -244,7 +245,7 @@ Given a canister ID as input, this method returns a vector of logs of that canis | ------------------- | ---------------------------------------------------------------- | | `fetchCanisterLogs` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L349) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L350) ##### :gear: takeCanisterSnapshot @@ -262,7 +263,22 @@ Parameters: Can be provided as a `string` or a `Uint8Array`. If not provided, a new snapshot will be created. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L375) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L376) + +##### :gear: listCanisterSnapshots + +Lists the snapshots of a canister. + +| Method | Type | +| ----------------------- | ------------------------------------------------------------------------------------------ | +| `listCanisterSnapshots` | `({ canisterId, }: { canisterId: Principal; }) => Promise` | + +Parameters: + +- `params`: - Parameters for the listing operation. +- `params.canisterId`: - The ID of the canister for which snapshots will be listed. + +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L405) diff --git a/packages/ic-management/src/ic-management.canister.spec.ts b/packages/ic-management/src/ic-management.canister.spec.ts index e8409dbd..469cf586 100644 --- a/packages/ic-management/src/ic-management.canister.spec.ts +++ b/packages/ic-management/src/ic-management.canister.spec.ts @@ -4,6 +4,7 @@ import { mock } from "jest-mock-extended"; import type { _SERVICE as IcManagementService, chunk_hash, + list_canister_snapshots_result, take_canister_snapshot_result, } from "../candid/ic-management"; import { ICManagementCanister } from "./ic-management.canister"; @@ -805,4 +806,70 @@ describe("ICManagementCanister", () => { await expect(call).rejects.toThrow(error); }); }); + + describe("listCanisterSnapshots", () => { + const mockSnapshots = [ + { + id: Uint8Array.from([1]), + total_size: 5000n, + taken_at_timestamp: 123n, + }, + { + id: Uint8Array.from([2]), + total_size: 666n, + taken_at_timestamp: 456n, + }, + ]; + + const mockResponse: list_canister_snapshots_result = mockSnapshots; + + it("should return a list of snapshots for a canister", async () => { + const service = mock(); + service.list_canister_snapshots.mockResolvedValue(mockResponse); + + const icManagement = await createICManagement(service); + + const res = await icManagement.listCanisterSnapshots({ + canisterId: mockCanisterId, + }); + + expect(res).toEqual(mockSnapshots); + + expect(service.list_canister_snapshots).toHaveBeenCalledWith({ + canister_id: mockCanisterId, + }); + }); + + it("should handle no snapshots and return an empty array", async () => { + const service = mock(); + service.list_canister_snapshots.mockResolvedValue([]); + + const icManagement = await createICManagement(service); + + const res = await icManagement.listCanisterSnapshots({ + canisterId: mockCanisterId, + }); + + expect(res).toEqual([]); + + expect(service.list_canister_snapshots).toHaveBeenCalledWith({ + canister_id: mockCanisterId, + }); + }); + + it("should throw an error if list_canister_snapshots fails", async () => { + const error = new Error("Test error"); + const service = mock(); + service.list_canister_snapshots.mockRejectedValue(error); + + const icManagement = await createICManagement(service); + + const call = () => + icManagement.listCanisterSnapshots({ + canisterId: mockCanisterId, + }); + + await expect(call).rejects.toThrow(error); + }); + }); }); diff --git a/packages/ic-management/src/ic-management.canister.ts b/packages/ic-management/src/ic-management.canister.ts index a14e872d..583a7899 100644 --- a/packages/ic-management/src/ic-management.canister.ts +++ b/packages/ic-management/src/ic-management.canister.ts @@ -9,6 +9,7 @@ import { import type { _SERVICE as IcManagementService, chunk_hash, + list_canister_snapshots_result, snapshot_id, take_canister_snapshot_result, } from "../candid/ic-management"; @@ -388,4 +389,28 @@ export class ICManagementCanister { ), }); }; + + /** + * Lists the snapshots of a canister. + * + * @link https://internetcomputer.org/docs/current/references/ic-interface-spec#ic-list_canister_snapshots + * + * @param {Object} params - Parameters for the listing operation. + * @param {Principal} params.canisterId - The ID of the canister for which snapshots will be listed. + * + * @returns {Promise} A promise that resolves with the list of snapshots. + * + * @throws {Error} If the operation fails. + */ + listCanisterSnapshots = async ({ + canisterId, + }: { + canisterId: Principal; + }): Promise => { + const { list_canister_snapshots } = this.service; + + return list_canister_snapshots({ + canister_id: canisterId, + }); + }; } diff --git a/packages/ic-management/src/index.ts b/packages/ic-management/src/index.ts index c0ef371a..8980d299 100644 --- a/packages/ic-management/src/index.ts +++ b/packages/ic-management/src/index.ts @@ -4,6 +4,7 @@ export type { chunk_hash, definite_canister_settings, fetch_canister_logs_result, + list_canister_snapshots_result, log_visibility, snapshot_id, } from "../candid/ic-management";