update API
This commit is contained in:
@ -32,8 +32,9 @@ import { ClaimsToSet, RequestContext, ResolverContext } from './resolvers/types'
|
||||
import ScalarResolvers from './scalars'
|
||||
import typeDefs from './schema'
|
||||
import { batchGetHighlightsFromLibraryItemIds } from './services/highlights'
|
||||
import { batchGetHomeItems } from './services/home'
|
||||
import { batchGetPublicItems } from './services/home'
|
||||
import { batchGetLabelsFromLibraryItemIds } from './services/labels'
|
||||
import { batchGetLibraryItems } from './services/library_item'
|
||||
import { batchGetRecommendationsFromLibraryItemIds } from './services/recommendation'
|
||||
import {
|
||||
countDailyServiceUsage,
|
||||
@ -113,7 +114,8 @@ const contextFunc: ContextFunction<ExpressContext, ResolverContext> = async ({
|
||||
batchGetRecommendationsFromLibraryItemIds
|
||||
),
|
||||
uploadFiles: new DataLoader(batchGetUploadFilesByIds),
|
||||
homeItems: new DataLoader(batchGetHomeItems),
|
||||
libraryItems: new DataLoader(batchGetLibraryItems),
|
||||
publicItems: new DataLoader(batchGetPublicItems),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -1303,13 +1303,29 @@ export type HomeItem = {
|
||||
previewContent?: Maybe<Scalars['String']>;
|
||||
saveCount?: Maybe<Scalars['Int']>;
|
||||
seen_at?: Maybe<Scalars['Date']>;
|
||||
subscription: HomeSubscription;
|
||||
subscription?: Maybe<HomeItemSource>;
|
||||
thumbnail?: Maybe<Scalars['String']>;
|
||||
title: Scalars['String'];
|
||||
url: Scalars['String'];
|
||||
wordCount?: Maybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type HomeItemSource = {
|
||||
__typename?: 'HomeItemSource';
|
||||
icon?: Maybe<Scalars['String']>;
|
||||
id?: Maybe<Scalars['ID']>;
|
||||
name: Scalars['String'];
|
||||
type: HomeItemSourceType;
|
||||
url?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export enum HomeItemSourceType {
|
||||
Library = 'LIBRARY',
|
||||
Newsletter = 'NEWSLETTER',
|
||||
Recommendation = 'RECOMMENDATION',
|
||||
Rss = 'RSS'
|
||||
}
|
||||
|
||||
export type HomeResult = HomeError | HomeSuccess;
|
||||
|
||||
export type HomeSection = {
|
||||
@ -1320,14 +1336,6 @@ export type HomeSection = {
|
||||
title?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type HomeSubscription = {
|
||||
__typename?: 'HomeSubscription';
|
||||
icon?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
name: Scalars['String'];
|
||||
url?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type HomeSuccess = {
|
||||
__typename?: 'HomeSuccess';
|
||||
edges: Array<HomeEdge>;
|
||||
@ -4259,9 +4267,10 @@ export type ResolversTypes = {
|
||||
HomeError: ResolverTypeWrapper<HomeError>;
|
||||
HomeErrorCode: HomeErrorCode;
|
||||
HomeItem: ResolverTypeWrapper<HomeItem>;
|
||||
HomeItemSource: ResolverTypeWrapper<HomeItemSource>;
|
||||
HomeItemSourceType: HomeItemSourceType;
|
||||
HomeResult: ResolversTypes['HomeError'] | ResolversTypes['HomeSuccess'];
|
||||
HomeSection: ResolverTypeWrapper<HomeSection>;
|
||||
HomeSubscription: ResolverTypeWrapper<HomeSubscription>;
|
||||
HomeSuccess: ResolverTypeWrapper<HomeSuccess>;
|
||||
ID: ResolverTypeWrapper<Scalars['ID']>;
|
||||
ImportFromIntegrationError: ResolverTypeWrapper<ImportFromIntegrationError>;
|
||||
@ -4815,9 +4824,9 @@ export type ResolversParentTypes = {
|
||||
HomeEdge: HomeEdge;
|
||||
HomeError: HomeError;
|
||||
HomeItem: HomeItem;
|
||||
HomeItemSource: HomeItemSource;
|
||||
HomeResult: ResolversParentTypes['HomeError'] | ResolversParentTypes['HomeSuccess'];
|
||||
HomeSection: HomeSection;
|
||||
HomeSubscription: HomeSubscription;
|
||||
HomeSuccess: HomeSuccess;
|
||||
ID: Scalars['ID'];
|
||||
ImportFromIntegrationError: ImportFromIntegrationError;
|
||||
@ -6034,7 +6043,7 @@ export type HomeItemResolvers<ContextType = ResolverContext, ParentType extends
|
||||
previewContent?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
saveCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
||||
seen_at?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>;
|
||||
subscription?: Resolver<ResolversTypes['HomeSubscription'], ParentType, ContextType>;
|
||||
subscription?: Resolver<Maybe<ResolversTypes['HomeItemSource']>, ParentType, ContextType>;
|
||||
thumbnail?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
title?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
url?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
@ -6042,6 +6051,15 @@ export type HomeItemResolvers<ContextType = ResolverContext, ParentType extends
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type HomeItemSourceResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['HomeItemSource'] = ResolversParentTypes['HomeItemSource']> = {
|
||||
icon?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
id?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
type?: Resolver<ResolversTypes['HomeItemSourceType'], ParentType, ContextType>;
|
||||
url?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type HomeResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['HomeResult'] = ResolversParentTypes['HomeResult']> = {
|
||||
__resolveType: TypeResolveFn<'HomeError' | 'HomeSuccess', ParentType, ContextType>;
|
||||
};
|
||||
@ -6054,14 +6072,6 @@ export type HomeSectionResolvers<ContextType = ResolverContext, ParentType exten
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type HomeSubscriptionResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['HomeSubscription'] = ResolversParentTypes['HomeSubscription']> = {
|
||||
icon?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
url?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type HomeSuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['HomeSuccess'] = ResolversParentTypes['HomeSuccess']> = {
|
||||
edges?: Resolver<Array<ResolversTypes['HomeEdge']>, ParentType, ContextType>;
|
||||
pageInfo?: Resolver<ResolversTypes['PageInfo'], ParentType, ContextType>;
|
||||
@ -7653,9 +7663,9 @@ export type Resolvers<ContextType = ResolverContext> = {
|
||||
HomeEdge?: HomeEdgeResolvers<ContextType>;
|
||||
HomeError?: HomeErrorResolvers<ContextType>;
|
||||
HomeItem?: HomeItemResolvers<ContextType>;
|
||||
HomeItemSource?: HomeItemSourceResolvers<ContextType>;
|
||||
HomeResult?: HomeResultResolvers<ContextType>;
|
||||
HomeSection?: HomeSectionResolvers<ContextType>;
|
||||
HomeSubscription?: HomeSubscriptionResolvers<ContextType>;
|
||||
HomeSuccess?: HomeSuccessResolvers<ContextType>;
|
||||
ImportFromIntegrationError?: ImportFromIntegrationErrorResolvers<ContextType>;
|
||||
ImportFromIntegrationResult?: ImportFromIntegrationResultResolvers<ContextType>;
|
||||
|
||||
@ -1172,13 +1172,28 @@ type HomeItem {
|
||||
previewContent: String
|
||||
saveCount: Int
|
||||
seen_at: Date
|
||||
subscription: HomeSubscription!
|
||||
subscription: HomeItemSource
|
||||
thumbnail: String
|
||||
title: String!
|
||||
url: String!
|
||||
wordCount: Int
|
||||
}
|
||||
|
||||
type HomeItemSource {
|
||||
icon: String
|
||||
id: ID
|
||||
name: String!
|
||||
type: HomeItemSourceType!
|
||||
url: String
|
||||
}
|
||||
|
||||
enum HomeItemSourceType {
|
||||
LIBRARY
|
||||
NEWSLETTER
|
||||
RECOMMENDATION
|
||||
RSS
|
||||
}
|
||||
|
||||
union HomeResult = HomeError | HomeSuccess
|
||||
|
||||
type HomeSection {
|
||||
@ -1188,13 +1203,6 @@ type HomeSection {
|
||||
title: String
|
||||
}
|
||||
|
||||
type HomeSubscription {
|
||||
icon: String
|
||||
id: ID!
|
||||
name: String!
|
||||
url: String
|
||||
}
|
||||
|
||||
type HomeSuccess {
|
||||
edges: [HomeEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
|
||||
@ -66,7 +66,7 @@ const libraryItemToCandidate = (
|
||||
dir: item.directionality || 'ltr',
|
||||
date: item.createdAt,
|
||||
topic: item.topic,
|
||||
wordCount: item.wordCount!,
|
||||
wordCount: item.wordCount || 0,
|
||||
siteName: item.siteName || undefined,
|
||||
siteIcon: item.siteIcon || undefined,
|
||||
folder: item.folder,
|
||||
@ -91,8 +91,9 @@ const publicItemToCandidate = (item: PublicItem): Candidate => ({
|
||||
dir: item.dir || 'ltr',
|
||||
date: item.createdAt,
|
||||
topic: item.topic,
|
||||
wordCount: item.wordCount!,
|
||||
wordCount: item.wordCount || 0,
|
||||
siteIcon: item.siteIcon,
|
||||
siteName: item.siteName,
|
||||
publishedAt: item.publishedAt,
|
||||
subscription: {
|
||||
name: item.source.name,
|
||||
|
||||
@ -4,11 +4,14 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { createHmac } from 'crypto'
|
||||
import { isError } from 'lodash'
|
||||
import { Highlight as HighlightEntity } from '../entity/highlight'
|
||||
import { LibraryItem } from '../entity/library_item'
|
||||
import {
|
||||
EXISTING_NEWSLETTER_FOLDER,
|
||||
NewsletterEmail,
|
||||
} from '../entity/newsletter_email'
|
||||
import { PublicItem } from '../entity/public_item'
|
||||
import {
|
||||
DEFAULT_SUBSCRIPTION_FOLDER,
|
||||
Subscription,
|
||||
@ -17,6 +20,7 @@ import { env } from '../env'
|
||||
import {
|
||||
Article,
|
||||
Highlight,
|
||||
HomeItem,
|
||||
Label,
|
||||
PageType,
|
||||
Recommendation,
|
||||
@ -626,11 +630,87 @@ export const functionResolvers = {
|
||||
},
|
||||
HomeSection: {
|
||||
async items(
|
||||
section: { items: Array<{ id: string }> },
|
||||
section: {
|
||||
items: Array<{ id: string; type: 'library_item' | 'public_item' }>
|
||||
},
|
||||
_: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
return ctx.dataLoaders.homeItems.loadMany(section.items.map((i) => i.id))
|
||||
const libraryItemIds = section.items
|
||||
.filter((item) => item.type === 'library_item')
|
||||
.map((item) => item.id)
|
||||
const libraryItems = (
|
||||
await ctx.dataLoaders.libraryItems.loadMany(libraryItemIds)
|
||||
).filter((libraryItem) => !isError(libraryItem)) as Array<LibraryItem>
|
||||
|
||||
const publicItemIds = section.items
|
||||
.filter((item) => item.type === 'public_item')
|
||||
.map((item) => item.id)
|
||||
const publicItems = (
|
||||
await ctx.dataLoaders.publicItems.loadMany(publicItemIds)
|
||||
).filter((publicItem) => !isError(publicItem)) as Array<PublicItem>
|
||||
|
||||
return libraryItems
|
||||
.map(
|
||||
(libraryItem) =>
|
||||
({
|
||||
id: libraryItem.id,
|
||||
title: libraryItem.title,
|
||||
author: libraryItem.author,
|
||||
thumbnail: libraryItem.thumbnail,
|
||||
wordCount: libraryItem.wordCount,
|
||||
date: libraryItem.savedAt,
|
||||
url: libraryItem.originalUrl,
|
||||
canArchive: !libraryItem.archivedAt,
|
||||
canDelete: !libraryItem.deletedAt,
|
||||
canSave: false,
|
||||
dir: libraryItem.directionality,
|
||||
subscription: null,
|
||||
previewContent: libraryItem.description,
|
||||
} as HomeItem)
|
||||
)
|
||||
.concat(
|
||||
publicItems.map(
|
||||
(publicItem) =>
|
||||
({
|
||||
id: publicItem.id,
|
||||
title: publicItem.title,
|
||||
author: publicItem.author,
|
||||
dir: publicItem.dir,
|
||||
previewContent: publicItem.previewContent,
|
||||
thumbnail: publicItem.thumbnail,
|
||||
wordCount: publicItem.wordCount,
|
||||
date: publicItem.createdAt,
|
||||
url: publicItem.url,
|
||||
canArchive: false,
|
||||
canDelete: false,
|
||||
canSave: true,
|
||||
broadcastCount: publicItem.stats.broadcastCount,
|
||||
likeCount: publicItem.stats.likeCount,
|
||||
saveCount: publicItem.stats.saveCount,
|
||||
subscription: publicItem.source,
|
||||
} as HomeItem)
|
||||
)
|
||||
)
|
||||
},
|
||||
},
|
||||
HomeItem: {
|
||||
async subscription(
|
||||
item: HomeItem,
|
||||
_: unknown,
|
||||
ctx: WithDataSourcesContext
|
||||
) {
|
||||
// if (item.subscription) {
|
||||
// return item.subscription
|
||||
// }
|
||||
// const subscription = await ctx.dataLoaders.subscriptions.load(item.id)
|
||||
// if (!subscription) {
|
||||
// return {
|
||||
// name: item.siteName,
|
||||
// icon: item.siteIcon,
|
||||
// type: 'rss',
|
||||
// }
|
||||
// }
|
||||
},
|
||||
},
|
||||
...resultResolveTypeResolver('Login'),
|
||||
|
||||
@ -28,7 +28,7 @@ export const homeResolver = authorized<
|
||||
HomeError,
|
||||
QueryHomeArgs
|
||||
>(async (_, { first, after }, { uid, log }) => {
|
||||
const limit = first || 10
|
||||
const limit = first || 6
|
||||
const cursor = after ? parseInt(after) : undefined
|
||||
|
||||
const sections = await getHomeSections(uid, limit, cursor)
|
||||
|
||||
@ -8,6 +8,8 @@ import winston from 'winston'
|
||||
import { ReadingProgressDataSource } from '../datasources/reading_progress_data_source'
|
||||
import { Highlight } from '../entity/highlight'
|
||||
import { Label } from '../entity/label'
|
||||
import { LibraryItem } from '../entity/library_item'
|
||||
import { PublicItem } from '../entity/public_item'
|
||||
import { Recommendation } from '../entity/recommendation'
|
||||
import { UploadFile } from '../entity/upload_file'
|
||||
import { HomeItem } from '../generated/graphql'
|
||||
@ -52,7 +54,8 @@ export interface RequestContext {
|
||||
highlights: DataLoader<string, Highlight[]>
|
||||
recommendations: DataLoader<string, Recommendation[]>
|
||||
uploadFiles: DataLoader<string, UploadFile | undefined>
|
||||
homeItems: DataLoader<string, HomeItem>
|
||||
libraryItems: DataLoader<string, LibraryItem>
|
||||
publicItems: DataLoader<string, PublicItem>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3101,11 +3101,19 @@ const schema = gql`
|
||||
SUBSCRIBE
|
||||
}
|
||||
|
||||
type HomeSubscription {
|
||||
id: ID!
|
||||
enum HomeItemSourceType {
|
||||
RSS
|
||||
NEWSLETTER
|
||||
RECOMMENDATION
|
||||
LIBRARY
|
||||
}
|
||||
|
||||
type HomeItemSource {
|
||||
id: ID
|
||||
name: String!
|
||||
url: String
|
||||
icon: String
|
||||
type: HomeItemSourceType!
|
||||
}
|
||||
|
||||
type HomeItem {
|
||||
@ -3122,7 +3130,7 @@ const schema = gql`
|
||||
dir: String
|
||||
seen_at: Date
|
||||
wordCount: Int
|
||||
subscription: HomeSubscription!
|
||||
subscription: HomeItemSource
|
||||
canSave: Boolean
|
||||
canComment: Boolean
|
||||
canShare: Boolean
|
||||
|
||||
@ -3,6 +3,18 @@ import { HomeItem } from '../generated/graphql'
|
||||
import { authTrx } from '../repository'
|
||||
import { findLibraryItemsByIds } from './library_item'
|
||||
|
||||
export const batchGetPublicItems = async (
|
||||
ids: readonly string[]
|
||||
): Promise<Array<PublicItem>> => {
|
||||
return authTrx(async (tx) =>
|
||||
tx
|
||||
.getRepository(PublicItem)
|
||||
.createQueryBuilder('public_item')
|
||||
.where('public_item.id IN (:...ids)', { ids })
|
||||
.getMany()
|
||||
)
|
||||
}
|
||||
|
||||
export const batchGetHomeItems = async (
|
||||
ids: readonly string[]
|
||||
): Promise<Array<HomeItem>> => {
|
||||
@ -38,11 +50,13 @@ export const batchGetHomeItems = async (
|
||||
canDelete: !libraryItem.deletedAt,
|
||||
canSave: false,
|
||||
dir: libraryItem.directionality,
|
||||
}
|
||||
subscription: null,
|
||||
previewContent: libraryItem.description,
|
||||
} as HomeItem
|
||||
} else {
|
||||
const publicItem = publicItems.find((pi) => pi.id === id)
|
||||
return publicItem
|
||||
? {
|
||||
? ({
|
||||
...publicItem,
|
||||
date: publicItem.createdAt,
|
||||
url: publicItem.url,
|
||||
@ -53,7 +67,7 @@ export const batchGetHomeItems = async (
|
||||
likeCount: publicItem.stats.likeCount,
|
||||
saveCount: publicItem.stats.saveCount,
|
||||
subscription: publicItem.source,
|
||||
}
|
||||
} as HomeItem)
|
||||
: undefined
|
||||
}
|
||||
})
|
||||
|
||||
@ -140,6 +140,10 @@ interface Select {
|
||||
|
||||
const readingProgressDataSource = new ReadingProgressDataSource()
|
||||
|
||||
export const batchGetLibraryItems = async (ids: readonly string[]) => {
|
||||
return findLibraryItemsByIds(ids as string[])
|
||||
}
|
||||
|
||||
export const getItemUrl = (id: string) => `${env.client.url}/me/${id}`
|
||||
|
||||
const markItemAsRead = async (libraryItemId: string, userId: string) => {
|
||||
|
||||
@ -17,4 +17,6 @@ ALTER TABLE omnivore.library_item
|
||||
DROP COLUMN topic,
|
||||
DROP COLUMN score;
|
||||
|
||||
DROP EXTENSION LTREE;
|
||||
|
||||
COMMIT;
|
||||
|
||||
Reference in New Issue
Block a user