add directionality to the SearchItem and Article Grahql type in the schema

This commit is contained in:
Hongbo Wu 2024-03-04 13:23:52 +08:00
parent 0e46dc2302
commit 4a70291898
5 changed files with 96 additions and 62 deletions

View file

@ -94,6 +94,7 @@ export type Article = {
contentReader: ContentReader;
createdAt: Scalars['Date'];
description?: Maybe<Scalars['String']>;
directionality?: Maybe<DirectionalityType>;
feedContent?: Maybe<Scalars['String']>;
folder: Scalars['String'];
hasContent?: Maybe<Scalars['Boolean']>;
@ -729,6 +730,11 @@ export type DeviceTokensSuccess = {
deviceTokens: Array<DeviceToken>;
};
export enum DirectionalityType {
Ltr = 'LTR',
Rtl = 'RTL'
}
export type EmptyTrashError = {
__typename?: 'EmptyTrashError';
errorCodes: Array<EmptyTrashErrorCode>;
@ -2404,6 +2410,7 @@ export enum SearchErrorCode {
export type SearchItem = {
__typename?: 'SearchItem';
aiSummary?: Maybe<Scalars['String']>;
annotation?: Maybe<Scalars['String']>;
archivedAt?: Maybe<Scalars['Date']>;
author?: Maybe<Scalars['String']>;
@ -2412,6 +2419,7 @@ export type SearchItem = {
contentReader: ContentReader;
createdAt: Scalars['Date'];
description?: Maybe<Scalars['String']>;
directionality?: Maybe<DirectionalityType>;
feedContent?: Maybe<Scalars['String']>;
folder: Scalars['String'];
highlights?: Maybe<Array<Highlight>>;
@ -3378,6 +3386,7 @@ export enum UploadImportFileType {
export type User = {
__typename?: 'User';
email?: Maybe<Scalars['String']>;
features?: Maybe<Array<Maybe<Scalars['String']>>>;
followersCount?: Maybe<Scalars['Int']>;
friendsCount?: Maybe<Scalars['Int']>;
id: Scalars['ID'];
@ -3707,6 +3716,7 @@ export type ResolversTypes = {
DeviceTokensErrorCode: DeviceTokensErrorCode;
DeviceTokensResult: ResolversTypes['DeviceTokensError'] | ResolversTypes['DeviceTokensSuccess'];
DeviceTokensSuccess: ResolverTypeWrapper<DeviceTokensSuccess>;
DirectionalityType: DirectionalityType;
EmptyTrashError: ResolverTypeWrapper<EmptyTrashError>;
EmptyTrashErrorCode: EmptyTrashErrorCode;
EmptyTrashResult: ResolversTypes['EmptyTrashError'] | ResolversTypes['EmptyTrashSuccess'];
@ -4605,6 +4615,7 @@ export type ArticleResolvers<ContextType = ResolverContext, ParentType extends R
contentReader?: Resolver<ResolversTypes['ContentReader'], ParentType, ContextType>;
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
description?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
directionality?: Resolver<Maybe<ResolversTypes['DirectionalityType']>, ParentType, ContextType>;
feedContent?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
folder?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
hasContent?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
@ -5945,6 +5956,7 @@ export type SearchErrorResolvers<ContextType = ResolverContext, ParentType exten
};
export type SearchItemResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['SearchItem'] = ResolversParentTypes['SearchItem']> = {
aiSummary?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
annotation?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
archivedAt?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
author?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
@ -5953,6 +5965,7 @@ export type SearchItemResolvers<ContextType = ResolverContext, ParentType extend
contentReader?: Resolver<ResolversTypes['ContentReader'], ParentType, ContextType>;
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
description?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
directionality?: Resolver<Maybe<ResolversTypes['DirectionalityType']>, ParentType, ContextType>;
feedContent?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
folder?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
highlights?: Resolver<Maybe<Array<ResolversTypes['Highlight']>>, ParentType, ContextType>;
@ -6528,6 +6541,7 @@ export type UploadImportFileSuccessResolvers<ContextType = ResolverContext, Pare
export type UserResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['User'] = ResolversParentTypes['User']> = {
email?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
features?: Resolver<Maybe<Array<Maybe<ResolversTypes['String']>>>, ParentType, ContextType>;
followersCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
friendsCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;

View file

@ -69,6 +69,7 @@ type Article {
contentReader: ContentReader!
createdAt: Date!
description: String
directionality: DirectionalityType
feedContent: String
folder: String!
hasContent: Boolean
@ -646,6 +647,11 @@ type DeviceTokensSuccess {
deviceTokens: [DeviceToken!]!
}
enum DirectionalityType {
LTR
RTL
}
type EmptyTrashError {
errorCodes: [EmptyTrashErrorCode!]!
}
@ -1832,6 +1838,7 @@ enum SearchErrorCode {
}
type SearchItem {
aiSummary: String
annotation: String
archivedAt: Date
author: String
@ -1840,6 +1847,7 @@ type SearchItem {
contentReader: ContentReader!
createdAt: Date!
description: String
directionality: DirectionalityType
feedContent: String
folder: String!
highlights: [Highlight!]
@ -2733,6 +2741,7 @@ enum UploadImportFileType {
type User {
email: String
features: [String]
followersCount: Int
friendsCount: Int
id: ID!

View file

@ -354,6 +354,11 @@ const schema = gql`
note: String
}
enum DirectionalityType {
LTR
RTL
}
type Article {
id: ID!
title: String!
@ -399,6 +404,7 @@ const schema = gql`
wordsCount: Int
folder: String!
feedContent: String
directionality: DirectionalityType
}
# Query: article
@ -1649,6 +1655,7 @@ const schema = gql`
links: JSON
folder: String!
aiSummary: String
directionality: DirectionalityType
}
type SearchItemEdge {

View file

@ -18,6 +18,7 @@ import {
ContentReader,
CreateArticleError,
CreateArticleSuccess,
DirectionalityType,
FeedArticle,
Highlight,
PageType,
@ -227,6 +228,7 @@ export const libraryItemToArticle = (item: LibraryItem): Article => ({
uploadFileId: item.uploadFile?.id,
pageType: item.itemType as unknown as PageType,
wordsCount: item.wordCount,
directionality: item.directionality as unknown as DirectionalityType,
})
export const libraryItemToSearchItem = (item: LibraryItem): SearchItem => ({
@ -245,6 +247,7 @@ export const libraryItemToSearchItem = (item: LibraryItem): SearchItem => ({
image: item.thumbnail,
highlights: item.highlights?.map(highlightDataToHighlight),
wordsCount: item.wordCount,
directionality: item.directionality as unknown as DirectionalityType,
})
export const isParsingTimeout = (libraryItem: LibraryItem): boolean => {

View file

@ -7,7 +7,11 @@ import { DeepPartial } from 'typeorm'
import { Group } from '../../src/entity/groups/group'
import { Highlight } from '../../src/entity/highlight'
import { Label } from '../../src/entity/label'
import { LibraryItem, LibraryItemState } from '../../src/entity/library_item'
import {
DirectionalityType,
LibraryItem,
LibraryItemState,
} from '../../src/entity/library_item'
import { UploadFile } from '../../src/entity/upload_file'
import { User } from '../../src/entity/user'
import {
@ -118,6 +122,7 @@ const getArticleQuery = (slug: string) => {
id
slug
content
directionality
highlights {
id
shortId
@ -155,6 +160,7 @@ const searchQuery = (keyword = '') => {
url
createdAt
updatedAt
directionality
highlights {
id
}
@ -183,32 +189,9 @@ const searchQuery = (keyword = '') => {
`
}
const savePageQuery = (
url: string,
title: string,
originalContent: string,
state: ArticleSavingRequestStatus | null = null,
labels: string[] | null = null,
clientRequestId = generateFakeUuid(),
source = 'puppeteer-parse'
) => {
return `
mutation {
savePage(
input: {
url: "${url}",
source: "${source}",
clientRequestId: "${clientRequestId}",
title: "${title}",
originalContent: "${originalContent}"
state: ${state}
labels: ${
labels
? '[' + labels.map((label) => `{ name: "${label}" }`) + ']'
: null
}
}
) {
const savePageQuery = `
mutation SavePage($input: SavePageInput!) {
savePage(input: $input) {
... on SaveSuccess {
url
}
@ -216,9 +199,7 @@ const savePageQuery = (
errorCodes
}
}
}
`
}
}`
const saveFileQuery = (
clientRequestId: string,
@ -452,6 +433,7 @@ describe('Article API', () => {
readingProgressTopPercent: 100,
user,
originalUrl: 'https://blog.omnivore.app/test-with-omnivore',
directionality: DirectionalityType.RTL,
}
const item = await createOrUpdateLibraryItem(itemToCreate, user.id)
itemId = item.id
@ -487,6 +469,9 @@ describe('Article API', () => {
const res = await graphqlRequest(query, authToken).expect(200)
expect(res.body.data.article.article.slug).to.eql(slug)
expect(res.body.data.article.article.directionality).to.eql(
DirectionalityType.RTL
)
})
it('should return highlights', async () => {
@ -543,14 +528,11 @@ describe('Article API', () => {
})
describe('SavePage', () => {
let query = ''
let title = 'Example Title'
let url = 'https://blog.omnivore.app'
let originalContent = '<div>Example Content</div>'
beforeEach(() => {
query = savePageQuery(url, title, originalContent)
})
let originalContent =
'<html dir="rtl"><body><div>Example Content</div></body></html>'
let source = 'puppeteer-parse'
context('when we save a new item', () => {
after(async () => {
@ -558,7 +540,15 @@ describe('Article API', () => {
})
it('should return a slugged url', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
const res = await graphqlRequest(savePageQuery, authToken, {
input: {
url,
originalContent,
title,
clientRequestId: generateFakeUuid(),
source,
},
}).expect(200)
expect(res.body.data.savePage.url).to.startsWith(
'http://localhost:3000/fakeUser/example-title-'
)
@ -575,10 +565,16 @@ describe('Article API', () => {
})
it('it should return that item in the Search Query', async () => {
await graphqlRequest(
savePageQuery(url, title, originalContent),
authToken
).expect(200)
const variables = {
input: {
url,
originalContent,
title,
clientRequestId: generateFakeUuid(),
source,
},
}
await graphqlRequest(savePageQuery, authToken, variables).expect(200)
// Save a link, then archive it
let allLinks = await graphqlRequest(
@ -596,17 +592,7 @@ describe('Article API', () => {
expect(allLinks.body.data.search.edges[0]?.node?.url).to.not.eq(url)
// Now save the link again, and ensure it is returned
await graphqlRequest(
savePageQuery(
url,
title,
originalContent,
null,
null,
generateFakeUuid()
),
authToken
).expect(200)
await graphqlRequest(savePageQuery, authToken, variables).expect(200)
allLinks = await graphqlRequest(
searchQuery('in:inbox'),
@ -614,6 +600,9 @@ describe('Article API', () => {
).expect(200)
expect(allLinks.body.data.search.edges[0].node.id).to.eq(justSavedId)
expect(allLinks.body.data.search.edges[0].node.url).to.eq(url)
expect(allLinks.body.data.search.edges[0].node.directionality).to.eq(
'RTL'
)
})
})
@ -628,15 +617,23 @@ describe('Article API', () => {
it('saves the labels and archives the item', async () => {
const state = ArticleSavingRequestStatus.Archived
const labels = ['test name', 'test name 2']
await graphqlRequest(
savePageQuery(url, title, originalContent, state, labels),
authToken
).expect(200)
const labels = [{ name: 'test name' }, { name: 'test name 2' }]
await graphqlRequest(savePageQuery, authToken, {
input: {
url,
state,
labels,
originalContent,
clientRequestId: generateFakeUuid(),
source,
},
}).expect(200)
const savedItem = await findLibraryItemByUrl(url, user.id)
expect(savedItem?.archivedAt).to.not.be.null
expect(savedItem?.labels?.map((l) => l.name)).to.include.members(labels)
expect(savedItem?.labels?.map((l) => l.name)).to.include.members(
labels.map((l) => l.name)
)
})
})
@ -654,10 +651,14 @@ describe('Article API', () => {
})
it('does not parse in the backend', async () => {
await graphqlRequest(
savePageQuery(url, title, originalContent, null, null, '', source),
authToken
).expect(200)
await graphqlRequest(savePageQuery, authToken, {
input: {
url,
source,
originalContent,
clientRequestId: generateFakeUuid(),
},
}).expect(200)
expect(stub).not.to.have.been.called
})