From a97d716d7cf7359f9cdbd5f8092b2791df6f1abe Mon Sep 17 00:00:00 2001 From: Marcus Pousette Date: Mon, 17 Feb 2025 17:53:00 +0100 Subject: [PATCH] fix: respect canOpen on re-opening programs on program restart --- .../data/document/document/src/program.ts | 20 +++++---- .../data/document/document/src/search.ts | 8 +++- .../data/document/document/test/index.spec.ts | 41 +++++++++++++++---- 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/packages/programs/data/document/document/src/program.ts b/packages/programs/data/document/document/src/program.ts index 24b994d57..7b13756bf 100644 --- a/packages/programs/data/document/document/src/program.ts +++ b/packages/programs/data/document/document/src/program.ts @@ -125,7 +125,7 @@ export class Documents< private idResolver!: (any: any) => indexerTypes.IdPrimitive; private domain?: CustomDocumentDomain>; - canOpen?: (program: T, entry: Entry) => Promise | boolean; + canOpen?: (program: T) => Promise | boolean; compatibility: 6 | 7 | undefined; @@ -145,6 +145,16 @@ export class Documents< return this._index; } + private async maybeSubprogramOpen(value: T & Program): Promise { + if (await this.canOpen!(value)) { + return (await this.node.open(value, { + parent: this as Program, + existing: "reuse", + })) as any as T & Program; // TODO types + } + + return value; + } async open(options: SetupOptions) { this._clazz = options.type; this.canOpen = options.canOpen; @@ -195,6 +205,7 @@ export class Documents< ); }, dbType: this.constructor, + maybeOpen: this.maybeSubprogramOpen.bind(this), }); // document v6 and below need log compatibility of v8 or below @@ -594,12 +605,7 @@ export class Documents< // Program specific if (value instanceof Program) { // if replicator, then open - if (await this.canOpen!(value, item)) { - value = (await this.node.open(value, { - parent: this as Program, - existing: "reuse", - })) as any as T; // TODO types - } + value = await this.maybeSubprogramOpen(value); } documentsChanged.added.push(value); await this._index.put(value, item, key); diff --git a/packages/programs/data/document/document/src/search.ts b/packages/programs/data/document/document/src/search.ts index 53fcc710d..6301fef1f 100644 --- a/packages/programs/data/document/document/src/search.ts +++ b/packages/programs/data/document/document/src/search.ts @@ -321,6 +321,7 @@ export type OpenOptions< transform?: TransformOptions; cacheSize?: number; compatibility: 6 | 7 | 8 | undefined; + maybeOpen: (value: T & Program) => Promise; }; type IndexableClass = new ( @@ -374,6 +375,7 @@ export class DocumentIndex< private _resolverProgramCache?: Map; private _resolverCache?: Cache; private isProgramValued: boolean; + private _maybeOpen: (value: T & Program) => Promise; private _resultQueue: Map< string, @@ -474,7 +476,7 @@ export class DocumentIndex< })) || new HashmapIndex>(); this._resumableIterators = new ResumableIterators(this.index); - + this._maybeOpen = properties.maybeOpen; if (this.isProgramValued) { this._resolverProgramCache = new Map(); } @@ -545,8 +547,10 @@ export class DocumentIndex< ); continue; } + programValue.value = await this._maybeOpen( + programValue.value as Program & T, + ); this._resolverProgramCache.set(id.primitive, programValue.value as T); - await this.node.open(programValue.value as Program); } } return super.afterOpen(); diff --git a/packages/programs/data/document/document/test/index.spec.ts b/packages/programs/data/document/document/test/index.spec.ts index 28eaeb1b9..fcadf6dca 100644 --- a/packages/programs/data/document/document/test/index.spec.ts +++ b/packages/programs/data/document/document/test/index.spec.ts @@ -10,6 +10,7 @@ import { AccessError, Ed25519PublicKey, type PublicSignKey, + equals, randomBytes, toBase64, } from "@peerbit/crypto"; @@ -3070,34 +3071,60 @@ describe("index", () => { directory: "./tmp/document-store/program-perstance-test/" + new Date(), }); const peer = session.peers[0]; + + const subProgram1 = new SubProgram(); + + const subProgram2 = new SubProgram(); + let store = await peer.open( new TestStoreSubPrograms({ docs: new Documents(), }), { args: { - canOpen: () => true, + canOpen: (d) => equals(d.id, subProgram1.id), }, }, ); - const subProgram = new SubProgram(); - await store.docs.put(subProgram); - expect(subProgram.closed).to.be.false; + await store.docs.put(subProgram1); + await store.docs.put(subProgram2); + + expect(subProgram1.closed).to.be.false; + expect(subProgram2.closed).to.be.true; await session.peers[0].stop(); + + expect(subProgram1.closed).to.be.true; + expect(subProgram2.closed).to.be.true; + await session.peers[0].start(); store = await peer.open(store.clone(), { args: { - canOpen: () => true, + canOpen: (d) => equals(d.id, subProgram1.id), }, }); const programsInIndex = await store.docs.index .iterate({}, { local: true, remote: false }) .all(); - expect(programsInIndex).to.have.length(1); - expect(programsInIndex[0].closed).to.be.false; + expect(programsInIndex).to.have.length(2); + expect( + programsInIndex + .map((x) => x.closed) + .sort((a, b) => String(a).localeCompare(String(b))), + ).to.deep.eq([false, true]); // one is allowed to be opened and one is not + + // open all, and make sure that if we query all again, they are all open + for (const program of programsInIndex) { + program.closed && (await peer.open(program)); + } + + const programsInIndex2 = await store.docs.index + .iterate({}, { local: true, remote: false }) + .all(); + expect(programsInIndex2).to.have.length(2); + expect(programsInIndex2.map((x) => x.closed)).to.deep.eq([false, false]); }); describe("index", () => {