diff --git a/examples/integration-scripts/multisig.ts b/examples/integration-scripts/multisig.ts index e6ff96b5..ea59b32a 100644 --- a/examples/integration-scripts/multisig.ts +++ b/examples/integration-scripts/multisig.ts @@ -191,22 +191,22 @@ async function run() { console.log("Multisig created!") const identifiers1 = await client1.identifiers().list() - assert.equal(identifiers1.length, 2) - assert.equal(identifiers1[0].name, "multisig") - assert.equal(identifiers1[1].name, "multisig1") + assert.equal(identifiers1.aids.length, 2) + assert.equal(identifiers1.aids[0].name, "multisig") + assert.equal(identifiers1.aids[1].name, "multisig1") const identifiers2 = await client2.identifiers().list() - assert.equal(identifiers2.length, 2) - assert.equal(identifiers2[0].name, "multisig") - assert.equal(identifiers2[1].name, "multisig2") + assert.equal(identifiers2.aids.length, 2) + assert.equal(identifiers2.aids[0].name, "multisig") + assert.equal(identifiers2.aids[1].name, "multisig2") const identifiers3 = await client3.identifiers().list() - assert.equal(identifiers3.length, 2) - assert.equal(identifiers3[0].name, "multisig") - assert.equal(identifiers3[1].name, "multisig3") + assert.equal(identifiers3.aids.length, 2) + assert.equal(identifiers3.aids[0].name, "multisig") + assert.equal(identifiers3.aids[1].name, "multisig3") - console.log("Client 1 identifiers:", identifiers1[0].name, identifiers1[1].name) - console.log("Client 2 identifiers:", identifiers2[0].name, identifiers2[1].name) - console.log("Client 3 identifiers:", identifiers3[0].name, identifiers3[1].name) + console.log("Client 1 identifiers:", identifiers1.aids[0].name, identifiers1.aids[1].name) + console.log("Client 2 identifiers:", identifiers2.aids[0].name, identifiers2.aids[1].name) + console.log("Client 3 identifiers:", identifiers3.aids[0].name, identifiers3.aids[1].name) } \ No newline at end of file diff --git a/examples/integration-scripts/package-lock.json b/examples/integration-scripts/package-lock.json index 7ff33420..d922441a 100644 --- a/examples/integration-scripts/package-lock.json +++ b/examples/integration-scripts/package-lock.json @@ -28,6 +28,7 @@ "buffer": "^6.0.3", "cbor": "^8.0.0", "collections": "^5.1.12", + "jest-fetch-mock": "^3.0.3", "libsodium-wrappers-sumo": "^0.7.9", "lodash": "^4.17.21", "mathjs": "^11.8.2", @@ -36,6 +37,7 @@ "text-encoding": "^0.7.0", "ts-node": "^10.9.1", "urlsafe-base64": "^1.0.0", + "whatwg-fetch": "^3.6.17", "xregexp": "^5.1.0" }, "devDependencies": { diff --git a/examples/integration-scripts/randy.ts b/examples/integration-scripts/randy.ts index 8c4e056d..bb5a20ac 100644 --- a/examples/integration-scripts/randy.ts +++ b/examples/integration-scripts/randy.ts @@ -38,8 +38,8 @@ async function run() { let aids = await client1.identifiers().list() - assert.equal(aids.length, 1) - aid = aids[0] + assert.equal(aids.aids.length, 1) + aid = aids.aids[0] assert.equal(aid.name, 'aid1') assert.equal(aid.prefix, icp.pre) @@ -51,8 +51,8 @@ async function run() { assert.deepEqual(ixn.ked['a'], [icp.pre]) aids = await client1.identifiers().list() - assert.equal(aids.length, 1) - aid = aids[0] + assert.equal(aids.aids.length, 1) + aid = aids.aids[0] const events = client1.keyEvents() let log = await events.get(aid["prefix"]) diff --git a/examples/integration-scripts/salty.ts b/examples/integration-scripts/salty.ts index 9663be85..35291b49 100644 --- a/examples/integration-scripts/salty.ts +++ b/examples/integration-scripts/salty.ts @@ -39,8 +39,8 @@ async function run() { assert.equal(icp.ked['kt'], '1') assert.equal(icp.ked['nt'], '1') let aids = await client1.identifiers().list() - assert.equal(aids.length, 1) - let aid = aids.pop() + assert.equal(aids.aids.length, 1) + let aid = aids.aids.pop() assert.equal(aid.name, 'aid1') let salt = aid.salty assert.equal(salt.pidx, 0) @@ -63,14 +63,30 @@ async function run() { assert.equal(icp2.ked['kt'], '2') assert.equal(icp2.ked['nt'], '2') aids = await client1.identifiers().list() - assert.equal(aids.length, 2) - aid = aids[1] + assert.equal(aids.aids.length, 2) + aid = aids.aids[1] assert.equal(aid.name, 'aid2') salt = aid.salty assert.equal(salt.pidx, 1) assert.equal(salt.stem, 'signify:aid') assert.equal(aid.prefix, icp2.pre) + await client1.identifiers().create('aid3') + aids = await client1.identifiers().list() + assert.equal(aids.aids.length, 3) + aid = aids.aids[0] + assert.equal(aid.name, 'aid1') + + aids = await client1.identifiers().list(1,2) + assert.equal(aids.aids.length, 2) + aid = aids.aids[0] + assert.equal(aid.name, 'aid2') + + aids = await client1.identifiers().list(2,2) + assert.equal(aids.aids.length, 1) + aid = aids.aids[0] + assert.equal(aid.name, 'aid3') + op = await client1.identifiers().rotate('aid1') assert.equal(op['done'], true) let ked = op['response'] diff --git a/examples/signify-react-ts/src/test_components/Randy.tsx b/examples/signify-react-ts/src/test_components/Randy.tsx index d8bf194e..e17ee4ab 100644 --- a/examples/signify-react-ts/src/test_components/Randy.tsx +++ b/examples/signify-react-ts/src/test_components/Randy.tsx @@ -30,7 +30,7 @@ export function Randy() { assert.equal(client.agent?.anchor, 'ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose') const identifiers = client.identifiers() let aids = await identifiers.list() - assert.equal(aids.length, 0) + assert.equal(aids.aids.length, 0) let op = await identifiers.create('aid1', {algo: Algos.randy}) assert.equal(op['done'], true) @@ -43,8 +43,8 @@ export function Randy() { aids = await identifiers.list() - assert.equal(aids.length, 1) - aid = aids[0] + assert.equal(aids.aids.length, 1) + aid = aids.aids[0] assert.equal(aid.name, 'aid1') assert.equal(aid.prefix, icp.pre) @@ -56,8 +56,8 @@ export function Randy() { assert.deepEqual(ixn.ked['a'], [icp.pre]) aids = await identifiers.list() - assert.equal(aids.length, 1) - aid = aids[0] + assert.equal(aids.aids.length, 1) + aid = aids.aids[0] const events = client.keyEvents() let log = await events.get(aid["prefix"]) diff --git a/examples/signify-react-ts/src/test_components/Rotation.tsx b/examples/signify-react-ts/src/test_components/Rotation.tsx index c29b57aa..09df7d5e 100644 --- a/examples/signify-react-ts/src/test_components/Rotation.tsx +++ b/examples/signify-react-ts/src/test_components/Rotation.tsx @@ -37,10 +37,10 @@ export function Rotation() { assert.equal(op_salt['done'], true) - let pres = await identifiers.list_identifiers() + let pres = await identifiers.list() let aids = [] for (let pre of pres) { - let _aid = await identifiers.get_identifier(pre.name) + let _aid = await identifiers.get(pre.name) aids.push(_aid) } client.rotate('1111123456789abcdefghijk', aids) diff --git a/examples/signify-react-ts/src/test_components/Witnesses.tsx b/examples/signify-react-ts/src/test_components/Witnesses.tsx index 9970d2a5..49197b13 100644 --- a/examples/signify-react-ts/src/test_components/Witnesses.tsx +++ b/examples/signify-react-ts/src/test_components/Witnesses.tsx @@ -26,7 +26,7 @@ export function Witnesses() { const identifiers = client.identifiers() const operations = client.operations() let aids = await identifiers.list() - assert.equal(aids.length, 0) + assert.equal(aids.aids.length, 0) let op = await identifiers.create('aid1', { bran: 'canIGetAWitnessSaltGreaterThan21', @@ -57,8 +57,8 @@ export function Witnesses() { assert.equal(aid1.windexes.length, 3) aids = await identifiers.list() - assert.equal(aids.length, 1) - const aid = aids.pop() + assert.equal(aids.aids.length, 1) + const aid = aids.aids.pop() assert.equal(aid.prefix, icp1.pre) setTestResult("Passed") diff --git a/src/keri/app/signify.ts b/src/keri/app/signify.ts index 7bd30b28..569842fa 100644 --- a/src/keri/app/signify.ts +++ b/src/keri/app/signify.ts @@ -13,6 +13,7 @@ import { Siger } from "../core/siger" import { Prefixer } from "../core/prefixer" import { Salter } from "../core/salter" import { randomNonce } from "./apping" +import { parseRangeHeaders } from "../core/httping" const DEFAULT_BOOT_URL = "http://localhost:3903" @@ -176,17 +177,15 @@ export class SignifyClient { }) if (extraHeaders !== undefined) { extraHeaders.forEach((value, key) => { - final_headers.set(key, value) + final_headers.append(key, value) }) } - let res = await fetch(this.url + path, { method: method, body: _body, headers: final_headers }); - - if (!(res.status == 200 || res.status == 202)) { + if (!(res.status == 200 || res.status == 202 || res.status == 206)) { const error = await res.text() throw new Error(error) } @@ -477,14 +476,29 @@ export class Identifier { /** * List managed identifiers * @async + * @param {number} [start=0] Start index of list of notifications, defaults to 0 + * @param {number} [end=24] End index of list of notifications, defaults to 24 * @returns {Promise} A promise to the list of managed identifiers */ - async list(): Promise { + async list(start:number=0, end:number=24): Promise { + let extraHeaders = new Headers() + extraHeaders.append('Range', `aids=${start}-${end}`) + let path = `/identifiers` let data = null let method = 'GET' - let res = await this.client.fetch(path, method, data) - return await res.json() + let res = await this.client.fetch(path, method, data, extraHeaders) + + let cr = res.headers.get('content-range') + let range = parseRangeHeaders(cr,"aids") + let aids = await res.json() + + return { + start: range.start, + end: range.end, + total: range.total, + aids: aids + } } /** @@ -1704,19 +1718,28 @@ export class Notifications { /** * List notifications * @async - * @param {string} last SAID of the last notification received - * @param {number} limit number of notifications to return + * @param {number} [start=0] Start index of list of notifications, defaults to 0 + * @param {number} [end=24] End index of list of notifications, defaults to 24 * @returns {Promise} A promise to the list of notifications */ - async list(last?:string, limit?:number): Promise { - let params = new URLSearchParams() - if (last !== undefined) {params.append('last', last)} - if (limit !== undefined) {params.append('limit', limit.toString())} - - let path = `/notifications` + '?' + params.toString() + async list(start:number=0, end:number=24): Promise { + let extraHeaders = new Headers() + extraHeaders.append('Range', `aids=${start}-${end}`) + + let path = `/notifications` let method = 'GET' - let res = await this.client.fetch(path, method, null) - return await res.json() + let res = await this.client.fetch(path, method, null, extraHeaders) + + let cr = res.headers.get('content-range') + let range = parseRangeHeaders(cr,"notes") + let notes = await res.json() + + return { + start: range.start, + end: range.end, + total: range.total, + notes: notes + } } /** @@ -1738,7 +1761,7 @@ export class Notifications { * @param {string} said SAID of the notification * @returns {Promise} A promise to the result of the deletion */ - async delete(said:string) { + async delete(said:string): Promise { let path = `/notifications/`+ said let method = 'DELETE' let res = await this.client.fetch(path, method, null) diff --git a/src/keri/core/httping.ts b/src/keri/core/httping.ts index 80c03a35..b0548aea 100644 --- a/src/keri/core/httping.ts +++ b/src/keri/core/httping.ts @@ -160,4 +160,21 @@ export function desiginput(value: string): Array { return siginputs +} +/** Parse start, end and total from HTTP Content-Range header value + * @param {string|null} header - HTTP Range header value + * @param {string} typ - type of range, e.g. "aids" + * @returns {start: number, end: number, total: number} - object with start, end and total properties +*/ +export function parseRangeHeaders(header: string|null, typ: string): {start: number, end: number, total: number} { + if (header !== null) { + let data = header.replace(`${typ} `, "") + let values = data.split("/") + let rng = values[0].split("-") + + return {start: parseInt(rng[0]), end: parseInt(rng[1]), total: parseInt(values[1])} + } else { + return {start: 0, end: 0, total: 0} + } + } \ No newline at end of file