Add queue management dashboard (#15202)
Adds a comprehensive queue management interface to the admin panel for viewing and managing background jobs. **Features:** - Queue detail pages showing paginated job lists (50 per page) - Filter jobs by state: completed, failed, active, waiting, delayed, paused - Checkbox selection with bulk actions (delete jobs, retry failed jobs) - Per-job dropdown menu for individual retry/delete - Expandable rows showing error messages, stack traces, and job data - Relative timestamps with hover tooltips - Display attempt counts on failed jobs - Dynamic retention policy info from backend **Changes:** - Backend: New AdminPanelQueueService with GraphQL endpoints for job listing, retry, and delete - Frontend: Queue detail page with QueueJobsTable component - Updated retention policy: completed jobs kept 4 hours, failed jobs kept 7 days (max 1000 each) - Added JobState enum for type safety <img width="634" height="696" alt="Screenshot_2025-10-20_at_11 45 25" src="https://github.com/user-attachments/assets/c67bcd27-26cf-47f5-9575-3cd5684d006b" /> <img width="484" height="680" alt="Screenshot_2025-10-20_at_11 45 14" src="https://github.com/user-attachments/assets/68725cc6-b3ec-4098-99ca-f9a717d6f8f1" /> <img width="490" height="643" alt="Screenshot_2025-10-20_at_11 45 05" src="https://github.com/user-attachments/assets/b68a5809-33ff-4452-b48b-741aff7f1dd6" /> <img width="685" height="662" alt="Screenshot 2025-10-20 at 13 15 01" src="https://github.com/user-attachments/assets/eeb5207b-de5c-4b18-bdde-392892053dab" />
This commit is contained in:
+6
-6
@@ -89,7 +89,7 @@ describe('QueryRunnerArgsFactory', () => {
|
||||
const result = await factory.create(
|
||||
args,
|
||||
options,
|
||||
ResolverArgsType.CreateMany,
|
||||
ResolverArgsType.CREATE_MANY,
|
||||
);
|
||||
|
||||
expect(result).toEqual(args);
|
||||
@@ -104,7 +104,7 @@ describe('QueryRunnerArgsFactory', () => {
|
||||
const result = await factory.create(
|
||||
args,
|
||||
options,
|
||||
ResolverArgsType.CreateMany,
|
||||
ResolverArgsType.CREATE_MANY,
|
||||
);
|
||||
|
||||
const expectedArgs = {
|
||||
@@ -140,7 +140,7 @@ describe('QueryRunnerArgsFactory', () => {
|
||||
const result = await factory.create(
|
||||
args,
|
||||
options,
|
||||
ResolverArgsType.CreateMany,
|
||||
ResolverArgsType.CREATE_MANY,
|
||||
);
|
||||
|
||||
const expectedArgs = {
|
||||
@@ -176,7 +176,7 @@ describe('QueryRunnerArgsFactory', () => {
|
||||
const result = await factory.create(
|
||||
args,
|
||||
options,
|
||||
ResolverArgsType.FindMany,
|
||||
ResolverArgsType.FIND_MANY,
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -194,7 +194,7 @@ describe('QueryRunnerArgsFactory', () => {
|
||||
const result = await factory.create(
|
||||
args,
|
||||
options,
|
||||
ResolverArgsType.FindOne,
|
||||
ResolverArgsType.FIND_ONE,
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -212,7 +212,7 @@ describe('QueryRunnerArgsFactory', () => {
|
||||
const result = await factory.create(
|
||||
args,
|
||||
options,
|
||||
ResolverArgsType.FindDuplicates,
|
||||
ResolverArgsType.FIND_DUPLICATES,
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
|
||||
+11
-10
@@ -17,6 +17,7 @@ import {
|
||||
ResolverArgsType,
|
||||
type UpdateManyResolverArgs,
|
||||
type UpdateOneResolverArgs,
|
||||
type WorkspaceResolverBuilderMethodNames,
|
||||
} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
@@ -36,7 +37,7 @@ export class QueryRunnerArgsFactory {
|
||||
async create(
|
||||
args: ResolverArgs,
|
||||
options: WorkspaceQueryRunnerOptions,
|
||||
resolverArgsType: ResolverArgsType,
|
||||
resolverArgsType: WorkspaceResolverBuilderMethodNames,
|
||||
) {
|
||||
const fieldMetadataMapByNameByName =
|
||||
options.objectMetadataItemWithFieldMaps.fieldsById;
|
||||
@@ -44,7 +45,7 @@ export class QueryRunnerArgsFactory {
|
||||
const { objectMetadataItemWithFieldMaps, authContext } = options;
|
||||
|
||||
switch (resolverArgsType) {
|
||||
case ResolverArgsType.CreateOne:
|
||||
case ResolverArgsType.CREATE_ONE:
|
||||
return {
|
||||
...args,
|
||||
data: (
|
||||
@@ -55,7 +56,7 @@ export class QueryRunnerArgsFactory {
|
||||
})
|
||||
)[0],
|
||||
} satisfies CreateOneResolverArgs;
|
||||
case ResolverArgsType.CreateMany:
|
||||
case ResolverArgsType.CREATE_MANY:
|
||||
return {
|
||||
...args,
|
||||
data: await this.overrideDataByFieldMetadata({
|
||||
@@ -64,7 +65,7 @@ export class QueryRunnerArgsFactory {
|
||||
objectMetadataItemWithFieldMaps,
|
||||
}),
|
||||
} satisfies CreateManyResolverArgs;
|
||||
case ResolverArgsType.UpdateOne:
|
||||
case ResolverArgsType.UPDATE_ONE:
|
||||
return {
|
||||
...args,
|
||||
id: (args as UpdateOneResolverArgs).id,
|
||||
@@ -77,7 +78,7 @@ export class QueryRunnerArgsFactory {
|
||||
})
|
||||
)[0],
|
||||
} satisfies UpdateOneResolverArgs;
|
||||
case ResolverArgsType.UpdateMany:
|
||||
case ResolverArgsType.UPDATE_MANY:
|
||||
return {
|
||||
...args,
|
||||
filter: this.overrideFilterByFieldMetadata(
|
||||
@@ -93,7 +94,7 @@ export class QueryRunnerArgsFactory {
|
||||
})
|
||||
)[0],
|
||||
} satisfies UpdateManyResolverArgs;
|
||||
case ResolverArgsType.FindOne:
|
||||
case ResolverArgsType.FIND_ONE:
|
||||
return {
|
||||
...args,
|
||||
filter: this.overrideFilterByFieldMetadata(
|
||||
@@ -101,7 +102,7 @@ export class QueryRunnerArgsFactory {
|
||||
options.objectMetadataItemWithFieldMaps,
|
||||
),
|
||||
};
|
||||
case ResolverArgsType.FindMany:
|
||||
case ResolverArgsType.FIND_MANY:
|
||||
return {
|
||||
...args,
|
||||
filter: this.overrideFilterByFieldMetadata(
|
||||
@@ -109,7 +110,7 @@ export class QueryRunnerArgsFactory {
|
||||
options.objectMetadataItemWithFieldMaps,
|
||||
),
|
||||
};
|
||||
case ResolverArgsType.FindDuplicates:
|
||||
case ResolverArgsType.FIND_DUPLICATES:
|
||||
return {
|
||||
...args,
|
||||
ids: (await Promise.all(
|
||||
@@ -129,7 +130,7 @@ export class QueryRunnerArgsFactory {
|
||||
shouldBackfillPositionIfUndefined: false,
|
||||
}),
|
||||
} satisfies FindDuplicatesResolverArgs;
|
||||
case ResolverArgsType.MergeMany:
|
||||
case ResolverArgsType.MERGE_MANY:
|
||||
return {
|
||||
...args,
|
||||
ids: (await Promise.all(
|
||||
@@ -146,7 +147,7 @@ export class QueryRunnerArgsFactory {
|
||||
.conflictPriorityIndex,
|
||||
dryRun: (args as MergeManyResolverArgs).dryRun,
|
||||
} satisfies MergeManyResolverArgs;
|
||||
case ResolverArgsType.GroupBy:
|
||||
case ResolverArgsType.GROUP_BY:
|
||||
return {
|
||||
...args,
|
||||
filter: this.overrideFilterByFieldMetadata(
|
||||
|
||||
Reference in New Issue
Block a user