diff --git a/packages/model-client/src/index.ts b/packages/model-client/src/index.ts index 4d2883a..d5d8adf 100644 --- a/packages/model-client/src/index.ts +++ b/packages/model-client/src/index.ts @@ -3,6 +3,7 @@ import { SignedEvent, createSignedInitEvent, decodeMultibaseToJSON, + decodeMultibaseToStreamID, eventToContainer, } from '@ceramic-sdk/events' import { StreamID } from '@ceramic-sdk/identifiers' @@ -69,11 +70,21 @@ export class ModelClient extends StreamClient { return getModelStreamID(cid) } + /** Retrieve the stringified model stream ID from a stream */ + async getDocumentModel(streamID: StreamID | string): Promise { + const id = + typeof streamID === 'string' ? StreamID.fromString(streamID) : streamID + const streamState = await this.getStreamState(id) + const stream = decodeMultibaseToStreamID(streamState.dimensions.model) + return stream.toString() + } + /** Retrieve a model's JSON definition */ async getModelDefinition( streamID: StreamID | string, ): Promise { - const id = typeof streamID === 'string' ? streamID : streamID.toString() // Convert StreamID to string + const id = + typeof streamID === 'string' ? StreamID.fromString(streamID) : streamID const streamState = await this.getStreamState(id) const decodedData = decodeMultibaseToJSON(streamState.data) .content as ModelDefinition diff --git a/packages/model-instance-client/src/client.ts b/packages/model-instance-client/src/client.ts index 7ac2931..6238c6a 100644 --- a/packages/model-instance-client/src/client.ts +++ b/packages/model-instance-client/src/client.ts @@ -4,7 +4,7 @@ import { decodeMultibaseToJSON, decodeMultibaseToStreamID, } from '@ceramic-sdk/events' -import { CommitID, type StreamID } from '@ceramic-sdk/identifiers' +import { CommitID, StreamID } from '@ceramic-sdk/identifiers' import { DocumentEvent, getStreamID, @@ -120,8 +120,10 @@ export class ModelInstanceClient extends StreamClient { } /** Retrieve and return document state */ - async getDocumentState(streamID: string): Promise { - const streamState = await this.getStreamState(streamID) + async getDocumentState(streamID: StreamID | string): Promise { + const id = + typeof streamID === 'string' ? StreamID.fromString(streamID) : streamID + const streamState = await this.getStreamState(id) return this.streamStateToDocumentState(streamState) } @@ -133,7 +135,9 @@ export class ModelInstanceClient extends StreamClient { let currentId: CommitID // If currentState is not provided, fetch the current state if (!params.currentState) { - const streamState = await this.getStreamState(params.streamID) + const streamState = await this.getStreamState( + StreamID.fromString(params.streamID), + ) currentState = this.streamStateToDocumentState(streamState) currentId = this.getCurrentID(streamState.event_cid) } else { diff --git a/packages/model-instance-client/test/lib.test.ts b/packages/model-instance-client/test/lib.test.ts index a080da3..0412a30 100644 --- a/packages/model-instance-client/test/lib.test.ts +++ b/packages/model-instance-client/test/lib.test.ts @@ -1,6 +1,11 @@ import { assertSignedEvent, getSignedEventPayload } from '@ceramic-sdk/events' import type { CeramicClient } from '@ceramic-sdk/http-client' -import { CommitID, randomCID, randomStreamID } from '@ceramic-sdk/identifiers' +import { + CommitID, + StreamID, + randomCID, + randomStreamID, +} from '@ceramic-sdk/identifiers' import { DataInitEventPayload, DocumentDataEventPayload, @@ -250,7 +255,9 @@ describe('ModelInstanceClient', () => { } as unknown as CeramicClient const client = new ModelInstanceClient({ ceramic, did: authenticatedDID }) - const documentState = await client.getDocumentState(streamId) + const documentState = await client.getDocumentState( + StreamID.fromString(streamId), + ) expect(documentState.content).toEqual(docState.content) expect(documentState.metadata.model.toString()).toEqual( docState.metadata.model, diff --git a/packages/model-instance-handler/src/assertions.ts b/packages/model-instance-handler/src/assertions.ts index 58822dc..6d71f53 100644 --- a/packages/model-instance-handler/src/assertions.ts +++ b/packages/model-instance-handler/src/assertions.ts @@ -1,6 +1,4 @@ -import type { TimeEvent } from '@ceramic-sdk/events' import type { - DocumentDataEventPayload, DocumentInitEventHeader, DocumentMetadata, JSONPatchOperation, @@ -10,7 +8,7 @@ import addFormats from 'ajv-formats' import Ajv from 'ajv/dist/2020.js' import { equals } from 'uint8arrays' -import type { DocumentState, UnknownContent } from './types.js' +import type { UnknownContent } from './types.js' import { getUniqueFieldsValue } from './utils.js' const validator = new Ajv({ @@ -135,39 +133,3 @@ export function assertValidUniqueValue( ) } } - -/** - * Asserts that the 'id' and 'prev' properties of the given event properly link to the tip of - * the given document state. - * - * By the time the code gets into a StreamtypeHandler's applyCommit function the link to the state - * should already have been established by the stream loading and conflict resolution code, so - * if this check were to fail as part of a StreamtypeHandler's applyCommit function, that would - * indicate a programming error. - */ -export function assertEventLinksToState( - payload: DocumentDataEventPayload | TimeEvent, - state: DocumentState, -) { - if (state.log.length === 0) { - throw new Error('Invalid document state: log is empty') - } - - const initCID = state.log[0] - - // Older versions of the CAS created time events without an 'id' field, so only check - // the event payload 'id' field if it is present. - if (payload.id != null && payload.id.toString() !== initCID) { - throw new Error( - `Invalid init CID in event payload for document, expected ${initCID} but got ${payload.id}`, - ) - } - - const prev = payload.prev.toString() - const expectedPrev = state.log[state.log.length - 1] - if (prev !== expectedPrev) { - throw new Error( - `Commit doesn't properly point to previous event payload in log for document ${initCID}. Expected ${expectedPrev}, found 'prev' ${prev}`, - ) - } -} diff --git a/packages/model-instance-handler/src/handlers.ts b/packages/model-instance-handler/src/handlers.ts index 103c314..faa0af0 100644 --- a/packages/model-instance-handler/src/handlers.ts +++ b/packages/model-instance-handler/src/handlers.ts @@ -10,7 +10,6 @@ import { import jsonpatch from 'fast-json-patch' import { - assertEventLinksToState, assertNoImmutableFieldChange, assertValidContent, assertValidInitHeader, @@ -22,7 +21,6 @@ import { getImmutableFieldsToCheck } from './utils.js' import { validateRelationsContent } from './validation.js' function createInitState( - cid: string, header: DocumentInitEventHeader, content: Record | null, ): DocumentState { @@ -35,12 +33,10 @@ function createInitState( context: header.context, shouldIndex: header.shouldIndex, }, - log: [cid], } } export async function handleDeterministicInitPayload( - cid: string, payload: DeterministicInitEventPayload, context: Context, ): Promise { @@ -55,11 +51,10 @@ export async function handleDeterministicInitPayload( const definition = await context.getModelDefinition(modelID) assertValidInitHeader(definition, header) - return createInitState(cid, header, null) + return createInitState(header, null) } export async function handleInitPayload( - cid: string, payload: DocumentInitEventPayload, context: Context, ): Promise { @@ -78,17 +73,15 @@ export async function handleInitPayload( assertValidContent(modelID, definition.schema, data) await validateRelationsContent(context, definition, data) - return createInitState(cid, header, data) + return createInitState(header, data) } export async function handleDataPayload( - cid: string, payload: DocumentDataEventPayload, context: Context, ): Promise { const streamID = getStreamID(payload.id).toString() const state = await context.getDocumentState(streamID) - assertEventLinksToState(payload, state) const metadata = { ...state.metadata } @@ -131,22 +124,19 @@ export async function handleDataPayload( // Validate relations await validateRelationsContent(context, definition, content) - return { content, metadata, log: [...state.log, cid] } + return { content, metadata } } export async function handleTimeEvent( - cid: string, event: TimeEvent, context: Context, ): Promise { const streamID = getStreamID(event.id).toString() const state = await context.getDocumentState(streamID) - assertEventLinksToState(event, state) - return { ...state, log: [...state.log, cid] } + return { ...state } } export async function handleEvent( - cid: string, event: DocumentEvent, context: Context, ): Promise { @@ -158,15 +148,15 @@ export async function handleEvent( if (container.signed) { // Signed event is either non-deterministic init or data if (DocumentDataEventPayload.is(container.payload)) { - return await handleDataPayload(cid, container.payload, context) + return await handleDataPayload(container.payload, context) } if (DocumentInitEventPayload.is(container.payload)) { - return await handleInitPayload(cid, container.payload, context) + return await handleInitPayload(container.payload, context) } } // Unsigned event is either deterministic init or time if (TimeEvent.is(container.payload)) { - return await handleTimeEvent(cid, container.payload as TimeEvent, context) + return await handleTimeEvent(container.payload as TimeEvent, context) } - return await handleDeterministicInitPayload(cid, container.payload, context) + return await handleDeterministicInitPayload(container.payload, context) } diff --git a/packages/model-instance-handler/src/types.ts b/packages/model-instance-handler/src/types.ts index a498dfc..a73bcc4 100644 --- a/packages/model-instance-handler/src/types.ts +++ b/packages/model-instance-handler/src/types.ts @@ -7,7 +7,6 @@ export type UnknownContent = Record export type DocumentState = { content: UnknownContent | null metadata: DocumentMetadata - log: [string, ...Array] } export type Context = { diff --git a/packages/model-instance-handler/src/utils.ts b/packages/model-instance-handler/src/utils.ts index 3405baa..c6c4876 100644 --- a/packages/model-instance-handler/src/utils.ts +++ b/packages/model-instance-handler/src/utils.ts @@ -1,4 +1,3 @@ -import { DocumentDataEventPayload } from '@ceramic-sdk/model-instance-protocol' import type { ModelDefinition } from '@ceramic-sdk/model-protocol' import { fromString as bytesFromString } from 'uint8arrays' @@ -22,9 +21,7 @@ export function getImmutableFieldsToCheck( // Check if the stream is deterministic if (['set', 'single'].includes(definition.accountRelation.type)) { // Should check immutable fields if there is already a data event present, otherwise it is the first data event that sets the content of the deterministic stream - return state.log.some(DocumentDataEventPayload.is) - ? definition.immutableFields - : [] + return state.content != null ? definition.immutableFields : [] } // Should check immutable fields for all data events on non-deterministic streams diff --git a/packages/model-instance-handler/test/assertions.test.ts b/packages/model-instance-handler/test/assertions.test.ts index 282cc46..a5092b1 100644 --- a/packages/model-instance-handler/test/assertions.test.ts +++ b/packages/model-instance-handler/test/assertions.test.ts @@ -7,7 +7,6 @@ import type { import type { ModelDefinitionV2 } from '@ceramic-sdk/model-protocol' import { - assertEventLinksToState, assertNoImmutableFieldChange, assertValidContent, assertValidInitHeader, @@ -16,47 +15,6 @@ import { import type { DocumentState } from '../src/types.js' import { encodeUniqueFieldsValue } from '../src/utils.js' -describe('assertEventLinksToState()', () => { - test('throws if the state log is empty', () => { - const cid = randomCID() - expect(() => { - assertEventLinksToState( - { id: cid } as unknown as DocumentDataEventPayload, - { log: [] } as unknown as DocumentState, - ) - }).toThrow('Invalid document state: log is empty') - }) - - test('throws if the event id does not match the init event cid', () => { - const expectedID = randomCID().toString() - const invalidID = randomCID() - expect(() => { - assertEventLinksToState( - { id: invalidID } as unknown as DocumentDataEventPayload, - { log: [expectedID] } as unknown as DocumentState, - ) - }).toThrow( - `Invalid init CID in event payload for document, expected ${expectedID} but got ${invalidID}`, - ) - }) - - test('throws if the event prev does not match the previous event cid', () => { - const initID = randomCID() - const expectedID = randomCID() - const invalidID = randomCID() - expect(() => { - assertEventLinksToState( - { id: initID, prev: invalidID } as unknown as DocumentDataEventPayload, - { - log: [initID.toString(), expectedID.toString()], - } as unknown as DocumentState, - ) - }).toThrow( - `Commit doesn't properly point to previous event payload in log for document ${initID}. Expected ${expectedID}, found 'prev' ${invalidID}`, - ) - }) -}) - describe('assertNoImmutableFieldChange()', () => { test('throws if an immutable field is changed', () => { expect(() => { diff --git a/packages/model-instance-handler/test/handlers.test.ts b/packages/model-instance-handler/test/handlers.test.ts index 85f59fb..93805c8 100644 --- a/packages/model-instance-handler/test/handlers.test.ts +++ b/packages/model-instance-handler/test/handlers.test.ts @@ -30,7 +30,6 @@ import { encodeUniqueFieldsValue } from '../src/utils.js' const authenticatedDID = await getAuthenticatedDID(new Uint8Array(32)) describe('handleDeterministicInitPayload()', () => { - const cid = randomCID().toString() const modelID = randomStreamID() test('throws if content is provided', async () => { @@ -38,7 +37,7 @@ describe('handleDeterministicInitPayload()', () => { // @ts-expect-error data should be null event.data = { some: 'content' } await expect(async () => { - await handleDeterministicInitPayload(cid, event, {} as unknown as Context) + await handleDeterministicInitPayload(event, {} as unknown as Context) }).rejects.toThrow( 'Deterministic init events for ModelInstanceDocuments must not have content', ) @@ -62,7 +61,7 @@ describe('handleDeterministicInitPayload()', () => { event.header.unique = new Uint8Array() await expect(async () => { - await handleDeterministicInitPayload(cid, event, context) + await handleDeterministicInitPayload(event, context) }).rejects.toThrow( 'ModelInstanceDocuments for models with SINGLE accountRelations must be created deterministically', ) @@ -82,12 +81,11 @@ describe('handleDeterministicInitPayload()', () => { const context = { getModelDefinition } as unknown as Context const event = getDeterministicInitEventPayload(modelID, 'did:key:123') - const handled = await handleDeterministicInitPayload(cid, event, context) + const handled = await handleDeterministicInitPayload(event, context) expect(handled.content).toBeNull() expect(handled.metadata.controller).toBe('did:key:123') expect(handled.metadata.model.equals(modelID)).toBe(true) - expect(handled.log).toEqual([cid]) }) }) @@ -98,7 +96,6 @@ describe('handleInitPayload()', () => { test('throws if no content is provided', async () => { await expect(async () => { await handleInitPayload( - cid, { data: null } as unknown as DocumentInitEventPayload, {} as unknown as Context, ) @@ -111,7 +108,6 @@ describe('handleInitPayload()', () => { const value = 'a'.repeat(MAX_DOCUMENT_SIZE) await expect(async () => { await handleInitPayload( - cid, { data: { value } } as unknown as DocumentInitEventPayload, {} as unknown as Context, ) @@ -144,7 +140,7 @@ describe('handleInitPayload()', () => { payload.header.unique = undefined await expect(async () => { - await handleInitPayload(cid, payload, context) + await handleInitPayload(payload, context) }).rejects.toThrow( 'ModelInstanceDocuments for models with LIST accountRelations must be created with a unique field', ) @@ -171,7 +167,7 @@ describe('handleInitPayload()', () => { }), } await expect(async () => { - await handleInitPayload(cid, payload, context) + await handleInitPayload(payload, context) }).rejects.toThrow( "Validation Error: data must have required property 'hello'", ) @@ -212,7 +208,7 @@ describe('handleInitPayload()', () => { }), } await expect(async () => { - await handleInitPayload(cid, payload, context) + await handleInitPayload(payload, context) }).rejects.toThrow( `Relation on field foo points to Stream ${docID}, which belongs to Model ${docRefModelID}, but this Stream's Model (TestModel) specifies that this relation must be to a Stream in the Model ${expectedRefModelID}`, ) @@ -238,12 +234,11 @@ describe('handleInitPayload()', () => { model: modelID, }), } - const handled = await handleInitPayload(cid, payload, context) + const handled = await handleInitPayload(payload, context) expect(handled.content).toStrictEqual({ hello: 'world' }) expect(handled.metadata.controller).toBe(authenticatedDID.id) expect(handled.metadata.model.equals(modelID)).toBe(true) expect(handled.metadata.unique).toBeInstanceOf(Uint8Array) - expect(handled.log).toEqual([cid]) }) }) @@ -252,21 +247,6 @@ describe('handleDataPayload()', () => { const initID = randomCID() const modelID = randomStreamID() - test('throws if the event cannot be linked to the state', async () => { - const getDocumentState = jest.fn(() => { - return { log: [] } - }) - const context = { getDocumentState } as unknown as Context - - await expect(async () => { - await handleDataPayload( - eventID, - { id: initID } as unknown as DocumentDataEventPayload, - context, - ) - }).rejects.toThrow('Invalid document state: log is empty') - }) - test('throws if the event header contains other fields than "shouldIndex"', async () => { const getDocumentState = jest.fn(() => { return { @@ -278,7 +258,6 @@ describe('handleDataPayload()', () => { await expect(async () => { await handleDataPayload( - eventID, { id: initID, prev: initID, @@ -301,7 +280,6 @@ describe('handleDataPayload()', () => { await expect(async () => { await handleDataPayload( - eventID, { id: initID, prev: initID, @@ -339,7 +317,6 @@ describe('handleDataPayload()', () => { await expect(async () => { await handleDataPayload( - eventID, { id: initID, prev: initID, @@ -384,7 +361,6 @@ describe('handleDataPayload()', () => { await expect(async () => { await handleDataPayload( - eventID, { id: initID, prev: initID, @@ -423,7 +399,6 @@ describe('handleDataPayload()', () => { await expect(async () => { await handleDataPayload( - eventID, { id: initID, prev: initID, @@ -471,7 +446,6 @@ describe('handleDataPayload()', () => { await expect(async () => { await handleDataPayload( - eventID, { id: initID, prev: initID, @@ -514,41 +488,23 @@ describe('handleDataPayload()', () => { header: { shouldIndex: true }, } as unknown as DocumentDataEventPayload - const newState = await handleDataPayload(eventID, payload, context) + const newState = await handleDataPayload(payload, context) expect(newState.content).toEqual({ hello: 'test' }) - expect(newState.log).toEqual([initID.toString(), eventID]) expect(newState.metadata.shouldIndex).toBe(true) }) }) describe('handleTimeEvent()', () => { - const eventID = randomCID().toString() const initID = randomCID() - test('throws if the event cannot be linked to the state', async () => { - const getDocumentState = jest.fn(() => { - return { log: [] } - }) - const context = { getDocumentState } as unknown as Context - - await expect(async () => { - await handleTimeEvent( - eventID, - { id: initID } as unknown as TimeEvent, - context, - ) - }).rejects.toThrow('Invalid document state: log is empty') - }) - test('returns the updated DocumentState', async () => { const getDocumentState = jest.fn(() => { return { log: [initID.toString()], content: { test: true } } }) const context = { getDocumentState } as unknown as Context const event = { id: initID, prev: initID } as unknown as TimeEvent - const newState = await handleTimeEvent(eventID, event, context) + const newState = await handleTimeEvent(event, context) expect(newState.content).toEqual({ test: true }) - expect(newState.log).toEqual([initID.toString(), eventID]) }) }) @@ -587,7 +543,7 @@ describe('handleEvent()', () => { authenticatedDID.id, unique, ) - state = await handleEvent(initCID.toString(), initEvent, context) + state = await handleEvent(initEvent, context) const streamID = getStreamID(initCID) const timeCID = randomCID().toString() @@ -597,7 +553,7 @@ describe('handleEvent()', () => { proof: randomCID(), path: '/', } - state = await handleEvent(timeCID, timeEvent, context) + state = await handleEvent(timeEvent, context) const dataCID = randomCID().toString() const dataEvent = await createDataEvent({ @@ -606,14 +562,13 @@ describe('handleEvent()', () => { newContent: { hello: 'world', foo: 'one', bar: 'two' }, shouldIndex: true, }) - state = await handleEvent(dataCID, dataEvent, context) + state = await handleEvent(dataEvent, context) expect(state.content).toEqual({ hello: 'world', foo: 'one', bar: 'two' }) expect(state.metadata.controller).toBe(authenticatedDID.id) expect(state.metadata.model.equals(modelID)).toBe(true) expect(state.metadata.shouldIndex).toBe(true) expect(state.metadata.unique).toBe(unique) - expect(state.log).toEqual([initCID.toString(), timeCID, dataCID]) }) test('with non-deterministic init event', async () => { @@ -645,7 +600,7 @@ describe('handleEvent()', () => { content: { hello: 'world' }, model: modelID, }) - state = await handleEvent(initCID.toString(), initEvent, context) + state = await handleEvent(initEvent, context) const streamID = getStreamID(initCID) const dataCID = randomCID() @@ -655,7 +610,7 @@ describe('handleEvent()', () => { newContent: { hello: 'test' }, shouldIndex: true, }) - state = await handleEvent(dataCID.toString(), dataEvent, context) + state = await handleEvent(dataEvent, context) const timeCID = randomCID().toString() const timeEvent: TimeEvent = { @@ -664,13 +619,12 @@ describe('handleEvent()', () => { proof: randomCID(), path: '/', } - state = await handleEvent(timeCID, timeEvent, context) + state = await handleEvent(timeEvent, context) expect(state.content).toEqual({ hello: 'test' }) expect(state.metadata.controller).toBe(authenticatedDID.id) expect(state.metadata.model.equals(modelID)).toBe(true) expect(state.metadata.shouldIndex).toBe(true) expect(state.metadata.unique).toBeInstanceOf(Uint8Array) - expect(state.log).toEqual([initCID.toString(), dataCID.toString(), timeCID]) }) }) diff --git a/packages/stream-client/package.json b/packages/stream-client/package.json index 84f73d0..d492401 100644 --- a/packages/stream-client/package.json +++ b/packages/stream-client/package.json @@ -32,7 +32,8 @@ "prepublishOnly": "package-check" }, "dependencies": { - "@ceramic-sdk/http-client": "workspace:^" + "@ceramic-sdk/http-client": "workspace:^", + "@ceramic-sdk/identifiers": "workspace:^" }, "devDependencies": { "dids": "^5.0.2" diff --git a/packages/stream-client/src/index.ts b/packages/stream-client/src/index.ts index 4b564ef..e148b7e 100644 --- a/packages/stream-client/src/index.ts +++ b/packages/stream-client/src/index.ts @@ -1,4 +1,5 @@ import { type CeramicClient, getCeramicClient } from '@ceramic-sdk/http-client' +import type { StreamID } from '@ceramic-sdk/identifiers' import type { DID } from 'dids' export type StreamState = { @@ -40,11 +41,16 @@ export class StreamClient { * @param streamId - Multibase encoded stream ID * @returns The state of the stream */ - async getStreamState(streamId: string): Promise { + async getStreamState(streamId: StreamID | string): Promise { const { data, error } = await this.#ceramic.api.GET( '/streams/{stream_id}', { - params: { path: { stream_id: streamId } }, + params: { + path: { + stream_id: + typeof streamId === 'string' ? streamId : streamId.toString(), + }, + }, }, ) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e7e52a..8151c72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -456,6 +456,9 @@ importers: '@ceramic-sdk/http-client': specifier: workspace:^ version: link:../http-client + '@ceramic-sdk/identifiers': + specifier: workspace:^ + version: link:../identifiers devDependencies: dids: specifier: ^5.0.2 @@ -622,6 +625,9 @@ importers: specifier: ^1.0.0 version: 1.0.0 devDependencies: + dids: + specifier: ^5.0.2 + version: 5.0.2(typescript@5.6.2)(zod@3.23.8) multiformats: specifier: ^13.3.0 version: 13.3.0 diff --git a/tests/c1-integration/package.json b/tests/c1-integration/package.json index 586b9e7..ac22220 100644 --- a/tests/c1-integration/package.json +++ b/tests/c1-integration/package.json @@ -40,7 +40,8 @@ "modern-spawn": "^1.0.0" }, "devDependencies": { - "multiformats": "^13.3.0" + "multiformats": "^13.3.0", + "dids": "^5.0.2" }, "jest": { "extensionsToTreatAsEsm": [".ts"], diff --git a/tests/c1-integration/test/classes.test.ts b/tests/c1-integration/test/classes.test.ts index 7517e1a..e10cb0e 100644 --- a/tests/c1-integration/test/classes.test.ts +++ b/tests/c1-integration/test/classes.test.ts @@ -7,6 +7,7 @@ import { } from '@ceramic-sdk/model-handler' import { ModelInstanceClient } from '@ceramic-sdk/model-instance-client' import { + type Context, type DocumentState, handleEvent as handleDocument, } from '@ceramic-sdk/model-instance-handler' @@ -60,7 +61,7 @@ describe('stream classes', () => { const modelsStore: Record = {} const contextDocumentModel = modelID.baseID.toString() let docState: DocumentState - const context = { + const context: Context = { getDocumentModel: async () => contextDocumentModel, getDocumentState: async () => docState, getModelDefinition: async (id) => { @@ -90,11 +91,7 @@ describe('stream classes', () => { model: modelID, }) const initEvent = await docClient.getEvent(initCommitID) - docState = await handleDocument( - initCommitID.commit.toString(), - initEvent, - context, - ) + docState = await handleDocument(initEvent, context) expect(docState.content).toBeNull() const updateCommitID = await docClient.postData({ @@ -102,11 +99,7 @@ describe('stream classes', () => { newContent: { test: 'set' }, }) const updateEvent = await docClient.getEvent(updateCommitID) - docState = await handleDocument( - updateCommitID.commit.toString(), - updateEvent, - context, - ) + docState = await handleDocument(updateEvent, context) expect(docState.content).toEqual({ test: 'set' }) const finalCommitID = await docClient.postData({ @@ -114,11 +107,7 @@ describe('stream classes', () => { newContent: { test: 'changed' }, }) const finalEvent = await docClient.getEvent(finalCommitID) - docState = await handleDocument( - finalCommitID.commit.toString(), - finalEvent, - context, - ) + docState = await handleDocument(finalEvent, context) expect(docState.content).toEqual({ test: 'changed' }) }) @@ -179,11 +168,7 @@ describe('stream classes', () => { model: modelID, }) const initEvent = await docClient.getEvent(initCommitID) - docState = await handleDocument( - initCommitID.commit.toString(), - initEvent, - context, - ) + docState = await handleDocument(initEvent, context) expect(docState.content).toEqual({ test: 'one' }) const updateCommitID = await docClient.postData({ @@ -191,11 +176,7 @@ describe('stream classes', () => { newContent: { test: 'two' }, }) const updateEvent = await docClient.getEvent(updateCommitID) - docState = await handleDocument( - updateCommitID.commit.toString(), - updateEvent, - context, - ) + docState = await handleDocument(updateEvent, context) expect(docState.content).toEqual({ test: 'two' }) const finalCommitID = await docClient.postData({ @@ -203,11 +184,7 @@ describe('stream classes', () => { newContent: { test: 'three' }, }) const finalEvent = await docClient.getEvent(finalCommitID) - docState = await handleDocument( - finalCommitID.commit.toString(), - finalEvent, - context, - ) + docState = await handleDocument(finalEvent, context) expect(docState.content).toEqual({ test: 'three' }) }) diff --git a/tests/c1-integration/test/document.test.ts b/tests/c1-integration/test/document.test.ts index ce72e8d..531b89d 100644 --- a/tests/c1-integration/test/document.test.ts +++ b/tests/c1-integration/test/document.test.ts @@ -1,19 +1,18 @@ -import { signedEventToCAR } from '@ceramic-sdk/events' import { CeramicClient } from '@ceramic-sdk/http-client' import { StreamID } from '@ceramic-sdk/identifiers' -import { createInitEvent as createModel } from '@ceramic-sdk/model-client' +import { ModelClient } from '@ceramic-sdk/model-client' import { - type ModelState, - handleInitEvent as handleModel, -} from '@ceramic-sdk/model-handler' -import { createInitEvent as createDocument } from '@ceramic-sdk/model-instance-client' -import { handleEvent as handleDocument } from '@ceramic-sdk/model-instance-handler' -import { DocumentEvent } from '@ceramic-sdk/model-instance-protocol' + ModelInstanceClient, + createInitEvent as createDocument, +} from '@ceramic-sdk/model-instance-client' import { - type ModelDefinition, - getModelStreamID, -} from '@ceramic-sdk/model-protocol' + type Context, + handleEvent as handleDocument, +} from '@ceramic-sdk/model-instance-handler' +import { DocumentEvent } from '@ceramic-sdk/model-instance-protocol' +import type { ModelDefinition } from '@ceramic-sdk/model-protocol' import { getAuthenticatedDID } from '@didtools/key-did' +import type { DID } from 'dids' import CeramicOneContainer, { type EnvironmentOptions } from '../src' const authenticatedDID = await getAuthenticatedDID(new Uint8Array(32)) @@ -46,38 +45,40 @@ describe('model integration test', () => { const client = new CeramicClient({ url: `http://127.0.0.1:${CONTAINER_OPTS.apiPort}`, }) + const modelInstanceClient = new ModelInstanceClient({ + ceramic: client, + did: authenticatedDID, + }) + + const modelClient = new ModelClient({ + ceramic: client, + did: authenticatedDID, + }) + + let model: StreamID beforeAll(async () => { c1Container = await CeramicOneContainer.startContainer(CONTAINER_OPTS) + model = await modelClient.postDefinition(testModel) }, 10000) test('create model and documents using the model', async () => { - const modelsStore: Record = {} - - const context = { + const context: Context = { getModelDefinition: async (id) => { - const cid = StreamID.fromString(id).cid.toString() - const state = modelsStore[cid] - if (state == null) { - throw new Error(`State not found for model: ${id}`) - } - return state.content + return await modelClient.getModelDefinition(StreamID.fromString(id)) + }, + verifier: authenticatedDID as DID, + getDocumentState: async (id) => { + return await modelInstanceClient.getDocumentState( + StreamID.fromString(id), + ) + }, + getDocumentModel: async (id) => { + return await modelClient.getDocumentModel(StreamID.fromString(id)) }, - verifier: authenticatedDID, } - const modelEvent = await createModel(authenticatedDID, testModel) - const modelCID = signedEventToCAR(modelEvent).roots[0] - const modelCIDstring = modelCID.toString() - modelsStore[modelCIDstring] = await handleModel( - modelCIDstring, - modelEvent, - context, - ) - - await client.registerInterestModel(modelCIDstring) - - const model = getModelStreamID(modelCID) + await client.registerInterestModel(model.toString()) async function createAndPostDocument( content: Record, @@ -94,17 +95,17 @@ describe('model integration test', () => { await createAndPostDocument({ test: 'two' }) const feed = await client.getEventsFeed() - expect(feed.events).toHaveLength(2) - const eventID1 = feed.events[0].id - const eventID2 = feed.events[1].id + expect(feed.events).toHaveLength(3) + const eventID1 = feed.events[1].id + const eventID2 = feed.events[2].id const [event1, event2] = await Promise.all([ client.getEventType(DocumentEvent, eventID1), client.getEventType(DocumentEvent, eventID2), ]) const [state1, state2] = await Promise.all([ - handleDocument(eventID1, event1, context), - handleDocument(eventID2, event2, context), + handleDocument(event1 as DocumentEvent, context), + handleDocument(event2 as DocumentEvent, context), ]) expect(state1.content).toEqual({ test: 'one' }) expect(state2.content).toEqual({ test: 'two' }) diff --git a/tests/c1-integration/test/model-mid-listType.test.ts b/tests/c1-integration/test/model-mid-listType.test.ts index d75c6eb..841c17c 100644 --- a/tests/c1-integration/test/model-mid-listType.test.ts +++ b/tests/c1-integration/test/model-mid-listType.test.ts @@ -1,5 +1,6 @@ import { CeramicClient } from '@ceramic-sdk/http-client' -import type { CommitID, StreamID } from '@ceramic-sdk/identifiers' +import type { CommitID } from '@ceramic-sdk/identifiers' +import { StreamID } from '@ceramic-sdk/identifiers' import { ModelClient } from '@ceramic-sdk/model-client' import { ModelInstanceClient } from '@ceramic-sdk/model-instance-client' import type { ModelDefinition } from '@ceramic-sdk/model-protocol' @@ -70,14 +71,14 @@ describe('model integration test for list model and MID', () => { // wait 1 seconds await new Promise((resolve) => setTimeout(resolve, 1000)) const currentState = await modelInstanceClient.getDocumentState( - documentStream.toString(), + new StreamID(3, documentStream.commit.toString()), ) expect(currentState.content).toEqual({ test: 'hello' }) }) test('updates document and obtains correct state', async () => { // update the document const updatedState = await modelInstanceClient.updateDocument({ - streamID: documentStream.toString(), + streamID: new StreamID(3, documentStream.commit.toString()).toString(), newContent: { test: 'world' }, shouldIndex: true, }) diff --git a/tests/c1-integration/test/model-mid-setType.test.ts b/tests/c1-integration/test/model-mid-setType.test.ts index 7f642ed..5de4bf2 100644 --- a/tests/c1-integration/test/model-mid-setType.test.ts +++ b/tests/c1-integration/test/model-mid-setType.test.ts @@ -1,5 +1,5 @@ import { CeramicClient } from '@ceramic-sdk/http-client' -import type { CommitID, StreamID } from '@ceramic-sdk/identifiers' +import { type CommitID, StreamID } from '@ceramic-sdk/identifiers' import { ModelClient } from '@ceramic-sdk/model-client' import { ModelInstanceClient } from '@ceramic-sdk/model-instance-client' import type { ModelDefinition } from '@ceramic-sdk/model-protocol' @@ -77,14 +77,14 @@ describe('model integration test for list model and MID', () => { // wait 1 seconds await new Promise((resolve) => setTimeout(resolve, 1000)) const currentState = await modelInstanceClient.getDocumentState( - documentStream.toString(), + new StreamID(3, documentStream.commit.toString()), ) expect(currentState.content).toEqual({ test: 'hello' }) }) test('updates document and obtains correct state', async () => { // update the document const updatedState = await modelInstanceClient.updateDocument({ - streamID: documentStream.toString(), + streamID: new StreamID(3, documentStream.commit.toString()).toString(), newContent: { test: 'world' }, shouldIndex: true, }) diff --git a/tests/c1-integration/test/model-mid-singleType.test.ts b/tests/c1-integration/test/model-mid-singleType.test.ts index 3fb4beb..af4e1e1 100644 --- a/tests/c1-integration/test/model-mid-singleType.test.ts +++ b/tests/c1-integration/test/model-mid-singleType.test.ts @@ -1,5 +1,5 @@ import { CeramicClient } from '@ceramic-sdk/http-client' -import type { CommitID, StreamID } from '@ceramic-sdk/identifiers' +import { type CommitID, StreamID } from '@ceramic-sdk/identifiers' import { ModelClient } from '@ceramic-sdk/model-client' import { ModelInstanceClient } from '@ceramic-sdk/model-instance-client' import type { ModelDefinition } from '@ceramic-sdk/model-protocol' @@ -69,14 +69,14 @@ describe('model integration test for list model and MID', () => { // wait 1 seconds await new Promise((resolve) => setTimeout(resolve, 1000)) const currentState = await modelInstanceClient.getDocumentState( - documentStream.toString(), + new StreamID(3, documentStream.commit.toString()), ) expect(currentState.content).toEqual(null) }) test('updates document and obtains correct state', async () => { // update the document const updatedState = await modelInstanceClient.updateDocument({ - streamID: documentStream.toString(), + streamID: new StreamID(3, documentStream.commit.toString()).toString(), newContent: { test: 'hello' }, shouldIndex: true, }) diff --git a/tests/c1-integration/test/model.test.ts b/tests/c1-integration/test/model.test.ts index 8f31674..31921bd 100644 --- a/tests/c1-integration/test/model.test.ts +++ b/tests/c1-integration/test/model.test.ts @@ -9,7 +9,6 @@ import { } from '@ceramic-sdk/model-protocol' import { getAuthenticatedDID } from '@didtools/key-did' import CeramicOneContainer, { type EnvironmentOptions } from '../src' -import type ContainerWrapper from '../src/withContainer' const authenticatedDID = await getAuthenticatedDID(new Uint8Array(32)) diff --git a/tests/c1-integration/test/stream-client.test.ts b/tests/c1-integration/test/stream-client.test.ts index 62246a3..f187f9c 100644 --- a/tests/c1-integration/test/stream-client.test.ts +++ b/tests/c1-integration/test/stream-client.test.ts @@ -76,8 +76,7 @@ describe('stream client', () => { test('gets a stream', async () => { const client = new StreamClient({ ceramic: ceramicClient }) - console.log(streamId.toString()) - const streamState = await client.getStreamState(streamId.toString()) + const streamState = await client.getStreamState(streamId) expect(streamState).toBeDefined() expect(streamState.id.toString()).toEqual(streamId.toString()) }, 10000)