From 4b5b3e4f6c090bdb34235a98553daa33e30afdbd Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Wed, 12 Jun 2024 22:10:24 +0200 Subject: [PATCH 1/6] change api to use uuid instead of dpid for claiming attestations, enabling draft nodes to claim attestations --- .../migration.sql | 2 ++ desci-server/prisma/schema.prisma | 2 +- desci-server/src/controllers/attestations/show.ts | 10 +++++----- desci-server/src/routes/v1/attestations/index.ts | 2 +- desci-server/src/routes/v1/attestations/schema.ts | 8 ++++---- desci-server/src/services/Attestation.ts | 5 +++-- 6 files changed, 16 insertions(+), 13 deletions(-) create mode 100644 desci-server/prisma/migrations/20240612181102_node_attestation_dpid_optional/migration.sql diff --git a/desci-server/prisma/migrations/20240612181102_node_attestation_dpid_optional/migration.sql b/desci-server/prisma/migrations/20240612181102_node_attestation_dpid_optional/migration.sql new file mode 100644 index 00000000..69548293 --- /dev/null +++ b/desci-server/prisma/migrations/20240612181102_node_attestation_dpid_optional/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "NodeAttestation" ALTER COLUMN "nodeDpid10" DROP NOT NULL; diff --git a/desci-server/prisma/schema.prisma b/desci-server/prisma/schema.prisma index 9890e9c0..47bdcd7c 100755 --- a/desci-server/prisma/schema.prisma +++ b/desci-server/prisma/schema.prisma @@ -751,7 +751,7 @@ model NodeAttestation { community DesciCommunity @relation(fields: [desciCommunityId], references: [id]) claimedById Int claimedBy User @relation(fields: [claimedById], references: [id]) - nodeDpid10 String + nodeDpid10 String? nodeUuid String node Node @relation(fields: [nodeUuid], references: [uuid]) nodeVersion Int diff --git a/desci-server/src/controllers/attestations/show.ts b/desci-server/src/controllers/attestations/show.ts index 26c8ee12..118666b3 100644 --- a/desci-server/src/controllers/attestations/show.ts +++ b/desci-server/src/controllers/attestations/show.ts @@ -21,21 +21,21 @@ type ShowNodeAttestationsResponse = { }; export const showNodeAttestations = async ( - req: Request<{ dpid: string }>, + req: Request<{ uuid: string }>, res: Response, ) => { - const { dpid } = req.params; + const { uuid } = req.params; const logger = parentLogger.child({ module: 'ATTESTATIONS::showNodeAttestationsController', user: (req as any).user, - dpid, + uuid, }); logger.trace(`showNodeAttestations`); - if (!dpid) throw new BadRequestError('DPID is required'); + if (!uuid) throw new BadRequestError('uuid is required'); - let attestations = await attestationService.getAllNodeAttestations(dpid); + let attestations = await attestationService.getAllNodeAttestations(uuid); attestations = attestations.map((att) => ({ ...att, _count: undefined, diff --git a/desci-server/src/routes/v1/attestations/index.ts b/desci-server/src/routes/v1/attestations/index.ts index 359e3693..50961b87 100644 --- a/desci-server/src/routes/v1/attestations/index.ts +++ b/desci-server/src/routes/v1/attestations/index.ts @@ -49,7 +49,7 @@ router.get( asyncHander(showCommunityClaims), ); -router.get('/:dpid', [validate(showNodeAttestationsSchema)], asyncHander(showNodeAttestations)); +router.get('/:uuid', [validate(showNodeAttestationsSchema)], asyncHander(showNodeAttestations)); router.get('/:claimId/reactions', [validate(getAttestationReactionsSchema)], asyncHander(getAttestationReactions)); router.get( '/:claimId/verifications', diff --git a/desci-server/src/routes/v1/attestations/schema.ts b/desci-server/src/routes/v1/attestations/schema.ts index a720edf2..7a8ec37e 100644 --- a/desci-server/src/routes/v1/attestations/schema.ts +++ b/desci-server/src/routes/v1/attestations/schema.ts @@ -12,7 +12,7 @@ export const showCommunityClaimsSchema = z.object({ export const showNodeAttestationsSchema = z.object({ params: z.object({ - dpid, + uuid: z.string(), }), }); @@ -149,7 +149,7 @@ export const claimAttestationSchema = z.object({ attestationId: z.coerce.number(), nodeVersion: z.coerce.number(), nodeUuid: z.string(), - nodeDpid: z.string(), + nodeDpid: z.string().optional(), claimerId: z.coerce.number(), }), }); @@ -159,14 +159,14 @@ export const claimEntryAttestationsSchema = z.object({ communityId: z.coerce.number(), nodeVersion: z.coerce.number(), nodeUuid: z.string(), - nodeDpid: z.string(), + nodeDpid: z.string().optional(), claimerId: z.coerce.number(), }), }); export const removeClaimSchema = z.object({ body: z.object({ - dpid, + dpid: z.coerce.number().optional(), nodeUuid: z.string(), claimId: z.coerce.number(), }), diff --git a/desci-server/src/services/Attestation.ts b/desci-server/src/services/Attestation.ts index 8ac23c94..c862354f 100644 --- a/desci-server/src/services/Attestation.ts +++ b/desci-server/src/services/Attestation.ts @@ -18,6 +18,7 @@ import { NoAccessError, VerificationError, VerificationNotFoundError, + ensureUuidEndsWithDot, logger, } from '../internal.js'; import { communityService } from '../internal.js'; @@ -229,9 +230,9 @@ export class AttestationService { }); } - async getAllNodeAttestations(dpid: string) { + async getAllNodeAttestations(uuid: string) { return prisma.nodeAttestation.findMany({ - where: { nodeDpid10: dpid, revoked: false }, + where: { nodeUuid: ensureUuidEndsWithDot(uuid), revoked: false }, include: { community: { select: { name: true, description: true, keywords: true, image_url: true } }, attestation: { select: { protected: true, verified_image_url: true } }, From 8ebc4072cc4b3d04cc5090035582dc955a747324 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Wed, 12 Jun 2024 22:34:04 +0200 Subject: [PATCH 2/6] ensure we can unclaim draft attestations, only show published nodes on community radar --- desci-server/src/controllers/attestations/claims.ts | 2 +- desci-server/src/services/Attestation.ts | 4 ++++ desci-server/src/services/Communities.ts | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/desci-server/src/controllers/attestations/claims.ts b/desci-server/src/controllers/attestations/claims.ts index 58e5c782..5e72e313 100644 --- a/desci-server/src/controllers/attestations/claims.ts +++ b/desci-server/src/controllers/attestations/claims.ts @@ -106,7 +106,7 @@ export const removeClaim = async (req: RequestWithUser, res: Response, _next: Ne const node = await prisma.node.findFirst({ where: { uuid: body.nodeUuid } }); if (!node) throw new NotFoundError('Node not found'); - const claim = await attestationService.getClaimOnDpid(body.claimId, body.dpid.toString()); + const claim = await attestationService.getClaimOnUuid(body.claimId, body.nodeUuid); if (!claim) throw new NotFoundError(); if (node.ownerId !== req.user.id || claim.claimedById !== req.user.id) throw new AuthFailureError(); diff --git a/desci-server/src/services/Attestation.ts b/desci-server/src/services/Attestation.ts index c862354f..5e8604d6 100644 --- a/desci-server/src/services/Attestation.ts +++ b/desci-server/src/services/Attestation.ts @@ -446,6 +446,10 @@ export class AttestationService { return prisma.nodeAttestation.findFirst({ where: { id, nodeDpid10 } }); } + async getClaimOnUuid(id: number, nodeUuid: string) { + return prisma.nodeAttestation.findFirst({ where: { id, nodeUuid } }); + } + async verifyClaim(nodeAttestationId: number, userId: number) { assert(nodeAttestationId > 0, 'Error: nodeAttestationId is Zero'); assert(userId > 0, 'Error: userId is Zero'); diff --git a/desci-server/src/services/Communities.ts b/desci-server/src/services/Communities.ts index 1b1d83e5..ab01d5a4 100644 --- a/desci-server/src/services/Communities.ts +++ b/desci-server/src/services/Communities.ts @@ -95,7 +95,7 @@ export class CommunityService { left outer JOIN "Annotation" ON t1."id" = "Annotation"."nodeAttestationId" left outer JOIN "NodeAttestationReaction" ON t1."id" = "NodeAttestationReaction"."nodeAttestationId" left outer JOIN "NodeAttestationVerification" ON t1."id" = "NodeAttestationVerification"."nodeAttestationId" - WHERE t1."revoked" = false AND + WHERE t1."revoked" = false AND t1."nodeDpid10" IS NOT NULL AND EXISTS (SELECT * from "CommunityEntryAttestation" c1 From 0dc3c48155974d81ebaf093f2f8c7993a008508c Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Wed, 12 Jun 2024 22:44:07 +0200 Subject: [PATCH 3/6] fix tests --- desci-server/test/integration/Attestation.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/desci-server/test/integration/Attestation.test.ts b/desci-server/test/integration/Attestation.test.ts index da7efad8..71a9cbc7 100644 --- a/desci-server/test/integration/Attestation.test.ts +++ b/desci-server/test/integration/Attestation.test.ts @@ -337,7 +337,7 @@ describe('Attestations Service', async () => { expect(res.status).to.equal(200); // verify only one claim exists on the old version of the node - const attestations = await attestationService.getAllNodeAttestations('1'); + const attestations = await attestationService.getAllNodeAttestations(node.uuid); expect(attestations.length).to.equal(2); }); }); @@ -1508,7 +1508,7 @@ describe('Attestations Service', async () => { it('should show DPID 1 node attestations(API)', async () => { const JwtToken = jwt.sign({ email: users[0].email }, process.env.JWT_SECRET!, { expiresIn: '1y' }); const authHeaderVal = `Bearer ${JwtToken}`; - const res = await request(app).get(`/v1/attestations/${1}`).set('authorization', authHeaderVal); + const res = await request(app).get(`/v1/attestations/${node1.uuid}`).set('authorization', authHeaderVal); const attestations: NodeAttestationFragment[] = res.body.data; console.log(attestations); expect(attestations.length).to.be.equal(3); @@ -1518,7 +1518,7 @@ describe('Attestations Service', async () => { it('should show DPID 2 node attestations(API)', async () => { const JwtToken = jwt.sign({ email: users[0].email }, process.env.JWT_SECRET!, { expiresIn: '1y' }); const authHeaderVal = `Bearer ${JwtToken}`; - const res = await request(app).get(`/v1/attestations/${2}`).set('authorization', authHeaderVal); + const res = await request(app).get(`/v1/attestations/${node2.uuid}`).set('authorization', authHeaderVal); const attestations: NodeAttestationFragment[] = res.body.data; expect(attestations.length).to.be.equal(4); }); @@ -2029,7 +2029,7 @@ describe('Attestations Service', async () => { }); expect(res.status).to.equal(200); - const claims = await attestationService.getAllNodeAttestations('1'); + const claims = await attestationService.getAllNodeAttestations(node.uuid); expect(claims.length).to.equal(1); }); @@ -2077,7 +2077,7 @@ describe('Attestations Service', async () => { }); expect(res.status).to.equal(200); - const attestations = await attestationService.getAllNodeAttestations('1'); + const attestations = await attestationService.getAllNodeAttestations(node.uuid); expect(attestations.length).to.equal(2); res = await request(app).get(`/v1/attestations/${1}`).set('authorization', authHeaderVal); From 5be71e33f4310491e1f9956b012c68aafef4e079 Mon Sep 17 00:00:00 2001 From: kadami <86646883+kadamidev@users.noreply.github.com> Date: Mon, 17 Jun 2024 16:55:14 +0000 Subject: [PATCH 4/6] fix broken test causing tests to hang --- desci-server/test/integration/Attestation.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desci-server/test/integration/Attestation.test.ts b/desci-server/test/integration/Attestation.test.ts index 71a9cbc7..05966d43 100644 --- a/desci-server/test/integration/Attestation.test.ts +++ b/desci-server/test/integration/Attestation.test.ts @@ -1508,7 +1508,7 @@ describe('Attestations Service', async () => { it('should show DPID 1 node attestations(API)', async () => { const JwtToken = jwt.sign({ email: users[0].email }, process.env.JWT_SECRET!, { expiresIn: '1y' }); const authHeaderVal = `Bearer ${JwtToken}`; - const res = await request(app).get(`/v1/attestations/${node1.uuid}`).set('authorization', authHeaderVal); + const res = await request(app).get(`/v1/attestations/${node.uuid}`).set('authorization', authHeaderVal); const attestations: NodeAttestationFragment[] = res.body.data; console.log(attestations); expect(attestations.length).to.be.equal(3); From 1b8986d591b4cc1568fcafaf6af79be9aefe833d Mon Sep 17 00:00:00 2001 From: kadami <86646883+kadamidev@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:06:54 +0000 Subject: [PATCH 5/6] fix last test for uuid changes --- desci-server/test/integration/Attestation.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desci-server/test/integration/Attestation.test.ts b/desci-server/test/integration/Attestation.test.ts index 05966d43..45a484d8 100644 --- a/desci-server/test/integration/Attestation.test.ts +++ b/desci-server/test/integration/Attestation.test.ts @@ -2080,7 +2080,7 @@ describe('Attestations Service', async () => { const attestations = await attestationService.getAllNodeAttestations(node.uuid); expect(attestations.length).to.equal(2); - res = await request(app).get(`/v1/attestations/${1}`).set('authorization', authHeaderVal); + res = await request(app).get(`/v1/attestations/${node.uuid}`).set('authorization', authHeaderVal); const claims = res.body.data as NodeClaim[]; const revoked = claims.find((c) => c.id === claim.id); expect(revoked?.revoked).to.be.false; From c8ed676993b10257937f1608c6b0653b45e90a76 Mon Sep 17 00:00:00 2001 From: Sina Iman Date: Wed, 19 Jun 2024 12:49:58 +0200 Subject: [PATCH 6/6] support updating node attestations with dpid --- desci-server/src/controllers/nodes/publish.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/desci-server/src/controllers/nodes/publish.ts b/desci-server/src/controllers/nodes/publish.ts index 47e5c18e..6ac3a4dc 100644 --- a/desci-server/src/controllers/nodes/publish.ts +++ b/desci-server/src/controllers/nodes/publish.ts @@ -41,7 +41,21 @@ export type PublishResBody = | { error: string; }; - +async function updateAssociatedAttestations(nodeUuid: string, dpid: string) { + const logger = parentLogger.child({ + // id: req.id, + module: 'NODE::publishController', + }); + logger.info({ nodeUuid, dpid }, `[updateAssociatedAttestations]`); + return await prisma.nodeAttestation.updateMany({ + where: { + nodeUuid, + }, + data: { + nodeDpid10: dpid, + }, + }); +} // call node publish service and add job to queue export const publish = async (req: PublishRequest, res: Response, _next: NextFunction) => { const { uuid, cid, manifest, transactionId, ceramicStream, commitId } = req.body; @@ -132,7 +146,7 @@ export const publish = async (req: PublishRequest, res: Response status: PublishTaskQueueStatus.WAITING, }, }); - + updateAssociatedAttestations(node.uuid, manifest.dpid.id); return res.send({ ok: true, taskId: publishTask.id,