mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
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:
commit
b8146439d9
6 changed files with 66 additions and 35 deletions
|
|
@ -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']>>;
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue