mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
add directionality to the SearchItem and Article Grahql type in the schema
This commit is contained in:
parent
0e46dc2302
commit
4a70291898
5 changed files with 96 additions and 62 deletions
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue