diff --git a/packages/api/package.json b/packages/api/package.json index 76906a6f1..c7ed1be8c 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -88,6 +88,7 @@ "ts-loader": "^9.3.0", "typeorm": "^0.3.4", "typeorm-naming-strategies": "^4.1.0", + "underscore": "^1.13.6", "urlsafe-base64": "^1.0.0", "uuid": "^8.3.1", "voca": "^1.4.0", diff --git a/packages/api/src/resolvers/highlight/index.ts b/packages/api/src/resolvers/highlight/index.ts index d14419c03..d2783a26d 100644 --- a/packages/api/src/resolvers/highlight/index.ts +++ b/packages/api/src/resolvers/highlight/index.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { authorized } from '../../utils/helpers' +import { authorized, unescapeHtml } from '../../utils/helpers' import { CreateHighlightError, CreateHighlightErrorCode, @@ -219,9 +219,14 @@ export const updateHighlightResolver = authorized< } } + // unescape HTML entities + const annotation = input.annotation + ? unescapeHtml(input.annotation) + : undefined + const updatedHighlight: HighlightData = { ...highlight, - annotation: input.annotation, + annotation, updatedAt: new Date(), } diff --git a/packages/api/src/utils/helpers.ts b/packages/api/src/utils/helpers.ts index 45fefe83c..76ad7776c 100644 --- a/packages/api/src/utils/helpers.ts +++ b/packages/api/src/utils/helpers.ts @@ -17,6 +17,7 @@ import { updatePage } from '../elastic/pages' import path from 'path' import normalizeUrl from 'normalize-url' import wordsCounter from 'word-counting' +import _ from 'underscore' interface InputObject { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -283,3 +284,7 @@ export const generateRandomColor = (): string => { .toUpperCase() ) } + +export const unescapeHtml = (html: string): string => { + return _.unescape(html) +} diff --git a/packages/api/test/resolvers/highlight.test.ts b/packages/api/test/resolvers/highlight.test.ts index ca5e85967..d179c0ba0 100644 --- a/packages/api/test/resolvers/highlight.test.ts +++ b/packages/api/test/resolvers/highlight.test.ts @@ -12,7 +12,7 @@ import { User } from '../../src/entity/user' import chaiString from 'chai-string' import { createPubSubClient } from '../../src/datalayer/pubsub' import { PageContext } from '../../src/elastic/types' -import { deletePage } from '../../src/elastic/pages' +import { deletePage, updatePage } from '../../src/elastic/pages' chai.use(chaiString) @@ -101,6 +101,33 @@ const mergeHighlightQuery = ( ` } +const updateHighlightQuery = ( + authToken: string, + highlightId: string, + annotation = '_annotation' +) => { + return ` + mutation { + updateHighlight( + input: { + annotation: "${annotation}", + highlightId: "${highlightId}", + } + ) { + ... on UpdateHighlightSuccess { + highlight { + id + annotation + } + } + ... on UpdateHighlightError { + errorCodes + } + } + } + ` +} + describe('Highlights API', () => { let authToken: string let user: User @@ -116,7 +143,7 @@ describe('Highlights API', () => { authToken = res.body.authToken pageId = (await createTestElasticPage(user.id)).id - ctx = { pubsub: createPubSubClient(), uid: user.id } + ctx = { pubsub: createPubSubClient(), uid: user.id, refresh: true } }) after(async () => { @@ -192,4 +219,42 @@ describe('Highlights API', () => { ).to.eq(highlightPositionAnchorIndex) }) }) + + describe('updateHighlightMutation', () => { + let highlightId: string + + before(async () => { + // create test highlight + highlightId = generateFakeUuid() + await updatePage( + pageId, + { + highlights: [ + { + id: highlightId, + shortId: '_short_id_3', + annotation: '', + userId: user.id, + patch: '', + quote: '', + createdAt: new Date(), + updatedAt: new Date(), + }, + ], + }, + ctx + ) + }) + + context('when the annotation has HTML reserved characters', () => { + it('unescapes the annotation and updates', async () => { + const annotation = '> This is a test' + const query = updateHighlightQuery(authToken, highlightId, annotation) + const res = await graphqlRequest(query, authToken).expect(200) + expect(res.body.data.updateHighlight.highlight.annotation).to.eql( + '> This is a test' + ) + }) + }) + }) })