Skip to content

Commit

Permalink
Merge pull request #531 from desci-labs/highlight-adaptation
Browse files Browse the repository at this point in the history
Add support for Comments on Nodes
  • Loading branch information
shadrach-tayo authored Oct 9, 2024
2 parents d34bef2 + 021b29f commit 3f4fb08
Show file tree
Hide file tree
Showing 19 changed files with 299 additions and 67 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- DropForeignKey
ALTER TABLE "Annotation" DROP CONSTRAINT "Annotation_nodeAttestationId_fkey";

-- AlterTable
ALTER TABLE "Annotation" ADD COLUMN "nodeId" INTEGER,
ADD COLUMN "uuid" TEXT,
ADD COLUMN "visible" BOOLEAN NOT NULL DEFAULT true,
ALTER COLUMN "nodeAttestationId" DROP NOT NULL;

-- AddForeignKey
ALTER TABLE "Annotation" ADD CONSTRAINT "Annotation_nodeAttestationId_fkey" FOREIGN KEY ("nodeAttestationId") REFERENCES "NodeAttestation"("id") ON DELETE SET NULL ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Annotation" ADD CONSTRAINT "Annotation_uuid_fkey" FOREIGN KEY ("uuid") REFERENCES "Node"("uuid") ON DELETE SET NULL ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Warnings:
- You are about to drop the column `nodeId` on the `Annotation` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "Annotation" DROP COLUMN "nodeId";
16 changes: 10 additions & 6 deletions desci-server/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ model Node {
DoiSubmissionQueue DoiSubmissionQueue[]
BookmarkedNode BookmarkedNode[]
DeferredEmails DeferredEmails[]
Annotation Annotation[]
@@index([ownerId])
@@index([uuid])
Expand Down Expand Up @@ -811,16 +812,19 @@ enum EmailType {

//Comments on attestations
model Annotation {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
type AnnotationType
body String
highlights Json[] @default([])
links String[] @default([])
highlights Json[] @default([])
links String[] @default([])
authorId Int
author User @relation(fields: [authorId], references: [id])
nodeAttestationId Int
attestation NodeAttestation @relation(fields: [nodeAttestationId], references: [id])
author User @relation(fields: [authorId], references: [id])
nodeAttestationId Int?
attestation NodeAttestation? @relation(fields: [nodeAttestationId], references: [id])
deferredEmailsId Int?
uuid String?
node Node? @relation(fields: [uuid], references: [uuid])
visible Boolean @default(true)
}

//An emoji reaction to a node
Expand Down
19 changes: 15 additions & 4 deletions desci-server/src/controllers/attestations/comments.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HighlightBlock } from '@desci-labs/desci-models';
import { ActionType, Annotation } from '@prisma/client';
import { ActionType, Annotation, AnnotationType } from '@prisma/client';
import { NextFunction, Request, Response } from 'express';
import _ from 'lodash';
import zod from 'zod';
Expand All @@ -13,7 +13,9 @@ import {
asyncMap,
attestationService,
createCommentSchema,
ensureUuidEndsWithDot,
logger as parentLogger,
prisma,
} from '../../internal.js';
import { saveInteraction } from '../../services/interactionLog.js';
import { client } from '../../services/ipfs.js';
Expand Down Expand Up @@ -78,7 +80,7 @@ export const removeComment = async (req: Request<RemoveCommentBody, any, any>, r
type AddCommentBody = zod.infer<typeof createCommentSchema>;

export const addComment = async (req: Request<any, any, AddCommentBody['body']>, res: Response<AddCommentResponse>) => {
const { authorId, claimId, body, highlights, links } = req.body;
const { authorId, claimId, body, highlights, links, uuid, visible } = req.body;
const user = (req as any).user;

if (parseInt(authorId.toString()) !== user.id) throw new ForbiddenError();
Expand All @@ -89,6 +91,11 @@ export const addComment = async (req: Request<any, any, AddCommentBody['body']>,
user: (req as any).user,
body: req.body,
});

if (uuid) {
const node = await prisma.node.findFirst({ where: { uuid: ensureUuidEndsWithDot(uuid) } });
if (!node) throw new NotFoundError('Node with uuid ${uuid} not found');
}
logger.trace(`addComment`);

let annotation: Annotation;
Expand All @@ -102,19 +109,23 @@ export const addComment = async (req: Request<any, any, AddCommentBody['body']>,
});
logger.info({ processedHighlights }, 'processedHighlights');
annotation = await attestationService.createHighlight({
claimId: parseInt(claimId.toString()),
claimId: claimId && parseInt(claimId.toString()),
authorId: user.id,
comment: body,
links,
highlights: processedHighlights as unknown as HighlightBlock[],
visible,
...(uuid && { uuid: ensureUuidEndsWithDot(uuid) }),
});
await saveInteraction(req, ActionType.ADD_COMMENT, { annotationId: annotation.id, claimId, authorId });
} else {
annotation = await attestationService.createComment({
claimId: parseInt(claimId.toString()),
claimId: claimId && parseInt(claimId.toString()),
authorId: user.id,
comment: body,
links,
visible,
...(uuid && { uuid: ensureUuidEndsWithDot(uuid) }),
});
}
await saveInteraction(req, ActionType.ADD_COMMENT, { annotationId: annotation.id, claimId, authorId });
Expand Down
3 changes: 1 addition & 2 deletions desci-server/src/controllers/attestations/recommendations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export const getValidatedAttestations = async (req: Request, res: Response, _nex
logger.info({ communityName });
const community = await communityService.findCommunityByNameOrSlug(communityName);
if (!community) throw new NotFoundError('Community not found');
logger.info({ community });

const attestations = await attestationService.getCommunityAttestations({
communityId: community.id,
Expand All @@ -83,6 +82,6 @@ export const getValidatedRecommendations = async (req: Request, res: Response, _
communityName: attestation.community.name,
AttestationVersion: attestation.AttestationVersion[0],
}));
logger.info({ attestations }, 'getValidatedRecommendations');
logger.info({ recommendations: attestations.length }, 'getValidatedRecommendations');
return new SuccessResponse(response).send(res);
};
35 changes: 35 additions & 0 deletions desci-server/src/controllers/nodes/comments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Response, NextFunction } from 'express';
import _ from 'lodash';
import z from 'zod';

