diff --git a/packages/api/src/resolvers/function_resolvers.ts b/packages/api/src/resolvers/function_resolvers.ts index 1b957a371..50cd8580b 100644 --- a/packages/api/src/resolvers/function_resolvers.ts +++ b/packages/api/src/resolvers/function_resolvers.ts @@ -100,6 +100,7 @@ import { generateUploadFilePathName, } from '../utils/uploads' import { getPageByParam } from '../elastic/pages' +import { recentSearchesResolver } from './recent_searches' /* eslint-disable @typescript-eslint/naming-convention */ type ResultResolveType = { @@ -196,6 +197,7 @@ export const functionResolvers = { typeaheadSearch: typeaheadSearchResolver, updatesSince: updatesSinceResolver, integrations: integrationsResolver, + recentSearches: recentSearchesResolver, }, User: { async sharedArticles( @@ -604,4 +606,5 @@ export const functionResolvers = { ...resultResolveTypeResolver('SetIntegration'), ...resultResolveTypeResolver('Integrations'), ...resultResolveTypeResolver('DeleteIntegration'), + ...resultResolveTypeResolver('RecentSearches'), } diff --git a/packages/api/src/resolvers/recent_searches/index.ts b/packages/api/src/resolvers/recent_searches/index.ts new file mode 100644 index 000000000..d8efadf9e --- /dev/null +++ b/packages/api/src/resolvers/recent_searches/index.ts @@ -0,0 +1,36 @@ +import { authorized } from '../../utils/helpers' +import { + RecentSearchesError, + RecentSearchesErrorCode, + RecentSearchesSuccess, +} from '../../generated/graphql' +import { analytics } from '../../utils/analytics' +import { env } from '../../env' +import { getRepository } from '../../entity/utils' +import { User } from '../../entity/user' +import { getRecentSearches } from '../../services/search_history' + +export const recentSearchesResolver = authorized< + RecentSearchesSuccess, + RecentSearchesError +>(async (_obj, _params, { claims: { uid }, log }) => { + log.info('recentSearches') + + analytics.track({ + userId: uid, + event: 'recentSearches', + properties: { + env: env.server.apiEnv, + }, + }) + + const user = await getRepository(User).findOneBy({ id: uid }) + if (!user) { + return { errorCodes: [RecentSearchesErrorCode.Unauthorized] } + } + + const searches = await getRecentSearches(uid) + return { + searches, + } +}) diff --git a/packages/api/src/services/search_history.ts b/packages/api/src/services/search_history.ts index 2521ea64a..561d04782 100644 --- a/packages/api/src/services/search_history.ts +++ b/packages/api/src/services/search_history.ts @@ -17,7 +17,7 @@ export const saveSearchHistory = async ( term: string ): Promise => { const searchHistory = new SearchHistory() - searchHistory.user = { id: userId } as any + searchHistory.user.id = userId searchHistory.term = term searchHistory.createdAt = new Date() await getRepository(SearchHistory).save(searchHistory) diff --git a/packages/api/test/resolvers/recent_searches.test.ts b/packages/api/test/resolvers/recent_searches.test.ts new file mode 100644 index 000000000..4d6a95c67 --- /dev/null +++ b/packages/api/test/resolvers/recent_searches.test.ts @@ -0,0 +1,74 @@ +import 'mocha' +import { expect } from 'chai' +import { User } from '../../src/entity/user' +import { PageContext } from '../../src/elastic/types' +import { createTestUser, deleteTestUser } from '../db' +import { graphqlRequest, request } from '../util' +import { createPubSubClient } from '../../src/datalayer/pubsub' +import { getRepository } from '../../src/entity/utils' +import { SearchHistory } from '../../src/entity/search_history' + +describe('recent_searches resolver', () => { + let user: User + let authToken: string + let ctx: PageContext + + before(async () => { + // create fake user and login + user = await createTestUser('fakeUser') + const res = await request + .post('/local/debug/fake-user-login') + .send({ fakeEmail: user.email }) + authToken = res.body.authToken + ctx = { + pubsub: createPubSubClient(), + refresh: true, + uid: user.id, + } + }) + + after(async () => { + // clean up + await deleteTestUser(user.name) + }) + + describe('recentSearches API', () => { + const recentSearchesQuery = ` + query { + recentSearches { + ... on RecentSearchesSuccess { + searches { + term + } + } + } + } + ` + + before(async () => { + // create fake recent searches + await getRepository(SearchHistory).save([ + { + user: { id: user.id }, + term: 'test1', + }, + { + user: { id: user.id }, + term: 'test2', + }, + ]) + }) + + after(async () => { + await getRepository(SearchHistory).delete({ user: { id: user.id } }) + }) + + it('returns recent searches', async () => { + const response = await graphqlRequest( + recentSearchesQuery, + authToken + ).expect(200) + expect(response.body.data.recentSearches.searches).to.be.lengthOf(2) + }) + }) +})