Merge pull request #3204 from omnivore-app/feature/bulk-move-to-folder-api

feat: allow moving items to a folder in a bulk
This commit is contained in:
Hongbo Wu 2023-12-04 18:54:22 +08:00 committed by GitHub
commit b8146439d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 35 deletions

View file

@ -245,7 +245,8 @@ export enum BulkActionType {
AddLabels = 'ADD_LABELS',
Archive = 'ARCHIVE',
Delete = 'DELETE',
MarkAsRead = 'MARK_AS_READ'
MarkAsRead = 'MARK_AS_READ',
MoveToFolder = 'MOVE_TO_FOLDER'
}
export enum ContentReader {
@ -1376,6 +1377,7 @@ export type MutationAddPopularReadArgs = {
export type MutationBulkActionArgs = {
action: BulkActionType;
arguments?: InputMaybe<Scalars['JSON']>;
async?: InputMaybe<Scalars['Boolean']>;
expectedCount?: InputMaybe<Scalars['Int']>;
labelIds?: InputMaybe<Array<Scalars['ID']>>;

View file

@ -204,6 +204,7 @@ enum BulkActionType {
ARCHIVE
DELETE
MARK_AS_READ
MOVE_TO_FOLDER
}
enum ContentReader {
@ -1170,7 +1171,7 @@ type MoveToFolderSuccess {
type Mutation {
addPopularRead(name: String!): AddPopularReadResult!
bulkAction(action: BulkActionType!, async: Boolean, expectedCount: Int, labelIds: [ID!], query: String!): BulkActionResult!
bulkAction(action: BulkActionType!, arguments: JSON, async: Boolean, expectedCount: Int, labelIds: [ID!], query: String!): BulkActionResult!
createArticle(input: CreateArticleInput!): CreateArticleResult!
createArticleSavingRequest(input: CreateArticleSavingRequestInput!): CreateArticleSavingRequestResult!
createGroup(input: CreateGroupInput!): CreateGroupResult!

View file

@ -810,41 +810,47 @@ export const bulkActionResolver = authorized<
BulkActionSuccess,
BulkActionError,
MutationBulkActionArgs
>(async (_parent, { query, action, labelIds }, { uid, log }) => {
try {
analytics.track({
userId: uid,
event: 'BulkAction',
properties: {
env: env.server.apiEnv,
action,
},
})
>(
async (
_parent,
{ query, action, labelIds, arguments: args }, // arguments is a reserved keyword in JS
{ uid, log }
) => {
try {
analytics.track({
userId: uid,
event: 'BulkAction',
properties: {
env: env.server.apiEnv,
action,
},
})
// parse query
const searchQuery = parseSearchQuery(query)
if (searchQuery.ids.length > 100) {
return { errorCodes: [BulkActionErrorCode.BadRequest] }
}
// get labels if needed
let labels = undefined
if (action === BulkActionType.AddLabels) {
if (!labelIds || labelIds.length === 0) {
// parse query
const searchQuery = parseSearchQuery(query)
if (searchQuery.ids.length > 100) {
return { errorCodes: [BulkActionErrorCode.BadRequest] }
}
labels = await findLabelsByIds(labelIds, uid)
// get labels if needed
let labels = undefined
if (action === BulkActionType.AddLabels) {
if (!labelIds || labelIds.length === 0) {
return { errorCodes: [BulkActionErrorCode.BadRequest] }
}
labels = await findLabelsByIds(labelIds, uid)
}
await updateLibraryItems(action, searchQuery, uid, labels, args)
return { success: true }
} catch (error) {
log.error('bulkActionResolver error', error)
return { errorCodes: [BulkActionErrorCode.BadRequest] }
}
await updateLibraryItems(action, searchQuery, uid, labels)
return { success: true }
} catch (error) {
log.error('bulkActionResolver error', error)
return { errorCodes: [BulkActionErrorCode.BadRequest] }
}
})
)
export const setFavoriteArticleResolver = authorized<
SetFavoriteArticleSuccess,

View file

@ -2510,6 +2510,7 @@ const schema = gql`
ARCHIVE
MARK_AS_READ
ADD_LABELS
MOVE_TO_FOLDER
}
union BulkActionResult = BulkActionSuccess | BulkActionError
@ -2800,6 +2801,7 @@ const schema = gql`
labelIds: [ID!]
expectedCount: Int # max number of items to process
async: Boolean # if true, return immediately and process in the background
arguments: JSON # additional arguments for the action
): BulkActionResult!
importFromIntegration(integrationId: ID!): ImportFromIntegrationResult!
setFavoriteArticle(id: ID!): SetFavoriteArticleResult!

View file

@ -671,10 +671,19 @@ export const countByCreatedAt = async (
export const updateLibraryItems = async (
action: BulkActionType,
args: SearchArgs,
searchArgs: SearchArgs,
userId: string,
labels?: Label[]
labels?: Label[],
args?: unknown
) => {
interface FolderArguments {
folder: string
}
const isFolderArguments = (args: any): args is FolderArguments => {
return 'folder' in args
}
// build the script
let values: QueryDeepPartialEntity<LibraryItem> = {}
let addLabels = false
@ -700,6 +709,17 @@ export const updateLibraryItems = async (
readingProgressTopPercent: 100,
readingProgressBottomPercent: 100,
}
break
case BulkActionType.MoveToFolder:
if (!args || !isFolderArguments(args)) {
throw new Error('Invalid arguments')
}
values = {
folder: args.folder,
savedAt: new Date(),
}
break
default:
throw new Error('Invalid bulk action')
@ -711,7 +731,7 @@ export const updateLibraryItems = async (
.where('library_item.user_id = :userId', { userId })
// build the where clause
buildWhereClause(queryBuilder, args)
buildWhereClause(queryBuilder, searchArgs)
if (addLabels) {
if (!labels) {

View file

@ -137,7 +137,7 @@ export const isSystemRequest = (req: express.Request): boolean => {
try {
const claims = jwt.verify(token, env.server.jwtSecret) as Claims
return !claims.system
return !!claims.system
} catch (e) {
return false
}