fix(workflow-engine-*): q text search (#12435)

This commit is contained in:
Adrien de Peretti
2025-05-11 16:21:41 +02:00
committed by GitHub
parent e4d196e522
commit 9d29078b0d
6 changed files with 237 additions and 5 deletions
@@ -0,0 +1,94 @@
import {
createStep,
createWorkflow,
StepResponse,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import { Modules } from "@medusajs/utils"
import {
adminHeaders,
createAdminUser,
} from "../../../../helpers/create-admin-user"
jest.setTimeout(300000)
medusaIntegrationTestRunner({
testSuite: ({ dbConnection, getContainer, api }) => {
let container
beforeEach(async () => {
container = getContainer()
await createAdminUser(dbConnection, adminHeaders, container)
})
describe("GET /admin/workflow-executions", () => {
it("should filter using q", async () => {
const step1 = createStep(
{
name: "my-step",
},
async (_) => {
return new StepResponse({ result: "success" })
}
)
const workflowName = "workflow-admin/workflow-executions"
createWorkflow(
{
name: workflowName,
retentionTime: 50,
},
function (input: WorkflowData<{ initial: string }>) {
const stepRes = step1()
return new WorkflowResponse(stepRes)
}
)
const engine = container.resolve(Modules.WORKFLOW_ENGINE)
const transactionId = "test-transaction-id"
await engine.run(workflowName, {
transactionId,
input: {
initial: "test",
},
})
const transactionId2 = "unknown"
await engine.run(workflowName, {
transactionId: transactionId2,
input: {
initial: "test",
},
})
const q = "transaction-id"
const response = await api.get(
`/admin/workflows-executions?q=${q}`,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.workflow_executions.length).toEqual(1)
expect(response.data.workflow_executions[0].transaction_id).toEqual(
transactionId
)
const q2 = "known"
const response2 = await api.get(
`/admin/workflows-executions?q=${q2}`,
adminHeaders
)
expect(response2.status).toEqual(200)
expect(response2.data.workflow_executions.length).toEqual(1)
expect(response2.data.workflow_executions[0].transaction_id).toEqual(
transactionId2
)
})
})
},
})
@@ -1,6 +1,10 @@
import { FindParams } from "../../common" import { FindParams } from "../../common"
export interface AdminGetWorkflowExecutionsParams extends FindParams { export interface AdminGetWorkflowExecutionsParams extends FindParams {
/**
* Filter using a search query.
*/
q?: string
/** /**
* Filter by the ID of the transaction to retrieve workflow executions for a specific transaction. * Filter by the ID of the transaction to retrieve workflow executions for a specific transaction.
*/ */
@@ -1,19 +1,20 @@
import { BaseFilterable, OperatorMap } from "../dal" import { BaseFilterable, OperatorMap } from "../dal"
import { TransactionState } from "../http"
export interface WorkflowExecutionDTO { export interface WorkflowExecutionDTO {
id: string id: string
workflow_id: string workflow_id: string
transaction_id: string transaction_id: string
execution: string execution: Record<string, any> | null
context: string context: Record<string, any> | null
state: any state: TransactionState
created_at: Date created_at: Date
updated_at: Date updated_at: Date
deleted_at: Date deleted_at: Date | null
} }
export interface FilterableWorkflowExecutionProps export interface FilterableWorkflowExecutionProps
extends BaseFilterable<FilterableWorkflowExecutionProps> { extends BaseFilterable<FilterableWorkflowExecutionProps> {
q?: string
id?: string | string[] | OperatorMap<string> id?: string | string[] | OperatorMap<string>
workflow_id?: string | string[] | OperatorMap<string> workflow_id?: string | string[] | OperatorMap<string>
transaction_id?: string | string[] | OperatorMap<string> transaction_id?: string | string[] | OperatorMap<string>
@@ -16,6 +16,7 @@ export const AdminGetWorkflowExecutionsParams = createFindParams({
limit: 100, limit: 100,
}).merge( }).merge(
z.object({ z.object({
q: z.string().optional(),
transaction_id: z.union([z.string(), z.array(z.string())]).optional(), transaction_id: z.union([z.string(), z.array(z.string())]).optional(),
workflow_id: z.union([z.string(), z.array(z.string())]).optional(), workflow_id: z.union([z.string(), z.array(z.string())]).optional(),
}) })
@@ -1,12 +1,16 @@
import { import {
Context, Context,
DAL, DAL,
FilterableWorkflowExecutionProps,
FindConfig,
InferEntityType, InferEntityType,
InternalModuleDeclaration, InternalModuleDeclaration,
ModulesSdkTypes, ModulesSdkTypes,
WorkflowExecutionDTO,
WorkflowsSdkTypes, WorkflowsSdkTypes,
} from "@medusajs/framework/types" } from "@medusajs/framework/types"
import { import {
InjectManager,
InjectSharedContext, InjectSharedContext,
isDefined, isDefined,
MedusaContext, MedusaContext,
@@ -74,6 +78,68 @@ export class WorkflowsModuleService<
}, },
} }
static prepareFilters<T>(filters: T & { q?: string }) {
const filters_ = { ...filters } // shallow copy
if (filters_?.q) {
const q = filters_.q
delete filters_.q
const textSearch = { $ilike: `%${q}%` }
const textSearchFilters = {
$or: [
{
transaction_id: textSearch,
},
{
workflow_id: textSearch,
},
{
state: textSearch,
},
{
execution: {
runId: textSearch,
},
},
],
}
if (!Object.keys(filters_).length) {
return textSearchFilters
} else {
return { $and: [filters, textSearchFilters] }
}
}
return filters
}
@InjectManager()
// @ts-expect-error
async listWorkflowExecutions(
filters: FilterableWorkflowExecutionProps = {},
config?: FindConfig<WorkflowExecutionDTO>,
@MedusaContext() sharedContext?: Context
) {
const filters_ = WorkflowsModuleService.prepareFilters(filters)
return await super.listWorkflowExecutions(filters_, config, sharedContext)
}
@InjectManager()
// @ts-expect-error
async listAndCountWorkflowExecutions(
filters: FilterableWorkflowExecutionProps = {},
config?: FindConfig<WorkflowExecutionDTO>,
@MedusaContext() sharedContext?: Context
) {
const filters_ = WorkflowsModuleService.prepareFilters(filters)
return await super.listAndCountWorkflowExecutions(
filters_,
config,
sharedContext
)
}
@InjectSharedContext() @InjectSharedContext()
async run<TWorkflow extends string | ReturnWorkflow<any, any, any>>( async run<TWorkflow extends string | ReturnWorkflow<any, any, any>>(
workflowIdOrWorkflow: TWorkflow, workflowIdOrWorkflow: TWorkflow,
@@ -1,12 +1,16 @@
import { import {
Context, Context,
DAL, DAL,
FilterableWorkflowExecutionProps,
FindConfig,
InferEntityType, InferEntityType,
InternalModuleDeclaration, InternalModuleDeclaration,
ModulesSdkTypes, ModulesSdkTypes,
WorkflowExecutionDTO,
WorkflowsSdkTypes, WorkflowsSdkTypes,
} from "@medusajs/framework/types" } from "@medusajs/framework/types"
import { import {
InjectManager,
InjectSharedContext, InjectSharedContext,
isDefined, isDefined,
MedusaContext, MedusaContext,
@@ -86,6 +90,68 @@ export class WorkflowsModuleService<
}, },
} }
static prepareFilters<T>(filters: T & { q?: string }) {
const filters_ = { ...filters } // shallow copy
if (filters_?.q) {
const q = filters_.q
delete filters_.q
const textSearch = { $ilike: `%${q}%` }
const textSearchFilters = {
$or: [
{
transaction_id: textSearch,
},
{
workflow_id: textSearch,
},
{
state: textSearch,
},
{
execution: {
runId: textSearch,
},
},
],
}
if (!Object.keys(filters_).length) {
return textSearchFilters
} else {
return { $and: [filters, textSearchFilters] }
}
}
return filters
}
@InjectManager()
// @ts-expect-error
async listWorkflowExecutions(
filters: FilterableWorkflowExecutionProps = {},
config?: FindConfig<WorkflowExecutionDTO>,
@MedusaContext() sharedContext?: Context
) {
const filters_ = WorkflowsModuleService.prepareFilters(filters)
return await super.listWorkflowExecutions(filters_, config, sharedContext)
}
@InjectManager()
// @ts-expect-error
async listAndCountWorkflowExecutions(
filters: FilterableWorkflowExecutionProps = {},
config?: FindConfig<WorkflowExecutionDTO>,
@MedusaContext() sharedContext?: Context
) {
const filters_ = WorkflowsModuleService.prepareFilters(filters)
return await super.listAndCountWorkflowExecutions(
filters_,
config,
sharedContext
)
}
@InjectSharedContext() @InjectSharedContext()
async run<TWorkflow extends string | ReturnWorkflow<any, any, any>>( async run<TWorkflow extends string | ReturnWorkflow<any, any, any>>(
workflowIdOrWorkflow: TWorkflow, workflowIdOrWorkflow: TWorkflow,