import {
NotFoundError,
RequestWithNode,
SuccessResponse,
attestationService,
ensureUuidEndsWithDot,
getCommentsSchema,
logger,
prisma,
} from '../../internal.js';

export const getGeneralComments = async (req: RequestWithNode, res: Response, _next: NextFunction) => {
const { uuid } = req.params as z.infer<typeof getCommentsSchema>['params'];
const node = await prisma.node.findFirst({ where: { uuid: ensureUuidEndsWithDot(uuid) } });
if (!node) throw new NotFoundError("Can't comment on unknown research object");

const restrictVisibility = node.ownerId !== req?.user?.id;

logger.info({ restrictVisibility }, 'Query Comments');
const comments = await attestationService.getComments({
uuid: ensureUuidEndsWithDot(uuid),
...(restrictVisibility && { visible: true }),
});

const data = comments.map((comment) => {
const author = _.pick(comment.author, ['id', 'name', 'orcid']);
return { ...comment, author, highlights: comment.highlights.map((h) => JSON.parse(h as string)) };
});

return new SuccessResponse(data).send(res);
};
20 changes: 5 additions & 15 deletions desci-server/src/controllers/nodes/createDpid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import { ethers } from 'ethers';
import { Response } from 'express';
import { Logger } from 'pino';

import { CERAMIC_API_URL } from '../../config/index.js';
import { logger as parentLogger } from '../../logger.js';
import { RequestWithNode } from '../../middleware/authorisation.js';
import { setDpidAlias } from '../../services/nodeManager.js';
import { CERAMIC_API_URL } from '../../config/index.js';
import { getAliasRegistry, getHotWallet, getRegistryOwnerWallet } from '../../services/chain.js';
import { setDpidAlias } from '../../services/nodeManager.js';

type DpidResponse = DpidSuccessResponse | DpidErrorResponse;
export type DpidSuccessResponse = {
Expand Down Expand Up @@ -56,9 +56,7 @@ export const createDpid = async (req: RequestWithNode, res: Response<DpidRespons
}
};

