diff --git a/packages/api/src/repository/highlight.ts b/packages/api/src/repository/highlight.ts index b2eaab20f..543669170 100644 --- a/packages/api/src/repository/highlight.ts +++ b/packages/api/src/repository/highlight.ts @@ -34,6 +34,10 @@ export const highlightRepository = entityManager return this.save(unescapeHighlight(highlight)) }, + createAndSaves(highlights: DeepPartial[]) { + return this.save(highlights.map(unescapeHighlight)) + }, + updateAndSave( highlightId: string, highlight: QueryDeepPartialEntity diff --git a/packages/api/src/resolvers/recommendations/index.ts b/packages/api/src/resolvers/recommendations/index.ts index cbfcbc9cf..0a972d318 100644 --- a/packages/api/src/resolvers/recommendations/index.ts +++ b/packages/api/src/resolvers/recommendations/index.ts @@ -46,20 +46,10 @@ export const createGroupResolver = authorized< CreateGroupSuccess, CreateGroupError, MutationCreateGroupArgs ->(async (_, { input }, { claims: { uid }, log }) => { - log.info('Creating group', { - input, - labels: { - source: 'resolver', - resolver: 'createGroupResolver', - uid, - }, - }) - +>(async (_, { input }, { uid, log }) => { try { - const userData = await userRepository.findOne({ - where: { id: uid }, - relations: ['profile'], + const userData = await userRepository.findOneBy({ + id: uid, }) if (!userData) { return { @@ -106,14 +96,7 @@ export const createGroupResolver = authorized< }, } } catch (error) { - log.error('Error creating group', { - error, - labels: { - source: 'resolver', - resolver: 'createGroupResolver', - uid, - }, - }) + log.error('Error creating group', error) return { errorCodes: [CreateGroupErrorCode.BadRequest], @@ -122,15 +105,7 @@ export const createGroupResolver = authorized< }) export const groupsResolver = authorized( - async (_, __, { claims: { uid }, log }) => { - log.info('Getting groups', { - labels: { - source: 'resolver', - resolver: 'groupsResolver', - uid, - }, - }) - + async (_, __, { uid, log }) => { try { const user = await userRepository.findOneBy({ id: uid, @@ -168,15 +143,6 @@ export const recommendResolver = authorized< RecommendError, MutationRecommendArgs >(async (_, { input }, { uid, log, signToken }) => { - log.info('Recommend', { - input, - labels: { - source: 'resolver', - resolver: 'recommendResolver', - uid, - }, - }) - try { const item = await findLibraryItemById(input.pageId, uid) if (!item) { @@ -208,11 +174,11 @@ export const recommendResolver = authorized< member.user.id, item.id, { - group, - note: input.note ?? null, - recommender: item.user, + group: { id: group.id }, + note: input.note, + recommender: { id: uid }, createdAt: new Date(), - libraryItem: item, + libraryItem: { id: item.id }, }, auth, recommendedHighlightIds diff --git a/packages/api/src/routers/page_router.ts b/packages/api/src/routers/page_router.ts index 5f63c0b7b..990a49951 100644 --- a/packages/api/src/routers/page_router.ts +++ b/packages/api/src/routers/page_router.ts @@ -162,7 +162,7 @@ export function pageRouter() { return res.status(400).send({ errorCode: 'BAD_DATA' }) } - const item = await findLibraryItemById(itemId, userId) + const item = await findLibraryItemById(itemId, claims.uid) if (!item) { return res.status(404).send({ errorCode: 'NOT_FOUND' }) } @@ -170,7 +170,7 @@ export function pageRouter() { const recommendedItem = await addRecommendation( item, recommendation, - claims.uid, + userId, highlightIds ) if (!recommendedItem) { diff --git a/packages/api/src/services/highlights.ts b/packages/api/src/services/highlights.ts index 422b24d7e..6ae2bd9b4 100644 --- a/packages/api/src/services/highlights.ts +++ b/packages/api/src/services/highlights.ts @@ -20,6 +20,31 @@ export const getHighlightLocation = (patch: string): number | undefined => { export const getHighlightUrl = (slug: string, highlightId: string): string => `${homePageURL()}/me/${slug}#${highlightId}` +export const createHighlights = async ( + highlights: DeepPartial[], + libraryItemId: string, + userId: string, + pubsub = createPubSubClient() +) => { + const newHighlights = await authTrx( + async (tx) => + tx.withRepository(highlightRepository).createAndSaves(highlights), + undefined, + userId + ) + + await pubsub.entityCreated( + EntityType.HIGHLIGHT, + newHighlights.map((highlight) => ({ + ...highlight, + pageId: libraryItemId, + })), + userId + ) + + return newHighlights +} + export const createHighlight = async ( highlight: DeepPartial, libraryItemId: string, diff --git a/packages/api/src/services/library_item.ts b/packages/api/src/services/library_item.ts index 3be540b13..40c0a4cdc 100644 --- a/packages/api/src/services/library_item.ts +++ b/packages/api/src/services/library_item.ts @@ -311,6 +311,7 @@ export const findLibraryItemById = async ( .createQueryBuilder(LibraryItem, 'library_item') .leftJoinAndSelect('library_item.labels', 'labels') .leftJoinAndSelect('library_item.highlights', 'highlights') + .leftJoinAndSelect('highlights.user', 'user') .where('library_item.id = :id', { id }) .getOne(), undefined, diff --git a/packages/api/src/services/recommendation.ts b/packages/api/src/services/recommendation.ts index dc6c85d9e..933123bfe 100644 --- a/packages/api/src/services/recommendation.ts +++ b/packages/api/src/services/recommendation.ts @@ -1,12 +1,11 @@ +import { nanoid } from 'nanoid' import { DeepPartial } from 'typeorm' import { LibraryItem } from '../entity/library_item' import { Recommendation } from '../entity/recommendation' +import { getRepository } from '../repository' import { logger } from '../utils/logger' -import { - createLibraryItem, - findLibraryItemByUrl, - updateLibraryItem, -} from './library_item' +import { createHighlights } from './highlights' +import { createLibraryItem, findLibraryItemByUrl } from './library_item' export const addRecommendation = async ( item: LibraryItem, @@ -15,65 +14,83 @@ export const addRecommendation = async ( highlightIds?: string[] ) => { try { - const highlights = item.highlights?.filter((highlight) => - highlightIds?.includes(highlight.id) - ) - // check if the item is already recommended to the group - const existingItem = await findLibraryItemByUrl(item.originalUrl, userId) - if (existingItem) { - const existingHighlights = existingItem.highlights || [] + let recommendedItem = await findLibraryItemByUrl(item.originalUrl, userId) + // if (existingItem) { + // const existingHighlights = existingItem.highlights || [] - // remove duplicates - const newHighlights = - highlights?.filter( - (highlight) => - !existingHighlights.find( - (existingHighlight) => existingHighlight.quote === highlight.quote - ) - ) || [] + // // remove duplicates + // const newHighlights = + // highlights?.filter( + // (highlight) => + // !existingHighlights.find( + // (existingHighlight) => existingHighlight.quote === highlight.quote + // ) + // ) || [] - const existingRecommendations = existingItem.recommendations || [] + // return existingItem + // } - // update recommendations in the existing item - await updateLibraryItem( - existingItem.id, - { - recommendations: existingRecommendations.concat(recommendation), - highlights: existingHighlights.concat(newHighlights), - }, - userId - ) + if (!recommendedItem) { + // create a new item + const newItem: DeepPartial = { + user: { id: userId }, + slug: item.slug, + title: item.title, + author: item.author, + description: item.description, + originalUrl: item.originalUrl, + originalContent: item.originalContent, + contentReader: item.contentReader, + directionality: item.directionality, + itemLanguage: item.itemLanguage, + itemType: item.itemType, + readableContent: item.readableContent, + siteIcon: item.siteIcon, + siteName: item.siteName, + thumbnail: item.thumbnail, + uploadFile: item.uploadFile, + wordCount: item.wordCount, + } - return existingItem + recommendedItem = await createLibraryItem(newItem, userId) } - // create a new item - const newItem: DeepPartial = { - recommendations: [recommendation], - user: { id: userId }, - highlights, - slug: item.slug, - title: item.title, - author: item.author, - description: item.description, - originalUrl: item.originalUrl, - originalContent: item.originalContent, - contentReader: item.contentReader, - directionality: item.directionality, - itemLanguage: item.itemLanguage, - itemType: item.itemType, - readableContent: item.readableContent, - siteIcon: item.siteIcon, - siteName: item.siteName, - thumbnail: item.thumbnail, - uploadFile: item.uploadFile, - wordCount: item.wordCount, + const highlights = item.highlights + ?.filter((highlight) => highlightIds?.includes(highlight.id)) + .map((highlight) => ({ + shortId: nanoid(8), + createdAt: new Date(), + libraryItem: { id: recommendedItem?.id }, + user: { id: userId }, + quote: highlight.quote, + annotation: highlight.annotation, + prefix: highlight.prefix, + suffix: highlight.suffix, + patch: highlight.patch, + updatedAt: new Date(), + sharedAt: new Date(), + html: highlight.html, + color: highlight.color, + })) + if (highlights) { + await createHighlights(highlights, recommendedItem.id, userId) } - return createLibraryItem(newItem, userId) + await createRecommendation({ + ...recommendation, + libraryItem: { id: recommendedItem.id }, + }) + + return recommendedItem } catch (err) { logger.error('Error adding recommendation', err) return null } } + +export const createRecommendation = async ( + recommendation: DeepPartial +) => { + return getRepository(Recommendation).save(recommendation) +} diff --git a/packages/api/src/utils/createTask.ts b/packages/api/src/utils/createTask.ts index 3ff472853..370d3d726 100644 --- a/packages/api/src/utils/createTask.ts +++ b/packages/api/src/utils/createTask.ts @@ -5,6 +5,7 @@ import { CloudTasksClient, protos } from '@google-cloud/tasks' import { google } from '@google-cloud/tasks/build/protos/protos' import axios from 'axios' import { nanoid } from 'nanoid' +import { DeepPartial } from 'typeorm' import { Recommendation } from '../entity/recommendation' import { Subscription } from '../entity/subscription' import { env } from '../env' @@ -443,7 +444,7 @@ export const enqueueTextToSpeech = async ({ export const enqueueRecommendation = async ( userId: string, itemId: string, - recommendation: Partial, + recommendation: DeepPartial, authToken: string, highlightIds?: string[] ): Promise => { diff --git a/packages/db/elastic_migrations/migrate_from_elastic.py b/packages/db/elastic_migrations/migrate_from_elastic.py index 0546fdf4f..02807a617 100755 --- a/packages/db/elastic_migrations/migrate_from_elastic.py +++ b/packages/db/elastic_migrations/migrate_from_elastic.py @@ -11,7 +11,7 @@ from elasticsearch.helpers import async_scan PG_HOST = os.getenv('PG_HOST', 'localhost') PG_PORT = os.getenv('PG_PORT', 5432) -PG_USER = os.getenv('PG_USER', 'app_user') +PG_USER = os.getenv('PG_USER', 'hongbo') PG_PASSWORD = os.getenv('PG_PASSWORD', 'app_pass') PG_DB = os.getenv('PG_DB', 'omnivore') ES_URL = os.getenv('ES_URL', 'http://localhost:9200')