diff --git a/api/package.json b/api/package.json index 738d0515..364ea8fd 100644 --- a/api/package.json +++ b/api/package.json @@ -21,9 +21,9 @@ "body-parser": "^1.20.2", "cookie-parser": "^1.4.6", "cors": "^2.8.5", - "document-drive": "1.0.0-alpha.17", - "document-model": "^1.0.32", - "document-model-libs": "^1.15.0", + "document-drive": "1.0.0-alpha.23", + "document-model": "^1.0.35", + "document-model-libs": "^1.17.1", "dotenv": "^16.4.5", "ethers": "^5.7.2", "express": "^4.19.2", diff --git a/api/pnpm-lock.yaml b/api/pnpm-lock.yaml index 0360874a..8237f5ef 100644 --- a/api/pnpm-lock.yaml +++ b/api/pnpm-lock.yaml @@ -24,14 +24,14 @@ dependencies: specifier: ^2.8.5 version: 2.8.5 document-drive: - specifier: 1.0.0-alpha.17 - version: 1.0.0-alpha.17(@prisma/client@5.8.1)(document-model-libs@1.15.0)(document-model@1.0.32)(localforage@1.10.0)(sequelize@6.37.1)(sqlite3@5.1.7) + specifier: 1.0.0-alpha.23 + version: 1.0.0-alpha.23(@prisma/client@5.8.1)(document-model-libs@1.17.1)(document-model@1.0.35)(localforage@1.10.0)(sequelize@6.37.1)(sqlite3@5.1.7) document-model: - specifier: ^1.0.32 - version: 1.0.32 + specifier: ^1.0.35 + version: 1.0.35 document-model-libs: - specifier: ^1.15.0 - version: 1.15.0(graphql@16.8.1)(react-dom@18.2.0)(react@18.2.0)(vitest@0.32.4) + specifier: ^1.17.1 + version: 1.17.1(graphql@16.8.1)(react-dom@18.2.0)(react@18.2.0)(vitest@0.32.4) dotenv: specifier: ^16.4.5 version: 16.4.5 @@ -4297,6 +4297,10 @@ packages: tslib: 2.6.2 dev: false + /change-case@5.4.3: + resolution: {integrity: sha512-4cdyvorTy/lViZlVzw2O8/hHCLUuHqp4KpSSP3DlauhFCf3LdnfF+p5s0EAhjKsU7bqrMzu7iQArYfoPiHO2nw==} + dev: false + /check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} dependencies: @@ -4694,19 +4698,19 @@ packages: esutils: 2.0.3 dev: true - /document-drive@1.0.0-alpha.17(@prisma/client@5.8.1)(document-model-libs@1.15.0)(document-model@1.0.32)(localforage@1.10.0)(sequelize@6.37.1)(sqlite3@5.1.7): - resolution: {integrity: sha512-TCh2Os+ugxza2CbhWB0x5lPVwuQGStp/n5t8Wgq8TDvNJO2pimZSxL+X+kaNYjdGKUXebNcKo4CkzDKEN2OGQQ==} + /document-drive@1.0.0-alpha.23(@prisma/client@5.8.1)(document-model-libs@1.17.1)(document-model@1.0.35)(localforage@1.10.0)(sequelize@6.37.1)(sqlite3@5.1.7): + resolution: {integrity: sha512-tGiGBCNCB+sv+P3N4ZoxNi0wa/eJua+WGYsEP5GDMW2WfKKtXdI3XhoenZkAnqlaLcClwE5oe+/ruSQD9w2tBg==} peerDependencies: '@prisma/client': 5.8.1 - document-model: ^1.0.29 + document-model: ^1.0.34 document-model-libs: ^1.1.51 localforage: ^1.10.0 sequelize: ^6.35.2 sqlite3: ^5.1.7 dependencies: '@prisma/client': 5.8.1(prisma@5.8.1) - document-model: 1.0.32 - document-model-libs: 1.15.0(graphql@16.8.1)(react-dom@18.2.0)(react@18.2.0)(vitest@0.32.4) + document-model: 1.0.35 + document-model-libs: 1.17.1(graphql@16.8.1)(react-dom@18.2.0)(react@18.2.0)(vitest@0.32.4) graphql: 16.8.1 graphql-request: 6.1.0(graphql@16.8.1) json-stringify-deterministic: 1.0.12 @@ -4719,8 +4723,8 @@ packages: - encoding dev: false - /document-model-libs@1.15.0(graphql@16.8.1)(react-dom@18.2.0)(react@18.2.0)(vitest@0.32.4): - resolution: {integrity: sha512-SIN4bf/3+4el3b22GwQFz5+sKCV+Fl8GdvYzcV7DvqqosUtadSH4d1Vn1mQ+wGdC3iy1fEbfIVbPvCShHlLQdw==} + /document-model-libs@1.17.1(graphql@16.8.1)(react-dom@18.2.0)(react@18.2.0)(vitest@0.32.4): + resolution: {integrity: sha512-aXf6lkq2ENQl1JA0Tnf5KlrJ4iWMZWEDA7v4e99rCAHtc4XM7gtWj2Fy8T66UiE0tqhxF6Ab52ZyGMbC0Fjf1Q==} peerDependencies: react: ^18.2.0 react-dom: ^18.2.0 @@ -4750,9 +4754,10 @@ packages: - vitest dev: false - /document-model@1.0.32: - resolution: {integrity: sha512-TLmOm6XGWtp5i8Idu3UDjy5n6vbG7Av2ETqM+8tlY6nA/zUWm6fKesciu9BHGL+kncHRWXcv8cprrYiuoWtmDg==} + /document-model@1.0.35: + resolution: {integrity: sha512-F0hEmnsifjnqeZRydc+AtvOx1BG9ew+o+74MqtT+joeW/9KXpmAf9oIZzA83jswlJbr93tB+iqsORSApARaYTg==} dependencies: + change-case: 5.4.3 immer: 10.0.4 json-stringify-deterministic: 1.0.12 jszip: 3.10.1 diff --git a/api/src/graphql/generated/index/nexus.ts b/api/src/graphql/generated/index/nexus.ts index e199cb6c..bf81eeed 100644 --- a/api/src/graphql/generated/index/nexus.ts +++ b/api/src/graphql/generated/index/nexus.ts @@ -54,7 +54,7 @@ export interface NexusGenInputs { } DocumentDriveStateInput: { // input type icon?: string | null; // String - id: string; // ID! + id?: string | null; // ID name: string; // String! slug?: string | null; // String } diff --git a/api/src/graphql/generated/index/schema.graphql b/api/src/graphql/generated/index/schema.graphql index 4edca808..6c692448 100644 --- a/api/src/graphql/generated/index/schema.graphql +++ b/api/src/graphql/generated/index/schema.graphql @@ -55,7 +55,7 @@ type DocumentDriveState { input DocumentDriveStateInput { icon: String - id: ID! + id: ID name: String! slug: String } diff --git a/api/src/modules/document-drive/drive-resolver.ts b/api/src/modules/document-drive/drive-resolver.ts index 5875d728..46b3ab96 100644 --- a/api/src/modules/document-drive/drive-resolver.ts +++ b/api/src/modules/document-drive/drive-resolver.ts @@ -12,9 +12,11 @@ import { systemType } from '../system'; import { ListenerRevision as IListenerRevision, UpdateStatus as IUpdateStatus, StrandUpdate, } from 'document-drive'; -import { OperationScope } from 'document-model/document'; +import { Operation, OperationScope } from 'document-model/document'; import stringify from 'json-stringify-deterministic'; import { getChildLogger } from '../../logger'; +import { Context } from '../../graphql/server/drive/context'; +import { DocumentDriveAction } from 'document-model-libs/document-drive'; const logger = getChildLogger({ msgPrefix: 'Drive Resolver' }); @@ -178,7 +180,7 @@ export const syncType = objectType({ listenerId: idArg(), since: 'Date', }, - resolve: async (_parent, { listenerId, since }, ctx) => { + resolve: async (_parent, { listenerId, since }, ctx: Context) => { if (!listenerId) throw new Error('Listener ID is required'); try { const result = await ctx.prisma.document.pullStrands( @@ -228,11 +230,12 @@ export const driveSystemQueryField = queryField('system', { export const getDrive = queryField('drive', { type: DocumentDriveState, - resolve: async (_parent, args, ctx) => { + resolve: async (_parent, args, ctx: Context) => { try { const drive = await ctx.prisma.document.getDrive(ctx.driveId ?? '1'); return drive; } catch (e) { + logger.error(e); return null; } }, @@ -243,10 +246,15 @@ export const registerListener = mutationField('registerPullResponderListener', { args: { filter: nonNull(InputListenerFilter), }, - resolve: async (_parent, { filter }, ctx) => { + resolve: async (_parent, { filter }, ctx: Context) => { const result = await ctx.prisma.document.registerPullResponderListener( ctx.driveId ?? '1', - filter, + { + branch: filter.branch?.filter(b => !!b) as string[] ?? [], + documentId: filter.documentId?.filter(b => !!b) as string[] ?? [], + documentType: filter.documentType?.filter(b => !!b) as string[] ?? [], + scope: filter.scope?.filter(b => !!b) as string[] ?? [], + }, ); return result; @@ -259,7 +267,7 @@ export const deleteListener = mutationField('deletePullResponderListener', { args: { filter: nonNull(InputListenerFilter), }, - resolve: async (_parent, { filter }, ctx) => { + resolve: async (_parent, { filter }, ctx: Context) => { const result = await ctx.prisma.document.deletePullResponderListener( ctx.driveId ?? '1', filter, @@ -274,7 +282,7 @@ export const pushUpdates = mutationField('pushUpdates', { args: { strands: list(nonNull(InputStrandUpdate)), }, - resolve: async (_parent, { strands }, ctx) => { + resolve: async (_parent, { strands }, ctx: Context) => { logger.info('pushUpdates') if (!strands || strands?.length === 0) return []; @@ -290,13 +298,13 @@ export const pushUpdates = mutationField('pushUpdates', { const result = await ctx.prisma.document.pushUpdates( s.driveId, - operations, + operations as Operation[], s.documentId ?? undefined, ); if (result.status !== "SUCCESS") logger.error(result.error); - const revision = result.document.operations[s.scope].slice().pop()?.index ?? -1; + const revision = result.document?.operations[s.scope as OperationScope].slice().pop()?.index ?? -1; return { revision, branch: s.branch, @@ -317,7 +325,7 @@ export const acknowledge = mutationField('acknowledge', { listenerId: nonNull('String'), revisions: list(ListenerRevisionInput), }, - resolve: async (_parent, { revisions, listenerId }, ctx) => { + resolve: async (_parent, { revisions, listenerId }, ctx: Context) => { try { if (!listenerId || !revisions) return false; const validEntries: IListenerRevision[] = revisions @@ -332,7 +340,7 @@ export const acknowledge = mutationField('acknowledge', { })); const result = await ctx.prisma.document.processAcknowledge( - ctx.driveId, + ctx.driveId ?? "1", listenerId, validEntries, ); diff --git a/api/src/modules/document-drive/drives-resolver.ts b/api/src/modules/document-drive/drives-resolver.ts index e0b081ab..49da5252 100644 --- a/api/src/modules/document-drive/drives-resolver.ts +++ b/api/src/modules/document-drive/drives-resolver.ts @@ -7,6 +7,8 @@ import { queryField, } from 'nexus'; import { DocumentDriveState } from './drive-resolver'; +import { Context } from '../../graphql/server/drive/context'; +import logger from '../../logger'; export const DocumentDriveLocalState = objectType({ name: 'DocumentDriveLocalState', @@ -26,7 +28,7 @@ export const DocumentDriveLocalStateInput = inputObjectType({ export const DocumentDriveStateInput = inputObjectType({ name: 'DocumentDriveStateInput', definition(t) { - t.nonNull.id('id'); + t.id('id'); t.nonNull.string('name'); t.string('icon'); t.string('slug'); @@ -35,11 +37,12 @@ export const DocumentDriveStateInput = inputObjectType({ export const getDrives = queryField('drives', { type: list('String'), - resolve: async (_parent, args, ctx) => { + resolve: async (_parent, args, ctx: Context) => { try { const drives = await ctx.prisma.document.getDrives(); return drives; } catch (e) { + logger.error(e); throw new Error('Failed to get drives.'); } }, @@ -49,7 +52,7 @@ const addDriveResponseDefinition = objectType({ name: 'AddDriveResponse', definition(t) { t.nonNull.field('global', { - type: DocumentDriveState, + type: DocumentDriveState }); t.nonNull.field('local', { type: DocumentDriveLocalState, @@ -64,12 +67,12 @@ export const addDrive = mutationField('addDrive', { global: nonNull(DocumentDriveStateInput), local: nonNull(DocumentDriveLocalStateInput), }, - resolve: async (_parent, { global, local }, ctx) => { - await ctx; - return ctx.prisma.document.addDrive({ - global: { ...global, nodes: [] }, - local: { ...local, listeners: [], triggers: [] }, + resolve: async (_parent, { global, local }, ctx: Context) => { + const drive = await ctx.prisma.document.addDrive({ + global: { id: global.id, name: global.name, icon: global.icon ?? null, slug: global.slug ?? null }, + local: { availableOffline: local.availableOffline, sharingType: local.sharingType ?? null, listeners: [], triggers: [] }, }); + return drive.state; }, }); @@ -78,10 +81,11 @@ export const deleteDrive = mutationField('deleteDrive', { args: { id: nonNull('String'), }, - resolve: async (_parent, { id }, ctx) => { + resolve: async (_parent, { id }, ctx: Context) => { try { - await ctx.prisma.drive.deleteDrive(id); + await ctx.prisma.document.deleteDrive(id); } catch (e) { + logger.error(e); return false; } diff --git a/api/src/modules/document/model.ts b/api/src/modules/document/model.ts index 1476ba32..9cdfc404 100644 --- a/api/src/modules/document/model.ts +++ b/api/src/modules/document/model.ts @@ -20,7 +20,6 @@ import { Listener, ListenerFilter, actions, - reducer, DocumentDriveState, DocumentDriveAction } from 'document-model-libs/document-drive'; @@ -85,21 +84,21 @@ export function getDocumentDriveCRUD(prisma: Prisma.TransactionClient) { return { addDrive: async (args: DriveInput) => { try { - await driveServer.addDrive(args); + const drive = await driveServer.addDrive(args); await initialize(); clearDriveCache(); + return drive; } catch (e) { + logger.error(e); throw new Error("Couldn't add drive"); } - return { - ...args, - }; }, deleteDrive: async (id: string) => { try { await driveServer.deleteDrive(id); clearDriveCache(); } catch (e) { + logger.error(e); throw new Error("Couldn't delete drive"); } @@ -115,9 +114,8 @@ export function getDocumentDriveCRUD(prisma: Prisma.TransactionClient) { } else { return drive; } - - } catch (e) { + logger.error(e); throw new Error("Couldn't get drive"); } }, @@ -134,6 +132,7 @@ export function getDocumentDriveCRUD(prisma: Prisma.TransactionClient) { return driveIds; } catch (e) { + logger.error(e); throw new Error("Couldn't get drives"); } }, @@ -214,15 +213,11 @@ export function getDocumentDriveCRUD(prisma: Prisma.TransactionClient) { listenerId: uuid, system: false, }; - let drive = await driveServer.getDrive(driveId); - drive = reducer(drive, actions.addListener({ listener })); - const operation = drive.operations.local.slice().pop(); - if (!operation) { - throw new Error("Operation couldnt be applied") - } - const result = await driveServer.addDriveOperations(driveId, [operation]); + + const result = await driveServer.addDriveAction(driveId, actions.addListener({ listener })); if (result.status !== "SUCCESS") { - throw new Error(`Listener couldn't be registered: ${result.error}`); + result.error && logger.error(result.error); + throw new Error(`Listener couldn't be registered: ${result.error || result.status}`); } return listener; @@ -232,16 +227,10 @@ export function getDocumentDriveCRUD(prisma: Prisma.TransactionClient) { driveId: string, listenerId: string, ) => { - let drive = await driveServer.getDrive(driveId); - drive = reducer(drive, actions.removeListener({ listenerId })); - const operation = drive.operations.local.slice().pop(); - if (!operation) { - throw new Error("Operation couldnt be applied") - } - - const result = await driveServer.addDriveOperations(driveId, [operation]); + const result = await driveServer.addDriveAction(driveId, actions.removeListener({ listenerId })); if (result.status !== "SUCCESS") { - throw new Error(`Listener couldn't be deleted: ${result.error}`); + result.error && logger.error(result.error); + throw new Error(`Listener couldn't be deleted: ${result.error || result.status}`); } delete transmitters[driveId][listenerId]; diff --git a/api/src/modules/document/resolvers.ts b/api/src/modules/document/resolvers.ts index e9473819..1af43e02 100644 --- a/api/src/modules/document/resolvers.ts +++ b/api/src/modules/document/resolvers.ts @@ -1,5 +1,6 @@ import { interfaceType, nonNull, objectType, queryField } from 'nexus'; import { GQLDateBase } from '../system'; +import { Context } from '../../graphql/server/drive/context'; // todo: resolveType should be moved to somewhere else export const operationModelInterface = interfaceType({ @@ -51,7 +52,10 @@ export const documentQuery = queryField('document', { args: { id: nonNull('String'), }, - resolve: async (_root, { id }, ctx) => { + resolve: async (_root, { id }, ctx: Context) => { + if (!ctx.driveId) { + throw new Error("DriveId is not defined") + } const doc = await ctx.prisma.document.getDocument(ctx.driveId, id); return doc; }, diff --git a/api/src/modules/system/challenge/resolvers.ts b/api/src/modules/system/challenge/resolvers.ts index e9c98f72..e0b82bfe 100644 --- a/api/src/modules/system/challenge/resolvers.ts +++ b/api/src/modules/system/challenge/resolvers.ts @@ -1,4 +1,5 @@ import { mutationField, nonNull, objectType } from 'nexus'; +import { Context } from '../../../graphql/server/drive/context'; export const Challenge = objectType({ name: 'Challenge', @@ -14,7 +15,7 @@ export const createChallenge = mutationField('createChallenge', { args: { address: nonNull('String'), }, - resolve: async (_root, args, ctx) => ctx.prisma.challenge.createChallenge(args.address), + resolve: async (_root, args, ctx: Context) => ctx.prisma.challenge.createChallenge(args.address), }); export const solveChallenge = mutationField('solveChallenge', { @@ -23,5 +24,5 @@ export const solveChallenge = mutationField('solveChallenge', { nonce: nonNull('String'), signature: nonNull('String'), }, - resolve: async (_root, args, ctx) => ctx.prisma.challenge.solveChallenge(args.nonce, args.signature), + resolve: async (_root, args, ctx: Context) => ctx.prisma.challenge.solveChallenge(args.nonce, args.signature), }); diff --git a/api/src/modules/system/core-unit/resolvers.ts b/api/src/modules/system/core-unit/resolvers.ts index b05db3a0..4427ba04 100644 --- a/api/src/modules/system/core-unit/resolvers.ts +++ b/api/src/modules/system/core-unit/resolvers.ts @@ -1,6 +1,7 @@ import { stringArg, list, queryField, objectType, } from 'nexus/dist'; +import { Context } from '../../../graphql/server/index/context'; export const CoreUnit = objectType({ name: 'CoreUnit', @@ -18,7 +19,7 @@ export const CoreUnit = objectType({ export const coreUnits = queryField('coreUnits', { type: list('CoreUnit'), - resolve: async (_parent, _args, ctx) => { + resolve: async (_parent, _args, ctx: Context) => { const response = await ctx.prisma.coreUnit.findMany(); return response; }, @@ -27,7 +28,7 @@ export const coreUnits = queryField('coreUnits', { export const coreUnit = queryField('coreUnit', { type: 'CoreUnit', args: { id: stringArg() }, - resolve: async (_parent, { id }, ctx) => { + resolve: async (_parent, { id }, ctx: Context) => { if (!id) { throw new Error('please provide id'); } diff --git a/api/src/modules/system/resolvers.ts b/api/src/modules/system/resolvers.ts index 1851ab66..dbc707e7 100644 --- a/api/src/modules/system/resolvers.ts +++ b/api/src/modules/system/resolvers.ts @@ -4,6 +4,7 @@ import { list, mutationField, nonNull, objectType, queryField, scalarType, stringArg, } from 'nexus'; import logger from '../../logger'; +import { Context } from '../../graphql/server/index/context'; @@ -12,7 +13,7 @@ export const authType = objectType({ definition(t) { t.field('me', { type: 'User', - resolve: async (_, __, ctx) => { + resolve: async (_, __, ctx: Context) => { const { createdBy } = await ctx.getSession(); return ctx.prisma.user.findUnique({ where: { @@ -22,7 +23,7 @@ export const authType = objectType({ }, }); t.field('sessions', { - resolve: async (_, __, ctx) => { + resolve: async (_, __, ctx: Context) => { const { createdBy } = await ctx.getSession(); return ctx.prisma.session.listSessions(createdBy); }, diff --git a/api/src/modules/system/session/resolvers.ts b/api/src/modules/system/session/resolvers.ts index b6f5a441..6a953068 100644 --- a/api/src/modules/system/session/resolvers.ts +++ b/api/src/modules/system/session/resolvers.ts @@ -7,6 +7,7 @@ import { inputObjectType, objectType, } from 'nexus/dist'; +import { Context } from '../../../graphql/server/index/context'; export const Session = objectType({ name: 'Session', @@ -45,7 +46,7 @@ export const revoke = mutationField('revokeSession', { args: { sessionId: nonNull(stringArg()), }, - resolve: async (_parent, { sessionId }, ctx) => { + resolve: async (_parent, { sessionId }, ctx: Context) => { const userId = (await ctx.getSession()).createdBy; return ctx.prisma.session.revoke(sessionId, userId); }, @@ -59,7 +60,7 @@ export const create = mutationField('createSession', { resolve: async ( _parent, { session }, - ctx, + ctx: Context, ) => { const { createdBy } = await ctx.getSession(); return ctx.prisma.session.createCustomSession(createdBy, session, true); diff --git a/api/tests/documentDrive.test.ts b/api/tests/documentDrive.test.ts index badb439d..91db20af 100644 --- a/api/tests/documentDrive.test.ts +++ b/api/tests/documentDrive.test.ts @@ -20,7 +20,7 @@ describe("Document Drive Server", () => { it("it syncs with listener", async () => { // add drive without listener const addDriveResponse = await addDrive(); - expect(addDriveResponse.global.id).not.toBeNull(); + expect(addDriveResponse.global.id).toBe("1"); // add file const pushUpdatesResponse = await addBudgetStatement();