export const getOrCreateDpid = async (
streamId: string,
): Promise<number> => {
export const getOrCreateDpid = async (streamId: string): Promise<number> => {
const logger = parentLogger.child({
module: 'NODE::mintDpid',
ceramicStream: streamId,
Expand Down Expand Up @@ -97,10 +95,7 @@ export const getOrCreateDpid = async (
* Note: this method in the registry contract is only callable by contract
* owner, so this is not generally available.
*/
export const upgradeDpid = async (
dpid: number,
ceramicStream: string
): Promise<number> => {
export const upgradeDpid = async (dpid: number, ceramicStream: string): Promise<number> => {
const logger = parentLogger.child({
module: 'NODE::upgradeDpid',
ceramicStream,
Expand Down Expand Up @@ -130,12 +125,7 @@ export const upgradeDpid = async (
* This should be checked before upgrading a dPID, to make sure
* the new stream accurately represents the publish history.
*/
const validateHistory = async (
dpid: number,
ceramicStream: string,
registry: DpidAliasRegistry,
logger: Logger
) => {
const validateHistory = async (dpid: number, ceramicStream: string, registry: DpidAliasRegistry, logger: Logger) => {
const client = newCeramicClient(CERAMIC_API_URL);
const legacyEntry = await registry.legacyLookup(dpid);

Expand Down
5 changes: 5 additions & 0 deletions desci-server/src/controllers/nodes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ export * from './nodesCover.js';
export * from './manager.js';
export * from './metadata.js';
export * from './doi.js';
export * from './comments.js';
export * from './sharedNodes.js';
export * from './searchNodes.js';
export * from './versionDetails.js';
export * from './thumbnails.js';
1 change: 1 addition & 0 deletions desci-server/src/controllers/nodes/prepublish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export const prepublish = async (req: RequestWithNode, res: Response<PrepublishR
skipDuplicates: true,
});

logger.info({ nodeFileTreeDagCid }, 'publishDraftComments::Root');
logger.info(
`[Prepublish DAG Skeleton Refs] Created ${createdPublicRefs.count} public data refs for node ${node.id}`,
);
Expand Down
21 changes: 20 additions & 1 deletion desci-server/src/controllers/nodes/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ActionType, Node, Prisma, PublishTaskQueue, PublishTaskQueueStatus, Use
import { Request, Response, NextFunction } from 'express';

import { prisma } from '../../client.js';
import { attestationService } from '../../internal.js';
import { logger as parentLogger } from '../../logger.js';
import { getManifestByCid } from '../../services/data/processing.js';
import { getTargetDpidUrl } from '../../services/fixDpid.js';
Expand All @@ -15,6 +16,7 @@ import {
setDpidAlias,
} from '../../services/nodeManager.js';
import { publishServices } from '../../services/PublishServices.js';
import { getIndexedResearchObjects } from '../../theGraph.js';
import { discordNotify } from '../../utils/discordUtils.js';
import { ensureUuidEndsWithDot } from '../../utils.js';

Expand Down Expand Up @@ -69,7 +71,6 @@ export const publish = async (req: PublishRequest, res: Response<PublishResBody>
body: req.body,
uuid,
cid,
manifest,
transactionId,
ceramicStream,
commitId,
Expand Down Expand Up @@ -160,6 +161,24 @@ export const publish = async (req: PublishRequest, res: Response<PublishResBody>

updateAssociatedAttestations(node.uuid, dpidAlias ? dpidAlias.toString() : manifest.dpid?.id);

const root = await prisma.publicDataReference.findFirst({
where: { nodeId: node.id, root: true, userId: owner.id },
orderBy: { updatedAt: 'desc' },
});
const result = await getIndexedResearchObjects([ensureUuidEndsWithDot(uuid)]);
// if node is being published for the first time default to 1
const version = result ? result.researchObjects?.[0]?.versions.length : 1;
logger.info({ root, result, version }, 'publishDraftComments::Root');

// publish draft comments
await attestationService.publishDraftComments({
node,
userId: owner.id,
dpidAlias: dpidAlias ?? parseInt(manifest.dpid?.id),
rootCid: root.rootCid,
version,
});

return res.send({
ok: true,
dpid: dpidAlias ?? parseInt(manifest.dpid?.id),
Expand Down
8 changes: 3 additions & 5 deletions desci-server/src/controllers/raw/versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getIndexedResearchObjects, IndexedResearchObject } from '../../theGraph
import { ensureUuidEndsWithDot } from '../../utils.js';

const logger = parentLogger.child({
module: "RAW::versionsController"
module: 'RAW::versionsController',
});

/**
Expand All @@ -19,12 +19,10 @@ export const versions = async (req: Request, res: Response, next: NextFunction)
const { researchObjects } = await getIndexedResearchObjects([uuid]);
result = researchObjects[0];
} catch (err) {
logger.error(
{ result, err }, `[ERROR] graph lookup fail ${err.message}`,
);
logger.error({ result, err }, `[ERROR] graph lookup fail ${err.message}`);
}
if (!result) {
logger.warn({ uuid, result }, "could not find indexed versions");
logger.warn({ uuid, result }, 'could not find indexed versions');
res.status(404).send({ ok: false, msg: `could not locate uuid ${uuid}` });
return;
}
Expand Down
2 changes: 1 addition & 1 deletion desci-server/src/routes/v1/attestations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ router.post('/claim', [ensureUser, validate(claimAttestationSchema)], asyncHandl
router.post('/unclaim', [ensureUser, validate(removeClaimSchema)], asyncHandler(removeClaim));
router.post('/claimAll', [ensureUser, validate(claimEntryAttestationsSchema)], asyncHandler(claimEntryRequirements));

router.post('/comment', [ensureUser, validate(createCommentSchema)], asyncHandler(addComment));
router.post('/comments', [ensureUser, validate(createCommentSchema)], asyncHandler(addComment));
router.post('/reaction', [ensureUser, validate(addReactionSchema)], asyncHandler(addReaction));
router.post('/verification', [ensureUser, validate(addVerificationSchema)], asyncHandler(addVerification));

Expand Down
Loading

0 comments on commit 3f4fb08

Please sign in to comment.