mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
add feeds graphql api for searching feed by title or url
This commit is contained in:
parent
878d881e05
commit
fc783f01ec
8 changed files with 117 additions and 12 deletions
|
|
@ -791,6 +791,13 @@ export enum FeedsErrorCode {
|
|||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type FeedsInput = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
query: Scalars['String'];
|
||||
sort?: InputMaybe<SortParams>;
|
||||
};
|
||||
|
||||
export type FeedsResult = FeedsError | FeedsSuccess;
|
||||
|
||||
export type FeedsSuccess = {
|
||||
|
|
@ -1801,10 +1808,7 @@ export type QueryArticleSavingRequestArgs = {
|
|||
|
||||
|
||||
export type QueryFeedsArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
query: Scalars['String'];
|
||||
sort?: InputMaybe<SortParams>;
|
||||
input: FeedsInput;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -3515,6 +3519,7 @@ export type ResolversTypes = {
|
|||
FeedEdge: ResolverTypeWrapper<FeedEdge>;
|
||||
FeedsError: ResolverTypeWrapper<FeedsError>;
|
||||
FeedsErrorCode: FeedsErrorCode;
|
||||
FeedsInput: FeedsInput;
|
||||
FeedsResult: ResolversTypes['FeedsError'] | ResolversTypes['FeedsSuccess'];
|
||||
FeedsSuccess: ResolverTypeWrapper<FeedsSuccess>;
|
||||
Filter: ResolverTypeWrapper<Filter>;
|
||||
|
|
@ -3999,6 +4004,7 @@ export type ResolversParentTypes = {
|
|||
FeedArticlesSuccess: FeedArticlesSuccess;
|
||||
FeedEdge: FeedEdge;
|
||||
FeedsError: FeedsError;
|
||||
FeedsInput: FeedsInput;
|
||||
FeedsResult: ResolversParentTypes['FeedsError'] | ResolversParentTypes['FeedsSuccess'];
|
||||
FeedsSuccess: FeedsSuccess;
|
||||
Filter: Filter;
|
||||
|
|
@ -5357,7 +5363,7 @@ export type QueryResolvers<ContextType = ResolverContext, ParentType extends Res
|
|||
article?: Resolver<ResolversTypes['ArticleResult'], ParentType, ContextType, RequireFields<QueryArticleArgs, 'slug' | 'username'>>;
|
||||
articleSavingRequest?: Resolver<ResolversTypes['ArticleSavingRequestResult'], ParentType, ContextType, Partial<QueryArticleSavingRequestArgs>>;
|
||||
deviceTokens?: Resolver<ResolversTypes['DeviceTokensResult'], ParentType, ContextType>;
|
||||
feeds?: Resolver<ResolversTypes['FeedsResult'], ParentType, ContextType, RequireFields<QueryFeedsArgs, 'query'>>;
|
||||
feeds?: Resolver<ResolversTypes['FeedsResult'], ParentType, ContextType, RequireFields<QueryFeedsArgs, 'input'>>;
|
||||
filters?: Resolver<ResolversTypes['FiltersResult'], ParentType, ContextType>;
|
||||
getUserPersonalization?: Resolver<ResolversTypes['GetUserPersonalizationResult'], ParentType, ContextType>;
|
||||
groups?: Resolver<ResolversTypes['GroupsResult'], ParentType, ContextType>;
|
||||
|
|
|
|||
|
|
@ -700,6 +700,13 @@ enum FeedsErrorCode {
|
|||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
input FeedsInput {
|
||||
after: String
|
||||
first: Int
|
||||
query: String!
|
||||
sort: SortParams
|
||||
}
|
||||
|
||||
union FeedsResult = FeedsError | FeedsSuccess
|
||||
|
||||
type FeedsSuccess {
|
||||
|
|
@ -1327,7 +1334,7 @@ type Query {
|
|||
article(format: String, slug: String!, username: String!): ArticleResult!
|
||||
articleSavingRequest(id: ID, url: String): ArticleSavingRequestResult!
|
||||
deviceTokens: DeviceTokensResult!
|
||||
feeds(after: String, first: Int, query: String!, sort: SortParams): FeedsResult!
|
||||
feeds(input: FeedsInput!): FeedsResult!
|
||||
filters: FiltersResult!
|
||||
getUserPersonalization: GetUserPersonalizationResult!
|
||||
groups: GroupsResult!
|
||||
|
|
|
|||
29
packages/api/src/repository/feed.ts
Normal file
29
packages/api/src/repository/feed.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { ILike } from 'typeorm'
|
||||
import { appDataSource } from '../data_source'
|
||||
import { Feed } from '../entity/feed'
|
||||
|
||||
export const feedRepository = appDataSource.getRepository(Feed).extend({
|
||||
async searchFeeds(
|
||||
query: string,
|
||||
take = 10,
|
||||
skip = 0,
|
||||
orderBy = 'createdAt',
|
||||
order = 'DESC'
|
||||
) {
|
||||
query = `%${query}%`
|
||||
const where = [{ title: ILike(query) }, { url: ILike(query) }]
|
||||
const feeds = await this.find({
|
||||
where,
|
||||
order: { [orderBy]: order },
|
||||
take,
|
||||
skip,
|
||||
})
|
||||
|
||||
const count = await this.countBy(where)
|
||||
|
||||
return {
|
||||
feeds,
|
||||
count,
|
||||
}
|
||||
},
|
||||
})
|
||||
61
packages/api/src/resolvers/feeds/index.ts
Normal file
61
packages/api/src/resolvers/feeds/index.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import {
|
||||
FeedEdge,
|
||||
FeedsError,
|
||||
FeedsErrorCode,
|
||||
FeedsSuccess,
|
||||
QueryFeedsArgs,
|
||||
} from '../../generated/graphql'
|
||||
import { feedRepository } from '../../repository/feed'
|
||||
import { authorized } from '../../utils/helpers'
|
||||
|
||||
export const feedsResolve = authorized<
|
||||
FeedsSuccess,
|
||||
FeedsError,
|
||||
QueryFeedsArgs
|
||||
>(async (_, { input }, { log }) => {
|
||||
try {
|
||||
const startCursor = input.after || ''
|
||||
const start =
|
||||
startCursor && !isNaN(Number(startCursor)) ? Number(startCursor) : 0
|
||||
const first = input.first || 10
|
||||
|
||||
const { feeds, count } = await feedRepository.searchFeeds(
|
||||
input.query,
|
||||
first + 1, // fetch one extra to check if there is a next page
|
||||
start,
|
||||
input.sort?.by,
|
||||
input.sort?.order || undefined
|
||||
)
|
||||
|
||||
const hasNextPage = feeds.length > first
|
||||
const endCursor = String(start + feeds.length - (hasNextPage ? 1 : 0))
|
||||
|
||||
if (hasNextPage) {
|
||||
// remove an extra if exists
|
||||
feeds.pop()
|
||||
}
|
||||
|
||||
const edges: FeedEdge[] = feeds.map((feed) => ({
|
||||
node: feed,
|
||||
cursor: endCursor,
|
||||
}))
|
||||
|
||||
return {
|
||||
__typename: 'FeedsSuccess',
|
||||
edges,
|
||||
pageInfo: {
|
||||
hasPreviousPage: start > 0,
|
||||
hasNextPage,
|
||||
startCursor,
|
||||
endCursor,
|
||||
totalCount: count,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('Error fetching feeds', error)
|
||||
|
||||
return {
|
||||
errorCodes: [FeedsErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -2605,6 +2605,13 @@ const schema = gql`
|
|||
email: String!
|
||||
}
|
||||
|
||||
input FeedsInput {
|
||||
after: String
|
||||
first: Int @sanitize(minLength: 1, maxLength: 100)
|
||||
query: String! @sanitize(maxLength: 255)
|
||||
sort: SortParams
|
||||
}
|
||||
|
||||
union FeedsResult = FeedsSuccess | FeedsError
|
||||
|
||||
type FeedsSuccess {
|
||||
|
|
@ -2799,12 +2806,7 @@ const schema = gql`
|
|||
filters: FiltersResult!
|
||||
groups: GroupsResult!
|
||||
recentEmails: RecentEmailsResult!
|
||||
feeds(
|
||||
after: String
|
||||
first: Int
|
||||
query: String!
|
||||
sort: SortParams
|
||||
): FeedsResult!
|
||||
feeds(input: FeedsInput!): FeedsResult!
|
||||
}
|
||||
`
|
||||
|
||||
|
|
|
|||
0
packages/api/src/services/feeds.ts
Normal file
0
packages/api/src/services/feeds.ts
Normal file
Loading…
Reference in a new issue