From bed00e154d35131305aa30a2e0f2aa006362ab81 Mon Sep 17 00:00:00 2001 From: Stevche Radevski Date: Fri, 6 Dec 2024 08:39:35 +0100 Subject: [PATCH 1/9] feat(js-sdk): Make credentials configurable in SDK (#10464) --- packages/core/js-sdk/src/client.ts | 19 ++++++++++++------- packages/core/js-sdk/src/types.ts | 1 + 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/core/js-sdk/src/client.ts b/packages/core/js-sdk/src/client.ts index 025e0b7b0a6e0..14994129a463b 100644 --- a/packages/core/js-sdk/src/client.ts +++ b/packages/core/js-sdk/src/client.ts @@ -58,17 +58,20 @@ const normalizeRequest = ( body = JSON.stringify(body) } + // "credentials" is not supported in some environments (eg. on the backend), and it might throw an exception if the field is set. const isFetchCredentialsSupported = "credentials" in Request.prototype + // Oftentimes the server will be on a different origin, so we want to default to include + // Note that the cookie's SameSite attribute takes precedence over this setting. + const credentials = + config.auth?.type === "session" + ? config.auth?.fetchCredentials || "include" + : "omit" + return { ...init, headers, - // TODO: Setting this to "include" poses some security risks, as it will send cookies to any domain. We should consider making this configurable. - credentials: isFetchCredentialsSupported - ? config.auth?.type === "session" - ? "include" - : "omit" - : undefined, + credentials: isFetchCredentialsSupported ? credentials : undefined, ...(body ? { body: body as RequestInit["body"] } : {}), } as RequestInit } @@ -231,7 +234,9 @@ export class Client { let normalizedInput: RequestInfo | URL = input if (input instanceof URL || typeof input === "string") { const baseUrl = new URL(this.config.baseUrl) - const fullPath = `${baseUrl.pathname.replace(/\/$/, '')}/${input.toString().replace(/^\//, '')}` + const fullPath = `${baseUrl.pathname.replace(/\/$/, "")}/${input + .toString() + .replace(/^\//, "")}` normalizedInput = new URL(fullPath, baseUrl.origin) if (init?.query) { const params = Object.fromEntries( diff --git a/packages/core/js-sdk/src/types.ts b/packages/core/js-sdk/src/types.ts index af935549eab00..c4b08291f5f4f 100644 --- a/packages/core/js-sdk/src/types.ts +++ b/packages/core/js-sdk/src/types.ts @@ -14,6 +14,7 @@ export type Config = { type?: "jwt" | "session" jwtTokenStorageKey?: string jwtTokenStorageMethod?: "local" | "session" | "memory" | "nostore" + fetchCredentials?: "include" | "omit" | "same-origin" } logger?: Logger debug?: boolean From 597bffaab396931880c6986a889cbe9816a266e2 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Fri, 6 Dec 2024 09:53:34 +0200 Subject: [PATCH 2/9] fix(framework): add missing query type argument in request types (#10456) * fix(framework): add missing query type argument in request types * fix types --- .changeset/thick-cars-smash.md | 5 +++++ packages/core/framework/src/http/types.ts | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 .changeset/thick-cars-smash.md diff --git a/.changeset/thick-cars-smash.md b/.changeset/thick-cars-smash.md new file mode 100644 index 0000000000000..f28372c575cc3 --- /dev/null +++ b/.changeset/thick-cars-smash.md @@ -0,0 +1,5 @@ +--- +"@medusajs/framework": patch +--- + +fix(framework): add missing query type argument in request types diff --git a/packages/core/framework/src/http/types.ts b/packages/core/framework/src/http/types.ts index 1adff9d130563..9596b7e1b4a17 100644 --- a/packages/core/framework/src/http/types.ts +++ b/packages/core/framework/src/http/types.ts @@ -163,13 +163,14 @@ export interface PublishableKeyContext { sales_channel_ids: string[] } -export interface AuthenticatedMedusaRequest - extends MedusaRequest { +export interface AuthenticatedMedusaRequest> + extends MedusaRequest { auth_context: AuthContext publishable_key_context?: PublishableKeyContext } -export interface MedusaStoreRequest extends MedusaRequest { +export interface MedusaStoreRequest> + extends MedusaRequest { auth_context?: AuthContext publishable_key_context: PublishableKeyContext } From e3459b1f2fafba8417c84373bf4469a443d17542 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Fri, 6 Dec 2024 11:32:57 +0200 Subject: [PATCH 3/9] docs: fix links in extend product guide (#10472) * docs: fix links in extend product guide * fix links --- .../app/commerce-modules/cart/extend/page.mdx | 4 ++-- .../commerce-modules/customer/extend/page.mdx | 4 ++-- .../commerce-modules/product/extend/page.mdx | 22 +++++++++---------- .../promotion/extend/page.mdx | 8 +++---- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/www/apps/resources/app/commerce-modules/cart/extend/page.mdx b/www/apps/resources/app/commerce-modules/cart/extend/page.mdx index 4480c7c545ad2..d9ccafc782795 100644 --- a/www/apps/resources/app/commerce-modules/cart/extend/page.mdx +++ b/www/apps/resources/app/commerce-modules/cart/extend/page.mdx @@ -43,7 +43,7 @@ This creates a `Custom` data model that has the `id` and `custom_name` propertie -Learn more about data models in [this guide](!docs!/learn/data-models). +Learn more about data models in [this guide](!docs!/learn/basics/modules#1-create-data-model). @@ -55,7 +55,7 @@ Next, you'll define a module link between the `Custom` and `Cart` data model. A -Learn more about module links in [this guide](!docs!/learn/module-links). +Learn more about module links in [this guide](!docs!/learn/advanced-development/module-links). diff --git a/www/apps/resources/app/commerce-modules/customer/extend/page.mdx b/www/apps/resources/app/commerce-modules/customer/extend/page.mdx index 002ef8106b372..352c3882100b9 100644 --- a/www/apps/resources/app/commerce-modules/customer/extend/page.mdx +++ b/www/apps/resources/app/commerce-modules/customer/extend/page.mdx @@ -49,7 +49,7 @@ This creates a `Custom` data model that has the `id` and `custom_name` propertie -Learn more about data models in [this guide](!docs!/learn/data-models). +Learn more about data models in [this guide](!docs!/learn/basics/modules#1-create-data-model). @@ -61,7 +61,7 @@ Next, you'll define a module link between the `Custom` and `Customer` data model -Learn more about module links in [this guide](!docs!/learn/module-links). +Learn more about module links in [this guide](!docs!/learn/advanced-development/module-links). diff --git a/www/apps/resources/app/commerce-modules/product/extend/page.mdx b/www/apps/resources/app/commerce-modules/product/extend/page.mdx index 8fa711147b796..decd74503559e 100644 --- a/www/apps/resources/app/commerce-modules/product/extend/page.mdx +++ b/www/apps/resources/app/commerce-modules/product/extend/page.mdx @@ -28,7 +28,7 @@ Consider you have a Hello Module defined in the `/src/modules/hello` directory. -If you don't have a module, follow [this guide](!docs!/basics/modules) to create one. +If you don't have a module, follow [this guide](!docs!/learn/basics/modules) to create one. @@ -49,7 +49,7 @@ This creates a `Custom` data model that has the `id` and `custom_name` propertie -Learn more about data models in [this guide](!docs!/data-models). +Learn more about data models in [this guide](!docs!/learn/basics/modules#1-create-data-model). @@ -61,7 +61,7 @@ Next, you'll define a module link between the `Custom` and `Product` data model. -Learn more about module links in [this guide](!docs!/module-links). +Learn more about module links in [this guide](!docs!/learn/advanced-development/module-links). @@ -88,7 +88,7 @@ This defines a link between the `Product` and `Custom` data models. Using this l items={[ { text: "Module must be registered in medusa-config.js", - link: "!docs!/basics/modules#4-add-module-to-configurations" + link: "!docs!/learn/basics/modules#4-add-module-to-configurations" } ]} /> @@ -121,7 +121,7 @@ To do that, you'll consume the [productsCreated](/references/medusa-workflows/cr -Learn more about workflow hooks in [this guide](!docs!/advanced-development/workflows/workflow-hooks). +Learn more about workflow hooks in [this guide](!docs!/learn/advanced-development/workflows/workflow-hooks). @@ -156,7 +156,7 @@ In the snippet above, you add a validation rule indicating that `custom_name` is -Learn more about additional data validation in [this guide](!docs!/advanced-development/api-routes/additional-data). +Learn more about additional data validation in [this guide](!docs!/learn/advanced-development/api-routes/additional-data). @@ -208,7 +208,7 @@ In the compensation function that undoes the step's actions in case of an error, -Learn more about compensation functions in [this guide](!docs!/advanced-development/workflows/compensation-function). +Learn more about compensation functions in [this guide](!docs!/learn/advanced-development/workflows/compensation-function). @@ -266,9 +266,9 @@ The workflow accepts as an input the created product and the `additional_data` p In the workflow, you: -1. Use the `transform` utility to get the value of `custom_name` based on whether it's set in `additional_data`. Learn more about why you can't use conditional operators in a workflow without using `transform` in [this guide](!docs!/advanced-development/workflows/conditions#why-if-conditions-arent-allowed-in-workflows). +1. Use the `transform` utility to get the value of `custom_name` based on whether it's set in `additional_data`. Learn more about why you can't use conditional operators in a workflow without using `transform` in [this guide](!docs!/learn/advanced-development/workflows/conditions#why-if-conditions-arent-allowed-in-workflows). 2. Create the `Custom` record using the `createCustomStep`. -3. Use the `when-then` utility to link the product to the `Custom` record if it was created. Learn more about why you can't use if-then conditions in a workflow without using `when-then` in [this guide](!docs!/advanced-development/workflows/conditions#why-if-conditions-arent-allowed-in-workflows). +3. Use the `when-then` utility to link the product to the `Custom` record if it was created. Learn more about why you can't use if-then conditions in a workflow without using `when-then` in [this guide](!docs!/learn/advanced-development/workflows/conditions#why-if-conditions-arent-allowed-in-workflows). You'll next execute the workflow in the hook handler. @@ -373,7 +373,7 @@ Among the returned `product` object, you'll find a `custom` property which holds ### Retrieve using Query -You can also retrieve the `Custom` record linked to a product in your code using [Query](!docs!/advanced-development/module-links/query). +You can also retrieve the `Custom` record linked to a product in your code using [Query](!docs!/learn/advanced-development/module-links/query). For example: @@ -387,7 +387,7 @@ const { data: [product] } = await query.graph({ }) ``` -Learn more about how to use Query in [this guide](!docs!/advanced-development/module-links/query). +Learn more about how to use Query in [this guide](!docs!/learn/advanced-development/module-links/query). --- diff --git a/www/apps/resources/app/commerce-modules/promotion/extend/page.mdx b/www/apps/resources/app/commerce-modules/promotion/extend/page.mdx index fc54f108b4844..56b7489e75c25 100644 --- a/www/apps/resources/app/commerce-modules/promotion/extend/page.mdx +++ b/www/apps/resources/app/commerce-modules/promotion/extend/page.mdx @@ -28,7 +28,7 @@ Consider you have a Hello Module defined in the `/src/modules/hello` directory. -If you don't have a module, follow [this guide](!docs!/basics/modules) to create one. +If you don't have a module, follow [this guide](!docs!/learn/basics/modules) to create one. @@ -49,7 +49,7 @@ This creates a `Custom` data model that has the `id` and `custom_name` propertie -Learn more about data models in [this guide](!docs!/data-models). +Learn more about data models in [this guide](!docs!/learn/basics/modules#1-create-data-model). @@ -61,7 +61,7 @@ Next, you'll define a module link between the `Custom` and `Promotion` data mode -Learn more about module links in [this guide](!docs!/module-links). +Learn more about module links in [this guide](!docs!/learn/advanced-development/module-links). @@ -88,7 +88,7 @@ This defines a link between the `Promotion` and `Custom` data models. Using this items={[ { text: "Module must be registered in medusa-config.js", - link: "!docs!/basics/modules#4-add-module-to-configurations" + link: "!docs!/learn/basics/modules#4-add-module-to-configurations" } ]} /> From abdd4c9e99476561379294d4d69180ada40880e9 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Fri, 6 Dec 2024 12:21:19 +0200 Subject: [PATCH 4/9] docs-util: support new query type argument (#10468) * add support for second query argument * more fixes --- .../src/classes/helpers/schema-factory.ts | 6 + .../docs-generator/src/classes/kinds/oas.ts | 227 ++++++++++-------- 2 files changed, 139 insertions(+), 94 deletions(-) diff --git a/www/utils/packages/docs-generator/src/classes/helpers/schema-factory.ts b/www/utils/packages/docs-generator/src/classes/helpers/schema-factory.ts index 6be03bca6165a..c5c2f05471a28 100644 --- a/www/utils/packages/docs-generator/src/classes/helpers/schema-factory.ts +++ b/www/utils/packages/docs-generator/src/classes/helpers/schema-factory.ts @@ -37,6 +37,12 @@ class SchemaFactory { BigNumberValue: { type: "number", }, + difference_due: { + type: "number", + }, + refund_amount: { + type: "number", + }, File: { type: "object", description: "A File to upload.", diff --git a/www/utils/packages/docs-generator/src/classes/kinds/oas.ts b/www/utils/packages/docs-generator/src/classes/kinds/oas.ts index ec63cde75575b..8657ae76d4c4d 100644 --- a/www/utils/packages/docs-generator/src/classes/kinds/oas.ts +++ b/www/utils/packages/docs-generator/src/classes/kinds/oas.ts @@ -65,6 +65,7 @@ class OasKindGenerator extends FunctionKindGenerator { "MedusaRequest", "RequestWithContext", "AuthenticatedMedusaRequest", + "MedusaStoreRequest", ] // as it's not always possible to detect authenticated request // use this to override the default detection logic. @@ -1015,120 +1016,158 @@ class OasKindGenerator extends FunctionKindGenerator { */ requestSchema?: OpenApiSchema } { - const parameters: OpenAPIV3.ParameterObject[] = [] + const queryParameters: OpenAPIV3.ParameterObject[] = [] let requestSchema: OpenApiSchema | undefined if ( - node.parameters[0].type && - ts.isTypeReferenceNode(node.parameters[0].type) + !node.parameters[0].type || + !ts.isTypeReferenceNode(node.parameters[0].type) ) { - const requestType = this.checker.getTypeFromTypeNode( - node.parameters[0].type - ) as ts.TypeReference - // TODO for now I'll use the type for validatedQuery until - // we have an actual approach to infer query types - const querySymbol = requestType.getProperty("validatedQuery") - if (querySymbol) { - const { shouldAddFields, shouldAddPagination } = - this.shouldAddQueryParams(node) - const queryType = this.checker.getTypeOfSymbol(querySymbol) - const queryTypeName = this.checker.typeToString(queryType) - queryType.getProperties().forEach((property) => { - const propertyName = property.getName() - // if this is a field / pagination query parameter and - // they're not used in the route, don't add them. - if ( - (this.FIELD_QUERY_PARAMS.includes(propertyName) && - !shouldAddFields) || - (this.PAGINATION_QUERY_PARAMS.includes(propertyName) && - !shouldAddPagination) - ) { - return - } - const propertyType = this.checker.getTypeOfSymbol(property) - const descriptionOptions: SchemaDescriptionOptions = { - typeStr: propertyName, - parentName: tagName, - rawParentName: queryTypeName, - node: property.valueDeclaration, - symbol: property, - nodeType: propertyType, - } - parameters.push( - this.getParameterObject({ - name: propertyName, - type: "query", - description: this.getSchemaDescription(descriptionOptions), - required: this.isRequired(property), - schema: this.typeToSchema({ - itemType: propertyType, - title: propertyName, - descriptionOptions, - context: "query", - saveSchema: !forUpdate, - }), - }) - ) - }) + return { + queryParameters, + requestSchema, } + } - const requestTypeArguments = - requestType.typeArguments || requestType.aliasTypeArguments + const requestType = this.checker.getTypeFromTypeNode( + node.parameters[0].type + ) as ts.TypeReference + // TODO for now I'll use the type for validatedQuery until + // we have an actual approach to infer query types + const querySymbol = requestType.getProperty("validatedQuery") + if (querySymbol) { + const { shouldAddFields, shouldAddPagination } = + this.shouldAddQueryParams(node) + const queryType = this.checker.getTypeOfSymbol(querySymbol) + const queryTypeName = this.checker.typeToString(queryType) + queryType.getProperties().forEach((property) => { + const propertyName = property.getName() + // if this is a field / pagination query parameter and + // they're not used in the route, don't add them. + if ( + (this.FIELD_QUERY_PARAMS.includes(propertyName) && + !shouldAddFields) || + (this.PAGINATION_QUERY_PARAMS.includes(propertyName) && + !shouldAddPagination) + ) { + return + } + const propertyType = this.checker.getTypeOfSymbol(property) + const descriptionOptions: SchemaDescriptionOptions = { + typeStr: propertyName, + parentName: tagName, + rawParentName: queryTypeName, + node: property.valueDeclaration, + symbol: property, + nodeType: propertyType, + } + queryParameters.push( + this.getParameterObject({ + name: propertyName, + type: "query", + description: this.getSchemaDescription(descriptionOptions), + required: this.isRequired(property), + schema: this.typeToSchema({ + itemType: propertyType, + title: propertyName, + descriptionOptions, + context: "query", + saveSchema: !forUpdate, + }), + }) + ) + }) + } - if (requestTypeArguments?.length === 1) { - const zodObjectTypeName = getCorrectZodTypeName({ - typeReferenceNode: node.parameters[0].type, - itemType: requestTypeArguments[0], - }) - const isQuery = methodName === "get" - const parameterSchema = this.typeToSchema({ - itemType: requestTypeArguments[0], + const requestTypeArguments = + requestType.typeArguments || requestType.aliasTypeArguments + + if (!requestTypeArguments || requestTypeArguments.length < 2) { + return { + queryParameters, + requestSchema, + } + } + + // Not all routes support a second type argument yet, + // so the query param may be passed in the first type argument + const hasQueryParams = requestTypeArguments[1].getProperties().length > 0 + // Not all routes support a second type argument yet, + // so we have to support routes that pass the query parameters type + // in the first type argument + const isQuery = methodName === "get" + + const zodObjectRequestBodyTypeName = getCorrectZodTypeName({ + typeReferenceNode: node.parameters[0].type, + itemType: requestTypeArguments[0], + }) + const zodObjectQueryTypeName = getCorrectZodTypeName({ + typeReferenceNode: node.parameters[0].type, + itemType: requestTypeArguments[1], + }) + + const requestBodyParameterSchema = this.typeToSchema({ + itemType: requestTypeArguments[0], + descriptionOptions: { + parentName: tagName, + rawParentName: this.checker.typeToString(requestTypeArguments[0]), + }, + zodObjectTypeName: zodObjectRequestBodyTypeName, + context: isQuery ? "query" : "request", + saveSchema: !forUpdate, + }) + const queryParameterSchema = hasQueryParams + ? this.typeToSchema({ + itemType: requestTypeArguments[1], descriptionOptions: { parentName: tagName, - rawParentName: this.checker.typeToString(requestTypeArguments[0]), + rawParentName: this.checker.typeToString(requestTypeArguments[1]), }, - zodObjectTypeName: zodObjectTypeName, - context: isQuery ? "query" : "request", + zodObjectTypeName: zodObjectQueryTypeName, + context: "query", saveSchema: !forUpdate, }) + : requestBodyParameterSchema - // If function is a GET function, add the type parameter to the - // query parameters instead of request parameters. - if (isQuery) { - if (parameterSchema.type === "object" && parameterSchema.properties) { - Object.entries(parameterSchema.properties).forEach( - ([key, propertySchema]) => { - if ("$ref" in propertySchema) { - return - } + // If function is a GET function, add the type parameter to the + // query parameters instead of request parameters. + if ( + (isQuery || hasQueryParams) && + queryParameterSchema.type === "object" && + queryParameterSchema.properties + ) { + Object.entries(queryParameterSchema.properties).forEach( + ([key, propertySchema]) => { + if ("$ref" in propertySchema) { + return + } - // check if parameter is already added - const isAdded = parameters.some((param) => param.name === key) + // check if parameter is already added + const isAdded = queryParameters.some((param) => param.name === key) - if (isAdded) { - return - } - - parameters.push( - this.getParameterObject({ - name: key, - type: "query", - description: propertySchema.description, - required: parameterSchema.required?.includes(key) || false, - schema: propertySchema, - }) - ) - } - ) + if (isAdded) { + return } - } else if (methodName !== "delete") { - requestSchema = parameterSchema + + queryParameters.push( + this.getParameterObject({ + name: key, + type: "query", + description: propertySchema.description, + required: queryParameterSchema.required?.includes(key) || false, + schema: propertySchema, + }) + ) } - } + ) + } + + if (methodName !== "delete" && methodName !== "get") { + requestSchema = requestBodyParameterSchema } return { - queryParameters: parameters, + queryParameters, requestSchema, } } From 7e04091b492c1680fdf5321f512e43b90ba09e12 Mon Sep 17 00:00:00 2001 From: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:33:18 +0100 Subject: [PATCH 5/9] fix(dashboard): Prevent sending off empty string as handle for product category (#10473) --- .changeset/heavy-peaches-sniff.md | 5 +++++ .../create-category-form/create-category-form.tsx | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 .changeset/heavy-peaches-sniff.md diff --git a/.changeset/heavy-peaches-sniff.md b/.changeset/heavy-peaches-sniff.md new file mode 100644 index 0000000000000..d8488adf4029e --- /dev/null +++ b/.changeset/heavy-peaches-sniff.md @@ -0,0 +1,5 @@ +--- +"@medusajs/dashboard": patch +--- + +fix(dashboard): Prevent sending off empty string as handle for product category diff --git a/packages/admin/dashboard/src/routes/categories/category-create/components/create-category-form/create-category-form.tsx b/packages/admin/dashboard/src/routes/categories/category-create/components/create-category-form/create-category-form.tsx index 91531834aa051..b4a156fb63e5b 100644 --- a/packages/admin/dashboard/src/routes/categories/category-create/components/create-category-form/create-category-form.tsx +++ b/packages/admin/dashboard/src/routes/categories/category-create/components/create-category-form/create-category-form.tsx @@ -10,6 +10,7 @@ import { } from "../../../../../components/modals" import { KeyboundForm } from "../../../../../components/utilities/keybound-form" import { useCreateProductCategory } from "../../../../../hooks/api/categories" +import { transformNullableFormData } from "../../../../../lib/form-helpers" import { CreateCategoryDetails } from "./create-category-details" import { CreateCategoryNesting } from "./create-category-nesting" import { CreateCategoryDetailsSchema, CreateCategorySchema } from "./schema" @@ -79,13 +80,15 @@ export const CreateCategoryForm = ({ const { mutateAsync, isPending } = useCreateProductCategory() const handleSubmit = form.handleSubmit((data) => { - const { visibility, status, parent_category_id, rank, ...rest } = data + const { visibility, status, parent_category_id, rank, name, ...rest } = data + const parsedData = transformNullableFormData(rest, false) setShouldFreeze(true) mutateAsync( { - ...rest, + name: name, + ...parsedData, parent_category_id: parent_category_id ?? undefined, rank: rank ?? undefined, is_active: status === "active", From f3c91c908a1c8d38eb31c21dbd94d4ad6cea9688 Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Fri, 6 Dec 2024 17:38:15 +0530 Subject: [PATCH 6/9] refactor: migrate store module to DML (#10467) --- .changeset/gold-birds-draw.md | 5 + .../migrations/.snapshot-medusa-store.json | 36 +++++-- .../src/migrations/Migration20241206083313.ts | 13 +++ packages/modules/store/src/models/currency.ts | 88 +++-------------- packages/modules/store/src/models/store.ts | 99 +++---------------- .../src/services/store-module-service.ts | 11 ++- 6 files changed, 78 insertions(+), 174 deletions(-) create mode 100644 .changeset/gold-birds-draw.md create mode 100644 packages/modules/store/src/migrations/Migration20241206083313.ts diff --git a/.changeset/gold-birds-draw.md b/.changeset/gold-birds-draw.md new file mode 100644 index 0000000000000..ea21e15d809c4 --- /dev/null +++ b/.changeset/gold-birds-draw.md @@ -0,0 +1,5 @@ +--- +"@medusajs/store": patch +--- + +refactor: migrate store module to DML diff --git a/packages/modules/store/src/migrations/.snapshot-medusa-store.json b/packages/modules/store/src/migrations/.snapshot-medusa-store.json index a1b4e149778f4..cd4b7579cf0a3 100644 --- a/packages/modules/store/src/migrations/.snapshot-medusa-store.json +++ b/packages/modules/store/src/migrations/.snapshot-medusa-store.json @@ -1,5 +1,7 @@ { - "namespaces": ["public"], + "namespaces": [ + "public" + ], "name": "public", "tables": [ { @@ -97,15 +99,17 @@ "indexes": [ { "keyName": "IDX_store_deleted_at", - "columnNames": ["deleted_at"], + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_store_deleted_at\" ON \"store\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_store_deleted_at\" ON \"store\" (deleted_at) WHERE deleted_at IS NULL" }, { "keyName": "store_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -189,17 +193,27 @@ "name": "store_currency", "schema": "public", "indexes": [ + { + "keyName": "IDX_store_currency_store_id", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_store_currency_store_id\" ON \"store_currency\" (store_id) WHERE deleted_at IS NULL" + }, { "keyName": "IDX_store_currency_deleted_at", - "columnNames": ["deleted_at"], + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_store_currency_deleted_at\" ON \"store_currency\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_store_currency_deleted_at\" ON \"store_currency\" (deleted_at) WHERE deleted_at IS NULL" }, { "keyName": "store_currency_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -209,9 +223,13 @@ "foreignKeys": { "store_currency_store_id_foreign": { "constraintName": "store_currency_store_id_foreign", - "columnNames": ["store_id"], + "columnNames": [ + "store_id" + ], "localTableName": "public.store_currency", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.store", "deleteRule": "cascade", "updateRule": "cascade" diff --git a/packages/modules/store/src/migrations/Migration20241206083313.ts b/packages/modules/store/src/migrations/Migration20241206083313.ts new file mode 100644 index 0000000000000..de8556efea7a4 --- /dev/null +++ b/packages/modules/store/src/migrations/Migration20241206083313.ts @@ -0,0 +1,13 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20241206083313 extends Migration { + + async up(): Promise { + this.addSql('CREATE INDEX IF NOT EXISTS "IDX_store_currency_store_id" ON "store_currency" (store_id) WHERE deleted_at IS NULL;'); + } + + async down(): Promise { + this.addSql('drop index if exists "IDX_store_currency_store_id";'); + } + +} diff --git a/packages/modules/store/src/models/currency.ts b/packages/modules/store/src/models/currency.ts index e62286f141fdf..ae37ba6092aec 100644 --- a/packages/modules/store/src/models/currency.ts +++ b/packages/modules/store/src/models/currency.ts @@ -1,81 +1,15 @@ -import { - DALUtils, - Searchable, - createPsqlIndexStatementHelper, - generateEntityId, -} from "@medusajs/framework/utils" - -import { - BeforeCreate, - Entity, - OnInit, - PrimaryKey, - Property, - Filter, - ManyToOne, -} from "@mikro-orm/core" +import { model } from "@medusajs/framework/utils" import Store from "./store" -const StoreCurrencyDeletedAtIndex = createPsqlIndexStatementHelper({ - tableName: "store_currency", - columns: "deleted_at", - where: "deleted_at IS NOT NULL", +const StoreCurrency = model.define("StoreCurrency", { + id: model.id({ prefix: "stocur" }).primaryKey(), + currency_code: model.text().searchable(), + is_default: model.boolean().default(false), + store: model + .belongsTo(() => Store, { + mappedBy: "supported_currencies", + }) + .nullable(), }) -@Entity() -@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) -export default class StoreCurrency { - @PrimaryKey({ columnType: "text" }) - id: string - - @Searchable() - @Property({ columnType: "text" }) - currency_code: string - - @Property({ columnType: "boolean", default: false }) - is_default?: boolean - - @ManyToOne(() => Store, { - columnType: "text", - fieldName: "store_id", - mapToPk: true, - nullable: true, - onDelete: "cascade", - }) - store_id: string | null - - @ManyToOne(() => Store, { - persist: false, - nullable: true, - }) - store: Store | null - - @Property({ - onCreate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", - }) - created_at: Date - - @Property({ - onCreate: () => new Date(), - onUpdate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", - }) - updated_at: Date - - @StoreCurrencyDeletedAtIndex.MikroORMIndex() - @Property({ columnType: "timestamptz", nullable: true }) - deleted_at: Date | null = null - - @BeforeCreate() - onCreate() { - this.id = generateEntityId(this.id, "stocur") - } - - @OnInit() - onInit() { - this.id = generateEntityId(this.id, "stocur") - } -} +export default StoreCurrency diff --git a/packages/modules/store/src/models/store.ts b/packages/modules/store/src/models/store.ts index 86df01dbf38de..7a19e130aecab 100644 --- a/packages/modules/store/src/models/store.ts +++ b/packages/modules/store/src/models/store.ts @@ -1,89 +1,20 @@ -import { - DALUtils, - Searchable, - createPsqlIndexStatementHelper, - generateEntityId, -} from "@medusajs/framework/utils" - -import { DAL } from "@medusajs/framework/types" - -import { - BeforeCreate, - Entity, - OnInit, - PrimaryKey, - Property, - Filter, - OptionalProps, - OneToMany, - Collection, - Cascade, -} from "@mikro-orm/core" +import { model } from "@medusajs/framework/utils" import StoreCurrency from "./currency" -type StoreOptionalProps = DAL.SoftDeletableModelDateColumns - -const StoreDeletedAtIndex = createPsqlIndexStatementHelper({ - tableName: "store", - columns: "deleted_at", - where: "deleted_at IS NOT NULL", -}) - -@Entity() -@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) -export default class Store { - [OptionalProps]?: StoreOptionalProps - - @PrimaryKey({ columnType: "text" }) - id: string - - @Searchable() - @Property({ columnType: "text", default: "Medusa Store" }) - name: string - - @OneToMany(() => StoreCurrency, (o) => o.store, { - cascade: [Cascade.PERSIST, "soft-remove"] as any, - }) - supported_currencies = new Collection(this) - - @Property({ columnType: "text", nullable: true }) - default_sales_channel_id: string | null = null - - @Property({ columnType: "text", nullable: true }) - default_region_id: string | null = null - - @Property({ columnType: "text", nullable: true }) - default_location_id: string | null = null - - @Property({ columnType: "jsonb", nullable: true }) - metadata: Record | null = null - - @Property({ - onCreate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", +const Store = model + .define("Store", { + id: model.id({ prefix: "store" }).primaryKey(), + name: model.text().default("Medusa Store").searchable(), + default_sales_channel_id: model.text().nullable(), + default_region_id: model.text().nullable(), + default_location_id: model.text().nullable(), + metadata: model.json().nullable(), + supported_currencies: model.hasMany(() => StoreCurrency, { + mappedBy: "store", + }), }) - created_at: Date - - @Property({ - onCreate: () => new Date(), - onUpdate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", + .cascades({ + delete: ["supported_currencies"], }) - updated_at: Date - - @StoreDeletedAtIndex.MikroORMIndex() - @Property({ columnType: "timestamptz", nullable: true }) - deleted_at: Date | null = null - - @BeforeCreate() - onCreate() { - this.id = generateEntityId(this.id, "store") - } - @OnInit() - onInit() { - this.id = generateEntityId(this.id, "store") - } -} +export default Store diff --git a/packages/modules/store/src/services/store-module-service.ts b/packages/modules/store/src/services/store-module-service.ts index 5f8d422ce06d0..c4654465bf28b 100644 --- a/packages/modules/store/src/services/store-module-service.ts +++ b/packages/modules/store/src/services/store-module-service.ts @@ -1,6 +1,7 @@ import { Context, DAL, + InferEntityType, InternalModuleDeclaration, IStoreModuleService, ModulesSdkTypes, @@ -34,7 +35,9 @@ export default class StoreModuleService implements IStoreModuleService { protected baseRepository_: DAL.RepositoryService - protected readonly storeService_: ModulesSdkTypes.IMedusaInternalService + protected readonly storeService_: ModulesSdkTypes.IMedusaInternalService< + InferEntityType + > constructor( { baseRepository, storeService }: InjectedDependencies, @@ -73,7 +76,7 @@ export default class StoreModuleService async create_( data: StoreTypes.CreateStoreDTO[], @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise[]> { let normalizedInput = StoreModuleService.normalizeInput(data) StoreModuleService.validateCreateRequest(normalizedInput) @@ -107,7 +110,7 @@ export default class StoreModuleService (store): store is StoreTypes.CreateStoreDTO => !store.id ) - const operations: Promise[] = [] + const operations: Promise[]>[] = [] if (forCreate.length) { operations.push(this.create_(forCreate, sharedContext)) @@ -168,7 +171,7 @@ export default class StoreModuleService protected async update_( data: UpdateStoreInput[], @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise[]> { const normalizedInput = StoreModuleService.normalizeInput(data) StoreModuleService.validateUpdateRequest(normalizedInput) From f65a3cc06df9faca3615f30eb4d3bf889efa5765 Mon Sep 17 00:00:00 2001 From: "Carlos R. L. Rodrigues" <37986729+carlos-r-l-rodrigues@users.noreply.github.com> Date: Fri, 6 Dec 2024 09:16:02 -0300 Subject: [PATCH 7/9] fix(utils): avoid optional properties on graphql generated file (#10476) FIXES: SUP-367 --- .changeset/green-pants-remain.md | 5 ++++ .../utils/src/graphql/graphql-to-ts-types.ts | 25 +++++++++++-------- 2 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 .changeset/green-pants-remain.md diff --git a/.changeset/green-pants-remain.md b/.changeset/green-pants-remain.md new file mode 100644 index 0000000000000..6875fb1cc0eab --- /dev/null +++ b/.changeset/green-pants-remain.md @@ -0,0 +1,5 @@ +--- +"@medusajs/utils": patch +--- + +fix: avoid optional fields on graphql generated types diff --git a/packages/core/utils/src/graphql/graphql-to-ts-types.ts b/packages/core/utils/src/graphql/graphql-to-ts-types.ts index b449f6f252f80..7082ae0277e3e 100644 --- a/packages/core/utils/src/graphql/graphql-to-ts-types.ts +++ b/packages/core/utils/src/graphql/graphql-to-ts-types.ts @@ -1,7 +1,7 @@ import { codegen } from "@graphql-codegen/core" -import { type GraphQLSchema, parse, printSchema } from "graphql" import * as typescriptPlugin from "@graphql-codegen/typescript" import { ModuleJoinerConfig } from "@medusajs/types" +import { type GraphQLSchema, parse, printSchema } from "graphql" import { FileSystem } from "../common" function buildEntryPointsTypeMap({ @@ -13,15 +13,15 @@ function buildEntryPointsTypeMap({ }): { entryPoint: string; entityType: any }[] { // build map entry point to there type to be merged and used by the remote query - return joinerConfigs - .flatMap((config) => { - const aliases = Array.isArray(config.alias) - ? config.alias - : config.alias - ? [config.alias] - : [] + return joinerConfigs.flatMap((config) => { + const aliases = Array.isArray(config.alias) + ? config.alias + : config.alias + ? [config.alias] + : [] - return aliases.flatMap((alias) => { + return aliases + .flatMap((alias) => { const names = Array.isArray(alias.name) ? alias.name : [alias.name] const entity = alias?.["entity"] return names.map((aliasItem) => { @@ -35,8 +35,8 @@ function buildEntryPointsTypeMap({ } }) }) - }) - .filter(Boolean) + .filter(Boolean) + }) } async function generateTypes({ @@ -111,6 +111,9 @@ export async function gqlSchemaToTypes({ output: "Record", }, }, + avoidOptionals: { + field: true, // Avoid optional fields in types + }, }, filename: "", schema: parse(printSchema(schema as any)), From b0448a7c35135f5a0e41e0195a507b10ce4b2131 Mon Sep 17 00:00:00 2001 From: "Carlos R. L. Rodrigues" <37986729+carlos-r-l-rodrigues@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:14:14 -0300 Subject: [PATCH 8/9] chore: locking-postgres provider dml (#10478) --- .changeset/silly-waves-add.md | 5 + .../__fixtures__/remote-query-type.ts | 143 +++++++++--------- .../index-service-entry-points.ts | 22 +-- .../modules-sdk/__fixtures__/remote-query.ts | 120 +++++++-------- .../locking-postgres/src/models/locking.ts | 35 +---- 5 files changed, 153 insertions(+), 172 deletions(-) create mode 100644 .changeset/silly-waves-add.md diff --git a/.changeset/silly-waves-add.md b/.changeset/silly-waves-add.md new file mode 100644 index 0000000000000..7fad40b649f60 --- /dev/null +++ b/.changeset/silly-waves-add.md @@ -0,0 +1,5 @@ +--- +"@medusajs/locking-postgres": patch +--- + +chore: locking-postgres provider to DML diff --git a/packages/core/modules-sdk/src/remote-query/__fixtures__/remote-query-type.ts b/packages/core/modules-sdk/src/remote-query/__fixtures__/remote-query-type.ts index 43d66a6750de5..a4889a5e30ed5 100644 --- a/packages/core/modules-sdk/src/remote-query/__fixtures__/remote-query-type.ts +++ b/packages/core/modules-sdk/src/remote-query/__fixtures__/remote-query-type.ts @@ -18,7 +18,6 @@ export type Incremental = | { [P in keyof T]?: P extends " $fragmentName" | "__typename" ? T[P] : never } - /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { ID: { input: string; output: string } @@ -33,232 +32,232 @@ export type Scalars = { export type SimpleProduct = { id: Scalars["ID"]["output"] handle: string - title?: Scalars["String"]["output"] - variants?: Maybe>>> - sales_channels_link?: Array< + title: Scalars["String"]["output"] + variants: Maybe>>> + sales_channels_link: Array< Pick > - sales_channels?: Array> + sales_channels: Array> } export type Product = { - __typename?: "Product" + __typename: "Product" id: Scalars["ID"]["output"] handle: Scalars["String"]["output"] title: Scalars["String"]["output"] - description?: Scalars["String"]["output"] - variants?: Array - sales_channels_link?: Array - sales_channels?: Array - metadata?: Maybe - translation?: Maybe - categories?: Array + description: Scalars["String"]["output"] + variants: Array + sales_channels_link: Array + sales_channels: Array + metadata: Maybe + translation: Maybe + categories: Array } export type ProductTranslation = { - __typename?: "ProductTranslation" + __typename: "ProductTranslation" id: Scalars["ID"]["output"] title: Scalars["String"]["output"] description: Scalars["String"]["output"] - product?: Maybe + product: Maybe } export type ProductVariant = { - __typename?: "ProductVariant" + __typename: "ProductVariant" id: Scalars["ID"]["output"] handle: Scalars["String"]["output"] title: Scalars["String"]["output"] sku: Scalars["String"]["output"] - product?: Maybe - calculated_price?: Maybe - translation?: Maybe + product: Maybe + calculated_price: Maybe + translation: Maybe } export type ProductVariantTranslation = { - __typename?: "ProductVariantTranslation" + __typename: "ProductVariantTranslation" id: Scalars["ID"]["output"] title: Scalars["String"]["output"] description: Scalars["String"]["output"] - variant?: Maybe + variant: Maybe } export type ProductCategory = { - __typename?: "ProductCategory" + __typename: "ProductCategory" id: Scalars["ID"]["output"] handle: Scalars["String"]["output"] - title?: Maybe - translation?: Maybe + title: Maybe + translation: Maybe } export type ProductCategoryTranslation = { - __typename?: "ProductCategoryTranslation" + __typename: "ProductCategoryTranslation" id: Scalars["ID"]["output"] title: Scalars["String"]["output"] description: Scalars["String"]["output"] - category?: Maybe + category: Maybe } export type SalesChannel = { - __typename?: "SalesChannel" + __typename: "SalesChannel" id: Scalars["ID"]["output"] - name?: Maybe - description?: Maybe - created_at?: Maybe - updated_at?: Maybe - products_link?: Maybe>> - api_keys_link?: Maybe>> - locations_link?: Maybe>> + name: Maybe + description: Maybe + created_at: Maybe + updated_at: Maybe + products_link: Maybe>> + api_keys_link: Maybe>> + locations_link: Maybe>> } export type LinkCartPaymentCollection = { - __typename?: "LinkCartPaymentCollection" + __typename: "LinkCartPaymentCollection" cart_id: Scalars["String"]["output"] payment_collection_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkCartPromotion = { - __typename?: "LinkCartPromotion" + __typename: "LinkCartPromotion" cart_id: Scalars["String"]["output"] promotion_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkLocationFulfillmentProvider = { - __typename?: "LinkLocationFulfillmentProvider" + __typename: "LinkLocationFulfillmentProvider" stock_location_id: Scalars["String"]["output"] fulfillment_provider_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkLocationFulfillmentSet = { - __typename?: "LinkLocationFulfillmentSet" + __typename: "LinkLocationFulfillmentSet" stock_location_id: Scalars["String"]["output"] fulfillment_set_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkOrderCart = { - __typename?: "LinkOrderCart" + __typename: "LinkOrderCart" order_id: Scalars["String"]["output"] cart_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkOrderFulfillment = { - __typename?: "LinkOrderFulfillment" + __typename: "LinkOrderFulfillment" order_id: Scalars["String"]["output"] fulfillment_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkOrderPaymentCollection = { - __typename?: "LinkOrderPaymentCollection" + __typename: "LinkOrderPaymentCollection" order_id: Scalars["String"]["output"] payment_collection_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkOrderPromotion = { - __typename?: "LinkOrderPromotion" + __typename: "LinkOrderPromotion" order_id: Scalars["String"]["output"] promotion_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkReturnFulfillment = { - __typename?: "LinkReturnFulfillment" + __typename: "LinkReturnFulfillment" return_id: Scalars["String"]["output"] fulfillment_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkProductSalesChannel = { - __typename?: "LinkProductSalesChannel" + __typename: "LinkProductSalesChannel" product_id: Scalars["String"]["output"] sales_channel_id: Scalars["String"]["output"] - product?: Maybe - sales_channel?: Maybe + product: Maybe + sales_channel: Maybe createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkProductVariantInventoryItem = { - __typename?: "LinkProductVariantInventoryItem" + __typename: "LinkProductVariantInventoryItem" variant_id: Scalars["String"]["output"] inventory_item_id: Scalars["String"]["output"] required_quantity: Scalars["Int"]["output"] - variant?: Maybe + variant: Maybe createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkProductVariantPriceSet = { - __typename?: "LinkProductVariantPriceSet" + __typename: "LinkProductVariantPriceSet" variant_id: Scalars["String"]["output"] price_set_id: Scalars["String"]["output"] - variant?: Maybe + variant: Maybe createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkPublishableApiKeySalesChannel = { - __typename?: "LinkPublishableApiKeySalesChannel" + __typename: "LinkPublishableApiKeySalesChannel" publishable_key_id: Scalars["String"]["output"] sales_channel_id: Scalars["String"]["output"] - sales_channel?: Maybe + sales_channel: Maybe createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkRegionPaymentProvider = { - __typename?: "LinkRegionPaymentProvider" + __typename: "LinkRegionPaymentProvider" region_id: Scalars["String"]["output"] payment_provider_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkSalesChannelStockLocation = { - __typename?: "LinkSalesChannelStockLocation" + __typename: "LinkSalesChannelStockLocation" sales_channel_id: Scalars["String"]["output"] stock_location_id: Scalars["String"]["output"] - sales_channel?: Maybe + sales_channel: Maybe createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkShippingOptionPriceSet = { - __typename?: "LinkShippingOptionPriceSet" + __typename: "LinkShippingOptionPriceSet" shipping_option_id: Scalars["String"]["output"] price_set_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export interface FixtureEntryPoints { diff --git a/packages/core/types/src/index-data/__fixtures__/index-service-entry-points.ts b/packages/core/types/src/index-data/__fixtures__/index-service-entry-points.ts index 42aec29043a3c..d4e469b903ab2 100644 --- a/packages/core/types/src/index-data/__fixtures__/index-service-entry-points.ts +++ b/packages/core/types/src/index-data/__fixtures__/index-service-entry-points.ts @@ -28,23 +28,23 @@ export type Scalars = { } export type Product = { - __typename?: "Product" - id?: Maybe - title?: Maybe - variants?: Maybe>> + __typename: "Product" + id: Maybe + title: Maybe + variants: Maybe>> } export type ProductVariant = { - __typename?: "ProductVariant" - id?: Maybe - product_id?: Maybe - sku?: Maybe - prices?: Maybe>> + __typename: "ProductVariant" + id: Maybe + product_id: Maybe + sku: Maybe + prices: Maybe>> } export type Price = { - __typename?: "Price" - amount?: Maybe + __typename: "Price" + amount: Maybe } export interface FixtureEntryPoints { diff --git a/packages/core/types/src/modules-sdk/__fixtures__/remote-query.ts b/packages/core/types/src/modules-sdk/__fixtures__/remote-query.ts index 5169ee35fb4c3..8902a41d83c7f 100644 --- a/packages/core/types/src/modules-sdk/__fixtures__/remote-query.ts +++ b/packages/core/types/src/modules-sdk/__fixtures__/remote-query.ts @@ -31,207 +31,207 @@ export type Scalars = { } export type SimpleProduct = { - __typename?: "SimpleProduct" + __typename: "SimpleProduct" id: Scalars["ID"]["output"] handle: string - title?: Scalars["String"]["output"] - variants?: Maybe>>> - sales_channels_link?: Array< + title: Scalars["String"]["output"] + variants: Maybe>>> + sales_channels_link: Array< Pick< LinkProductSalesChannel, "product_id" | "sales_channel_id" | "__typename" > > - sales_channels?: Array> + sales_channels: Array> } export type Product = { - __typename?: "Product" + __typename: "Product" id: Scalars["ID"]["output"] handle: Scalars["String"]["output"] title: Scalars["String"]["output"] - description?: Scalars["String"]["output"] - variants?: Array - sales_channels_link?: Array - sales_channels?: Array + description: Scalars["String"]["output"] + variants: Array + sales_channels_link: Array + sales_channels: Array } export type ProductVariant = { - __typename?: "ProductVariant" + __typename: "ProductVariant" id: Scalars["ID"]["output"] handle: Scalars["String"]["output"] title: Scalars["String"]["output"] - product?: Maybe + product: Maybe } export type ProductCategory = { - __typename?: "ProductCategory" + __typename: "ProductCategory" id: Scalars["ID"]["output"] handle: Scalars["String"]["output"] - title?: Maybe + title: Maybe } export type SalesChannel = { - __typename?: "SalesChannel" + __typename: "SalesChannel" id: Scalars["ID"]["output"] - name?: Maybe - description?: Maybe - created_at?: Maybe - updated_at?: Maybe - products_link?: Maybe>> - api_keys_link?: Maybe>> - locations_link?: Maybe>> + name: Maybe + description: Maybe + created_at: Maybe + updated_at: Maybe + products_link: Maybe>> + api_keys_link: Maybe>> + locations_link: Maybe>> } export type LinkCartPaymentCollection = { - __typename?: "LinkCartPaymentCollection" + __typename: "LinkCartPaymentCollection" cart_id: Scalars["String"]["output"] payment_collection_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkCartPromotion = { - __typename?: "LinkCartPromotion" + __typename: "LinkCartPromotion" cart_id: Scalars["String"]["output"] promotion_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkLocationFulfillmentProvider = { - __typename?: "LinkLocationFulfillmentProvider" + __typename: "LinkLocationFulfillmentProvider" stock_location_id: Scalars["String"]["output"] fulfillment_provider_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkLocationFulfillmentSet = { - __typename?: "LinkLocationFulfillmentSet" + __typename: "LinkLocationFulfillmentSet" stock_location_id: Scalars["String"]["output"] fulfillment_set_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkOrderCart = { - __typename?: "LinkOrderCart" + __typename: "LinkOrderCart" order_id: Scalars["String"]["output"] cart_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkOrderFulfillment = { - __typename?: "LinkOrderFulfillment" + __typename: "LinkOrderFulfillment" order_id: Scalars["String"]["output"] fulfillment_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkOrderPaymentCollection = { - __typename?: "LinkOrderPaymentCollection" + __typename: "LinkOrderPaymentCollection" order_id: Scalars["String"]["output"] payment_collection_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkOrderPromotion = { - __typename?: "LinkOrderPromotion" + __typename: "LinkOrderPromotion" order_id: Scalars["String"]["output"] promotion_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkReturnFulfillment = { - __typename?: "LinkReturnFulfillment" + __typename: "LinkReturnFulfillment" return_id: Scalars["String"]["output"] fulfillment_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkProductSalesChannel = { - __typename?: "LinkProductSalesChannel" + __typename: "LinkProductSalesChannel" product_id: Scalars["String"]["output"] sales_channel_id: Scalars["String"]["output"] - product?: Maybe - sales_channel?: Maybe + product: Maybe + sales_channel: Maybe createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkProductVariantInventoryItem = { - __typename?: "LinkProductVariantInventoryItem" + __typename: "LinkProductVariantInventoryItem" variant_id: Scalars["String"]["output"] inventory_item_id: Scalars["String"]["output"] required_quantity: Scalars["Int"]["output"] - variant?: Maybe + variant: Maybe createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkProductVariantPriceSet = { - __typename?: "LinkProductVariantPriceSet" + __typename: "LinkProductVariantPriceSet" variant_id: Scalars["String"]["output"] price_set_id: Scalars["String"]["output"] - variant?: Maybe + variant: Maybe createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkPublishableApiKeySalesChannel = { - __typename?: "LinkPublishableApiKeySalesChannel" + __typename: "LinkPublishableApiKeySalesChannel" publishable_key_id: Scalars["String"]["output"] sales_channel_id: Scalars["String"]["output"] - sales_channel?: Maybe + sales_channel: Maybe createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkRegionPaymentProvider = { - __typename?: "LinkRegionPaymentProvider" + __typename: "LinkRegionPaymentProvider" region_id: Scalars["String"]["output"] payment_provider_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkSalesChannelStockLocation = { - __typename?: "LinkSalesChannelStockLocation" + __typename: "LinkSalesChannelStockLocation" sales_channel_id: Scalars["String"]["output"] stock_location_id: Scalars["String"]["output"] - sales_channel?: Maybe + sales_channel: Maybe createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export type LinkShippingOptionPriceSet = { - __typename?: "LinkShippingOptionPriceSet" + __typename: "LinkShippingOptionPriceSet" shipping_option_id: Scalars["String"]["output"] price_set_id: Scalars["String"]["output"] createdAt: Scalars["String"]["output"] updatedAt: Scalars["String"]["output"] - deletedAt?: Maybe + deletedAt: Maybe } export interface FixtureEntryPoints { diff --git a/packages/modules/providers/locking-postgres/src/models/locking.ts b/packages/modules/providers/locking-postgres/src/models/locking.ts index 8b9558befbcb6..8868dec65327c 100644 --- a/packages/modules/providers/locking-postgres/src/models/locking.ts +++ b/packages/modules/providers/locking-postgres/src/models/locking.ts @@ -1,32 +1,9 @@ -import { generateEntityId } from "@medusajs/framework/utils" -import { - BeforeCreate, - Entity, - OnInit, - PrimaryKey, - Property, -} from "@mikro-orm/core" +import { model } from "@medusajs/framework/utils" -@Entity({ tableName: "locking" }) -class Locking { - @PrimaryKey({ columnType: "text" }) - id!: string - - @Property({ columnType: "text", nullable: true }) - owner_id: string | null = null - - @Property({ columnType: "timestamptz", nullable: true }) - expiration: Date | null = null - - @BeforeCreate() - onCreate() { - this.id = generateEntityId(this.id, "lk") - } - - @OnInit() - onInit() { - this.id = generateEntityId(this.id, "lk") - } -} +const Locking = model.define("Locking", { + id: model.id({ prefix: "lk" }).primaryKey(), + owner_id: model.text().nullable(), + expiration: model.dateTime().nullable(), +}) export default Locking From 0a077d48e14976bafcf19705fc48e66756362fd6 Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Fri, 6 Dec 2024 14:23:07 +0100 Subject: [PATCH 9/9] chore(workflow-engine): Migrate to DML (#10477) RESOLVES FRMW-2832 RESOLVES FRMW-2833 **What** Migrate workflow engines to DML. Alos includes and update to the linkable generation which now takes into account id and primary keys to generate the linkable instead of only primary keys --- .changeset/clean-paws-build.md | 7 + .../__tests__/joiner-config-builder.spec.ts | 4 +- .../src/modules-sdk/joiner-config-builder.ts | 7 +- .../integration-tests/__tests__/index.spec.ts | 115 +++++++----- .../.snapshot-medusa-workflows.json | 171 ++++++++++++++++++ .../src/migrations/Migration20241206101446.ts | 27 +++ .../src/models/index.ts | 2 +- .../src/models/workflow-execution.ts | 102 +++-------- .../src/services/workflows-module.ts | 7 +- .../integration-tests/__tests__/index.spec.ts | 123 +++++++------ .../integration-tests/utils/database.ts | 4 +- .../.snapshot-medusa-workflows.json | 163 +++++++++++++++++ .../src/migrations/Migration20241206123341.ts | 27 +++ .../workflow-engine-redis/src/models/index.ts | 2 +- .../src/models/workflow-execution.ts | 102 +++-------- .../src/services/workflows-module.ts | 7 +- 16 files changed, 610 insertions(+), 260 deletions(-) create mode 100644 .changeset/clean-paws-build.md create mode 100644 packages/modules/workflow-engine-inmemory/src/migrations/.snapshot-medusa-workflows.json create mode 100644 packages/modules/workflow-engine-inmemory/src/migrations/Migration20241206101446.ts create mode 100644 packages/modules/workflow-engine-redis/src/migrations/.snapshot-medusa-workflows.json create mode 100644 packages/modules/workflow-engine-redis/src/migrations/Migration20241206123341.ts diff --git a/.changeset/clean-paws-build.md b/.changeset/clean-paws-build.md new file mode 100644 index 0000000000000..95b3498134ba7 --- /dev/null +++ b/.changeset/clean-paws-build.md @@ -0,0 +1,7 @@ +--- +"@medusajs/workflow-engine-inmemory": patch +"@medusajs/workflow-engine-redis": patch +"@medusajs/utils": patch +--- + +chore(workflow-engine): Migrate to DML diff --git a/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts b/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts index e7e93dfc5d64a..dcdfe6c00efb7 100644 --- a/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts +++ b/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts @@ -668,8 +668,8 @@ describe("joiner-config-builder", () => { serviceName: "myService", field: "car", entity: "Car", - linkable: "car_number_plate", - primaryKey: "number_plate", + linkable: "car_id", + primaryKey: "id", }) expect(linkConfig.user.toJSON()).toEqual({ serviceName: "myService", diff --git a/packages/core/utils/src/modules-sdk/joiner-config-builder.ts b/packages/core/utils/src/modules-sdk/joiner-config-builder.ts index 8c5809b6fbbba..b31278a88b5ae 100644 --- a/packages/core/utils/src/modules-sdk/joiner-config-builder.ts +++ b/packages/core/utils/src/modules-sdk/joiner-config-builder.ts @@ -17,7 +17,7 @@ import { toCamelCase, upperCaseFirst, } from "../common" -import { DmlEntity } from "../dml" +import { DmlEntity, IdProperty } from "../dml" import { toGraphQLSchema } from "../dml/helpers/create-graphql" import { PrimaryKeyModifier } from "../dml/properties/primary-key" import { BaseRelationship } from "../dml/relations/base" @@ -396,7 +396,10 @@ export function buildLinkConfigFromModelObjects< } const parsedProperty = (value as PropertyType).parse(property) - if (PrimaryKeyModifier.isPrimaryKeyModifier(value)) { + if ( + PrimaryKeyModifier.isPrimaryKeyModifier(value) || + IdProperty.isIdProperty(value) + ) { const linkableKeyName = parsedProperty.dataType.options?.linkable ?? `${camelToSnakeCase(model.name).toLowerCase()}_${property}` diff --git a/packages/modules/workflow-engine-inmemory/integration-tests/__tests__/index.spec.ts b/packages/modules/workflow-engine-inmemory/integration-tests/__tests__/index.spec.ts index e499e9381b37f..765d64facacbe 100644 --- a/packages/modules/workflow-engine-inmemory/integration-tests/__tests__/index.spec.ts +++ b/packages/modules/workflow-engine-inmemory/integration-tests/__tests__/index.spec.ts @@ -1,4 +1,7 @@ -import { WorkflowManager } from "@medusajs/framework/orchestration" +import { + DistributedTransactionType, + WorkflowManager, +} from "@medusajs/framework/orchestration" import { Context, IWorkflowEngineService, @@ -60,6 +63,20 @@ moduleIntegrationTestRunner({ serviceName: "workflows", field: "workflowExecution", }, + transaction_id: { + linkable: "workflow_execution_transaction_id", + entity: "WorkflowExecution", + primaryKey: "transaction_id", + serviceName: "workflows", + field: "workflowExecution", + }, + workflow_id: { + linkable: "workflow_execution_workflow_id", + entity: "WorkflowExecution", + primaryKey: "workflow_id", + serviceName: "workflows", + field: "workflowExecution", + }, }, }) }) @@ -87,12 +104,12 @@ moduleIntegrationTestRunner({ }) // Validate context event group id - expect(workflowEventGroupIdStep1Mock.mock.calls[0][1]).toEqual( - expect.objectContaining({ eventGroupId }) - ) - expect(workflowEventGroupIdStep2Mock.mock.calls[0][1]).toEqual( - expect.objectContaining({ eventGroupId }) - ) + expect( + (workflowEventGroupIdStep1Mock.mock.calls[0] as any[])[1] + ).toEqual(expect.objectContaining({ eventGroupId })) + expect( + (workflowEventGroupIdStep2Mock.mock.calls[0] as any[])[1] + ).toEqual(expect.objectContaining({ eventGroupId })) }) it("should execute an async workflow keeping track of the event group id that has been auto generated", async () => { @@ -114,14 +131,19 @@ moduleIntegrationTestRunner({ stepResponse: { hey: "oh" }, }) - const generatedEventGroupId = (workflowEventGroupIdStep1Mock.mock - .calls[0][1] as unknown as Context)!.eventGroupId + const generatedEventGroupId = (( + workflowEventGroupIdStep1Mock.mock.calls[0] as any[] + )[1] as unknown as Context)!.eventGroupId // Validate context event group id - expect(workflowEventGroupIdStep1Mock.mock.calls[0][1]).toEqual( + expect( + (workflowEventGroupIdStep1Mock.mock.calls[0] as any[])[1] + ).toEqual( expect.objectContaining({ eventGroupId: generatedEventGroupId }) ) - expect(workflowEventGroupIdStep2Mock.mock.calls[0][1]).toEqual( + expect( + (workflowEventGroupIdStep2Mock.mock.calls[0] as any[])[1] + ).toEqual( expect.objectContaining({ eventGroupId: generatedEventGroupId }) ) }) @@ -139,10 +161,9 @@ moduleIntegrationTestRunner({ throwOnError: true, }) - let executionsList = await query({ - workflow_executions: { - fields: ["workflow_id", "transaction_id", "state"], - }, + let { data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["workflow_id", "transaction_id", "state"], }) expect(executionsList).toHaveLength(1) @@ -157,11 +178,10 @@ moduleIntegrationTestRunner({ stepResponse: { uhuuuu: "yeaah!" }, }) - executionsList = await query({ - workflow_executions: { - fields: ["id"], - }, - }) + ;({ data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["id"], + })) expect(executionsList).toHaveLength(0) expect(result).toEqual({ @@ -180,10 +200,9 @@ moduleIntegrationTestRunner({ transactionId: "transaction_1", }) - let executionsList = await query({ - workflow_executions: { - fields: ["id"], - }, + let { data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["id"], }) expect(executionsList).toHaveLength(1) @@ -208,40 +227,38 @@ moduleIntegrationTestRunner({ expect(workflow2Step3Invoke.mock.calls[0][0]).toEqual({ uhuuuu: "yeaah!", }) - - executionsList = await query({ - workflow_executions: { - fields: ["id"], - }, - }) + ;({ data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["id"], + })) expect(executionsList).toHaveLength(1) }) it("should revert the entire transaction when a step timeout expires", async () => { - const { transaction } = await workflowOrcModule.run( + const { transaction } = (await workflowOrcModule.run( "workflow_step_timeout", { input: {}, throwOnError: false, } - ) + )) as Awaited<{ transaction: DistributedTransactionType }> - expect(transaction.flow.state).toEqual("reverted") + expect(transaction.getFlow().state).toEqual("reverted") }) it("should revert the entire transaction when the transaction timeout expires", async () => { - const { transaction } = await workflowOrcModule.run( + const { transaction } = (await workflowOrcModule.run( "workflow_transaction_timeout", { input: {}, throwOnError: false, } - ) + )) as Awaited<{ transaction: DistributedTransactionType }> await setTimeoutPromise(200) - expect(transaction.flow.state).toEqual("reverted") + expect(transaction.getFlow().state).toEqual("reverted") }) it.skip("should subscribe to a async workflow and receive the response when it finishes", (done) => { @@ -393,7 +410,7 @@ moduleIntegrationTestRunner({ }) it("should fetch an idempotent workflow after its completion", async () => { - const { transaction: firstRun } = await workflowOrcModule.run( + const { transaction: firstRun } = (await workflowOrcModule.run( "workflow_idempotent", { input: { @@ -402,15 +419,14 @@ moduleIntegrationTestRunner({ throwOnError: true, transactionId: "transaction_1", } - ) + )) as Awaited<{ transaction: DistributedTransactionType }> - let executionsList = await query({ - workflow_executions: { - fields: ["id"], - }, + let { data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["id"], }) - const { transaction: secondRun } = await workflowOrcModule.run( + const { transaction: secondRun } = (await workflowOrcModule.run( "workflow_idempotent", { input: { @@ -419,15 +435,16 @@ moduleIntegrationTestRunner({ throwOnError: true, transactionId: "transaction_1", } - ) + )) as Awaited<{ transaction: DistributedTransactionType }> - const executionsListAfter = await query({ - workflow_executions: { - fields: ["id"], - }, + const { data: executionsListAfter } = await query.graph({ + entity: "workflow_executions", + fields: ["id"], }) - expect(secondRun.flow.startedAt).toEqual(firstRun.flow.startedAt) + expect(secondRun.getFlow().startedAt).toEqual( + firstRun.getFlow().startedAt + ) expect(executionsList).toHaveLength(1) expect(executionsListAfter).toHaveLength(1) }) diff --git a/packages/modules/workflow-engine-inmemory/src/migrations/.snapshot-medusa-workflows.json b/packages/modules/workflow-engine-inmemory/src/migrations/.snapshot-medusa-workflows.json new file mode 100644 index 0000000000000..0c8c584c91589 --- /dev/null +++ b/packages/modules/workflow-engine-inmemory/src/migrations/.snapshot-medusa-workflows.json @@ -0,0 +1,171 @@ +{ + "namespaces": [ + "public" + ], + "name": "public", + "tables": [ + { + "columns": { + "workflow_id": { + "name": "workflow_id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "transaction_id": { + "name": "transaction_id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "id": { + "name": "id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "execution": { + "name": "execution", + "type": "jsonb", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "json" + }, + "context": { + "name": "context", + "type": "jsonb", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "json" + }, + "state": { + "name": "state", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "enumItems": [ + "not_started", + "invoking", + "waiting_to_compensate", + "compensating", + "done", + "reverted", + "failed" + ], + "mappedType": "enum" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "default": "now()", + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "default": "now()", + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + } + }, + "name": "workflow_execution", + "schema": "public", + "indexes": [ + { + "keyName": "IDX_workflow_execution_deleted_at", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_workflow_execution_deleted_at\" ON \"workflow_execution\" (deleted_at) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_workflow_execution_id", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_workflow_execution_id\" ON \"workflow_execution\" (id) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_workflow_execution_workflow_id_transaction_id_unique", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_workflow_execution_workflow_id_transaction_id_unique\" ON \"workflow_execution\" (workflow_id, transaction_id) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_workflow_execution_workflow_id", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_workflow_execution_workflow_id\" ON \"workflow_execution\" (workflow_id) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_workflow_execution_transaction_id", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_workflow_execution_transaction_id\" ON \"workflow_execution\" (transaction_id) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_workflow_execution_state", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_workflow_execution_state\" ON \"workflow_execution\" (state) WHERE deleted_at IS NULL" + }, + { + "keyName": "workflow_execution_pkey", + "columnNames": [ + "workflow_id", + "transaction_id" + ], + "composite": true, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": {} + } + ] +} diff --git a/packages/modules/workflow-engine-inmemory/src/migrations/Migration20241206101446.ts b/packages/modules/workflow-engine-inmemory/src/migrations/Migration20241206101446.ts new file mode 100644 index 0000000000000..be8b1cbac182a --- /dev/null +++ b/packages/modules/workflow-engine-inmemory/src/migrations/Migration20241206101446.ts @@ -0,0 +1,27 @@ +import { Migration } from "@mikro-orm/migrations" + +export class Migration20241206101446 extends Migration { + async up(): Promise { + this.addSql( + `DROP INDEX IF EXISTS "IDX_workflow_execution_id"; + DROP INDEX IF EXISTS "IDX_workflow_execution_workflow_id"; + DROP INDEX IF EXISTS "IDX_workflow_execution_transaction_id"; + DROP INDEX IF EXISTS "IDX_workflow_execution_state";` + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_workflow_execution_deleted_at" ON "workflow_execution" (deleted_at) WHERE deleted_at IS NULL;' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_workflow_execution_id" ON "workflow_execution" (id) WHERE deleted_at IS NULL;' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_workflow_execution_workflow_id" ON "workflow_execution" (workflow_id) WHERE deleted_at IS NULL;' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_workflow_execution_transaction_id" ON "workflow_execution" (transaction_id) WHERE deleted_at IS NULL;' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_workflow_execution_state" ON "workflow_execution" (state) WHERE deleted_at IS NULL;' + ) + } +} diff --git a/packages/modules/workflow-engine-inmemory/src/models/index.ts b/packages/modules/workflow-engine-inmemory/src/models/index.ts index 78fcbfa9214f9..fa5b8a3dd019e 100644 --- a/packages/modules/workflow-engine-inmemory/src/models/index.ts +++ b/packages/modules/workflow-engine-inmemory/src/models/index.ts @@ -1 +1 @@ -export { default as WorkflowExecution } from "./workflow-execution" +export { WorkflowExecution } from "./workflow-execution" diff --git a/packages/modules/workflow-engine-inmemory/src/models/workflow-execution.ts b/packages/modules/workflow-engine-inmemory/src/models/workflow-execution.ts index 22e693d4283eb..c41bc8936ed1b 100644 --- a/packages/modules/workflow-engine-inmemory/src/models/workflow-execution.ts +++ b/packages/modules/workflow-engine-inmemory/src/models/workflow-execution.ts @@ -1,76 +1,30 @@ import { TransactionState } from "@medusajs/framework/orchestration" -import { DALUtils, generateEntityId } from "@medusajs/framework/utils" -import { - BeforeCreate, - Entity, - Enum, - Filter, - Index, - OnInit, - OptionalProps, - PrimaryKey, - Property, - Unique, -} from "@mikro-orm/core" - -type OptionalFields = "deleted_at" - -@Entity() -@Unique({ - name: "IDX_workflow_execution_workflow_id_transaction_id_unique", - properties: ["workflow_id", "transaction_id"], -}) -@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) -export default class WorkflowExecution { - [OptionalProps]?: OptionalFields - - @Property({ columnType: "text", nullable: false }) - @Index({ name: "IDX_workflow_execution_id" }) - id!: string - - @Index({ name: "IDX_workflow_execution_workflow_id" }) - @PrimaryKey({ columnType: "text" }) - workflow_id: string - - @Index({ name: "IDX_workflow_execution_transaction_id" }) - @PrimaryKey({ columnType: "text" }) - transaction_id: string - - @Property({ columnType: "jsonb", nullable: true }) - execution: Record | null = null - - @Property({ columnType: "jsonb", nullable: true }) - context: Record | null = null - - @Index({ name: "IDX_workflow_execution_state" }) - @Enum(() => TransactionState) - state: TransactionState - - @Property({ - onCreate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", - }) - created_at: Date - - @Property({ - onCreate: () => new Date(), - onUpdate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", +import { model } from "@medusajs/framework/utils" + +export const WorkflowExecution = model + .define("workflow_execution", { + id: model.id({ prefix: "wf_exec" }), + workflow_id: model.text().primaryKey(), + transaction_id: model.text().primaryKey(), + execution: model.json().nullable(), + context: model.json().nullable(), + state: model.enum(TransactionState), }) - updated_at: Date - - @Property({ columnType: "timestamptz", nullable: true }) - deleted_at: Date | null = null - - @BeforeCreate() - onCreate() { - this.id = generateEntityId(this.id, "wf_exec") - } - - @OnInit() - onInit() { - this.id = generateEntityId(this.id, "wf_exec") - } -} + .indexes([ + { + on: ["id"], + where: "deleted_at IS NULL", + }, + { + on: ["workflow_id"], + where: "deleted_at IS NULL", + }, + { + on: ["transaction_id"], + where: "deleted_at IS NULL", + }, + { + on: ["state"], + where: "deleted_at IS NULL", + }, + ]) diff --git a/packages/modules/workflow-engine-inmemory/src/services/workflows-module.ts b/packages/modules/workflow-engine-inmemory/src/services/workflows-module.ts index 9771f48784aac..3b67e36d2a5c8 100644 --- a/packages/modules/workflow-engine-inmemory/src/services/workflows-module.ts +++ b/packages/modules/workflow-engine-inmemory/src/services/workflows-module.ts @@ -1,6 +1,7 @@ import { Context, DAL, + InferEntityType, InternalModuleDeclaration, MedusaContainer, ModulesSdkTypes, @@ -25,9 +26,11 @@ type InjectedDependencies = { } export class WorkflowsModuleService< - TWorkflowExecution extends WorkflowExecution = WorkflowExecution + TWorkflowExecution extends InferEntityType< + typeof WorkflowExecution + > = InferEntityType > extends ModulesSdkUtils.MedusaService<{ - WorkflowExecution: { dto: WorkflowExecution } + WorkflowExecution: { dto: InferEntityType } }>({ WorkflowExecution }) { protected baseRepository_: DAL.RepositoryService protected workflowExecutionService_: ModulesSdkTypes.IMedusaInternalService diff --git a/packages/modules/workflow-engine-redis/integration-tests/__tests__/index.spec.ts b/packages/modules/workflow-engine-redis/integration-tests/__tests__/index.spec.ts index 1e30157a24216..00f60a894afb9 100644 --- a/packages/modules/workflow-engine-redis/integration-tests/__tests__/index.spec.ts +++ b/packages/modules/workflow-engine-redis/integration-tests/__tests__/index.spec.ts @@ -1,10 +1,12 @@ import { + DistributedTransactionType, TransactionStepTimeoutError, TransactionTimeoutError, WorkflowManager, } from "@medusajs/framework/orchestration" import { IWorkflowEngineService, + Logger, MedusaContainer, RemoteQueryFunction, } from "@medusajs/framework/types" @@ -99,6 +101,20 @@ moduleIntegrationTestRunner({ serviceName: "workflows", field: "workflowExecution", }, + transaction_id: { + entity: "WorkflowExecution", + field: "workflowExecution", + linkable: "workflow_execution_transaction_id", + primaryKey: "transaction_id", + serviceName: "workflows", + }, + workflow_id: { + entity: "WorkflowExecution", + field: "workflowExecution", + linkable: "workflow_execution_workflow_id", + primaryKey: "workflow_id", + serviceName: "workflows", + }, }, }) }) @@ -112,10 +128,9 @@ moduleIntegrationTestRunner({ throwOnError: true, }) - let executionsList = await query({ - workflow_executions: { - fields: ["workflow_id", "transaction_id", "state"], - }, + let { data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["workflow_id", "transaction_id", "state"], }) expect(executionsList).toHaveLength(1) @@ -130,11 +145,10 @@ moduleIntegrationTestRunner({ stepResponse: { uhuuuu: "yeaah!" }, }) - executionsList = await query({ - workflow_executions: { - fields: ["id"], - }, - }) + ;({ data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["id"], + })) expect(executionsList).toHaveLength(0) expect(result).toEqual({ @@ -153,10 +167,9 @@ moduleIntegrationTestRunner({ transactionId: "transaction_1", }) - let executionsList = await query({ - workflow_executions: { - fields: ["id"], - }, + let { data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["id"], }) expect(executionsList).toHaveLength(1) @@ -170,12 +183,10 @@ moduleIntegrationTestRunner({ }, stepResponse: { uhuuuu: "yeaah!" }, }) - - executionsList = await query({ - workflow_executions: { - fields: ["id"], - }, - }) + ;({ data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["id"], + })) expect(executionsList).toHaveLength(1) }) @@ -188,10 +199,9 @@ moduleIntegrationTestRunner({ transactionId: "transaction_1", }) - let executionsList = await query({ - workflow_executions: { - fields: ["id"], - }, + let { data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["id"], }) expect(executionsList).toHaveLength(1) @@ -205,12 +215,10 @@ moduleIntegrationTestRunner({ }, stepResponse: { uhuuuu: "yeaah!" }, }) - - executionsList = await query({ - workflow_executions: { - fields: ["id", "state"], - }, - }) + ;({ data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["id", "state"], + })) expect(executionsList).toHaveLength(1) expect(executionsList[0].state).toEqual("reverted") @@ -237,10 +245,9 @@ moduleIntegrationTestRunner({ }, }) - let executionsList = await query({ - workflow_executions: { - fields: ["id"], - }, + let { data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["id"], }) expect(executionsList).toHaveLength(1) @@ -260,12 +267,10 @@ moduleIntegrationTestRunner({ }) expect(setStepError).toEqual({ uhuuuu: "yeaah!" }) - - executionsList = await query({ - workflow_executions: { - fields: ["id", "state", "context"], - }, - }) + ;({ data: executionsList } = await query.graph({ + entity: "workflow_executions", + fields: ["id", "state", "context"], + })) expect(executionsList).toHaveLength(1) expect(executionsList[0].state).toEqual("failed") @@ -273,7 +278,7 @@ moduleIntegrationTestRunner({ }) it("should revert the entire transaction when a step timeout expires", async () => { - const { transaction, result, errors } = await workflowOrcModule.run( + const { transaction, result, errors } = (await workflowOrcModule.run( "workflow_step_timeout", { input: { @@ -282,9 +287,13 @@ moduleIntegrationTestRunner({ throwOnError: false, logOnError: true, } - ) + )) as Awaited<{ + transaction: DistributedTransactionType + result: any + errors: any + }> - expect(transaction.flow.state).toEqual("reverted") + expect(transaction.getFlow().state).toEqual("reverted") expect(result).toEqual({ myInput: "123", }) @@ -294,16 +303,20 @@ moduleIntegrationTestRunner({ }) it("should revert the entire transaction when the transaction timeout expires", async () => { - const { transaction, result, errors } = await workflowOrcModule.run( + const { transaction, result, errors } = (await workflowOrcModule.run( "workflow_transaction_timeout", { input: {}, transactionId: "trx", throwOnError: false, } - ) + )) as Awaited<{ + transaction: DistributedTransactionType + result: any + errors: any + }> - expect(transaction.flow.state).toEqual("reverted") + expect(transaction.getFlow().state).toEqual("reverted") expect(result).toEqual({ executed: true }) expect(errors).toHaveLength(1) expect(errors[0].action).toEqual("step_1") @@ -323,7 +336,7 @@ moduleIntegrationTestRunner({ await setTimeout(200) - const { transaction, result, errors } = await workflowOrcModule.run( + const { transaction, result, errors } = (await workflowOrcModule.run( "workflow_step_timeout_async", { input: { @@ -332,9 +345,13 @@ moduleIntegrationTestRunner({ transactionId: "transaction_1", throwOnError: false, } - ) + )) as Awaited<{ + transaction: DistributedTransactionType + result: any + errors: any + }> - expect(transaction.flow.state).toEqual("reverted") + expect(transaction.getFlow().state).toEqual("reverted") expect(result).toEqual(undefined) expect(errors).toHaveLength(1) expect(errors[0].action).toEqual("step_1_async") @@ -354,16 +371,20 @@ moduleIntegrationTestRunner({ await setTimeout(200) - const { transaction, result, errors } = await workflowOrcModule.run( + const { transaction, result, errors } = (await workflowOrcModule.run( "workflow_transaction_timeout_async", { input: {}, transactionId: "transaction_1", throwOnError: false, } - ) + )) as Awaited<{ + transaction: DistributedTransactionType + result: any + errors: any + }> - expect(transaction.flow.state).toEqual("reverted") + expect(transaction.getFlow().state).toEqual("reverted") expect(result).toEqual(undefined) expect(errors).toHaveLength(1) expect(errors[0].action).toEqual("step_1") diff --git a/packages/modules/workflow-engine-redis/integration-tests/utils/database.ts b/packages/modules/workflow-engine-redis/integration-tests/utils/database.ts index 3fe2da068123a..7b027f5995841 100644 --- a/packages/modules/workflow-engine-redis/integration-tests/utils/database.ts +++ b/packages/modules/workflow-engine-redis/integration-tests/utils/database.ts @@ -15,11 +15,11 @@ const redisUrl = process.env.REDIS_URL || "redis://localhost:6379" const redis = new Redis(redisUrl) interface TestDatabase { - clearTables(knex): Promise + clearTables(): Promise } export const TestDatabase: TestDatabase = { - clearTables: async (knex) => { + clearTables: async () => { await cleanRedis() }, } diff --git a/packages/modules/workflow-engine-redis/src/migrations/.snapshot-medusa-workflows.json b/packages/modules/workflow-engine-redis/src/migrations/.snapshot-medusa-workflows.json new file mode 100644 index 0000000000000..66a9a97c216f0 --- /dev/null +++ b/packages/modules/workflow-engine-redis/src/migrations/.snapshot-medusa-workflows.json @@ -0,0 +1,163 @@ +{ + "namespaces": [ + "public" + ], + "name": "public", + "tables": [ + { + "columns": { + "workflow_id": { + "name": "workflow_id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "transaction_id": { + "name": "transaction_id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "id": { + "name": "id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "execution": { + "name": "execution", + "type": "jsonb", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "json" + }, + "context": { + "name": "context", + "type": "jsonb", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "json" + }, + "state": { + "name": "state", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "enumItems": [ + "not_started", + "invoking", + "waiting_to_compensate", + "compensating", + "done", + "reverted", + "failed" + ], + "mappedType": "enum" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "default": "now()", + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "default": "now()", + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + } + }, + "name": "workflow_execution", + "schema": "public", + "indexes": [ + { + "keyName": "IDX_workflow_execution_deleted_at", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_workflow_execution_deleted_at\" ON \"workflow_execution\" (deleted_at) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_workflow_execution_id", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_workflow_execution_id\" ON \"workflow_execution\" (id) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_workflow_execution_workflow_id", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_workflow_execution_workflow_id\" ON \"workflow_execution\" (workflow_id) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_workflow_execution_transaction_id", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_workflow_execution_transaction_id\" ON \"workflow_execution\" (transaction_id) WHERE deleted_at IS NULL" + }, + { + "keyName": "IDX_workflow_execution_state", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_workflow_execution_state\" ON \"workflow_execution\" (state) WHERE deleted_at IS NULL" + }, + { + "keyName": "workflow_execution_pkey", + "columnNames": [ + "workflow_id", + "transaction_id" + ], + "composite": true, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": {} + } + ] +} diff --git a/packages/modules/workflow-engine-redis/src/migrations/Migration20241206123341.ts b/packages/modules/workflow-engine-redis/src/migrations/Migration20241206123341.ts new file mode 100644 index 0000000000000..50c0680ae90c5 --- /dev/null +++ b/packages/modules/workflow-engine-redis/src/migrations/Migration20241206123341.ts @@ -0,0 +1,27 @@ +import { Migration } from "@mikro-orm/migrations" + +export class Migration20241206123341 extends Migration { + async up(): Promise { + this.addSql( + `DROP INDEX IF EXISTS "IDX_workflow_execution_id"; + DROP INDEX IF EXISTS "IDX_workflow_execution_workflow_id"; + DROP INDEX IF EXISTS "IDX_workflow_execution_transaction_id"; + DROP INDEX IF EXISTS "IDX_workflow_execution_state";` + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_workflow_execution_deleted_at" ON "workflow_execution" (deleted_at) WHERE deleted_at IS NULL;' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_workflow_execution_id" ON "workflow_execution" (id) WHERE deleted_at IS NULL;' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_workflow_execution_workflow_id" ON "workflow_execution" (workflow_id) WHERE deleted_at IS NULL;' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_workflow_execution_transaction_id" ON "workflow_execution" (transaction_id) WHERE deleted_at IS NULL;' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_workflow_execution_state" ON "workflow_execution" (state) WHERE deleted_at IS NULL;' + ) + } +} diff --git a/packages/modules/workflow-engine-redis/src/models/index.ts b/packages/modules/workflow-engine-redis/src/models/index.ts index 78fcbfa9214f9..fa5b8a3dd019e 100644 --- a/packages/modules/workflow-engine-redis/src/models/index.ts +++ b/packages/modules/workflow-engine-redis/src/models/index.ts @@ -1 +1 @@ -export { default as WorkflowExecution } from "./workflow-execution" +export { WorkflowExecution } from "./workflow-execution" diff --git a/packages/modules/workflow-engine-redis/src/models/workflow-execution.ts b/packages/modules/workflow-engine-redis/src/models/workflow-execution.ts index 22e693d4283eb..c41bc8936ed1b 100644 --- a/packages/modules/workflow-engine-redis/src/models/workflow-execution.ts +++ b/packages/modules/workflow-engine-redis/src/models/workflow-execution.ts @@ -1,76 +1,30 @@ import { TransactionState } from "@medusajs/framework/orchestration" -import { DALUtils, generateEntityId } from "@medusajs/framework/utils" -import { - BeforeCreate, - Entity, - Enum, - Filter, - Index, - OnInit, - OptionalProps, - PrimaryKey, - Property, - Unique, -} from "@mikro-orm/core" - -type OptionalFields = "deleted_at" - -@Entity() -@Unique({ - name: "IDX_workflow_execution_workflow_id_transaction_id_unique", - properties: ["workflow_id", "transaction_id"], -}) -@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) -export default class WorkflowExecution { - [OptionalProps]?: OptionalFields - - @Property({ columnType: "text", nullable: false }) - @Index({ name: "IDX_workflow_execution_id" }) - id!: string - - @Index({ name: "IDX_workflow_execution_workflow_id" }) - @PrimaryKey({ columnType: "text" }) - workflow_id: string - - @Index({ name: "IDX_workflow_execution_transaction_id" }) - @PrimaryKey({ columnType: "text" }) - transaction_id: string - - @Property({ columnType: "jsonb", nullable: true }) - execution: Record | null = null - - @Property({ columnType: "jsonb", nullable: true }) - context: Record | null = null - - @Index({ name: "IDX_workflow_execution_state" }) - @Enum(() => TransactionState) - state: TransactionState - - @Property({ - onCreate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", - }) - created_at: Date - - @Property({ - onCreate: () => new Date(), - onUpdate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", +import { model } from "@medusajs/framework/utils" + +export const WorkflowExecution = model + .define("workflow_execution", { + id: model.id({ prefix: "wf_exec" }), + workflow_id: model.text().primaryKey(), + transaction_id: model.text().primaryKey(), + execution: model.json().nullable(), + context: model.json().nullable(), + state: model.enum(TransactionState), }) - updated_at: Date - - @Property({ columnType: "timestamptz", nullable: true }) - deleted_at: Date | null = null - - @BeforeCreate() - onCreate() { - this.id = generateEntityId(this.id, "wf_exec") - } - - @OnInit() - onInit() { - this.id = generateEntityId(this.id, "wf_exec") - } -} + .indexes([ + { + on: ["id"], + where: "deleted_at IS NULL", + }, + { + on: ["workflow_id"], + where: "deleted_at IS NULL", + }, + { + on: ["transaction_id"], + where: "deleted_at IS NULL", + }, + { + on: ["state"], + where: "deleted_at IS NULL", + }, + ]) diff --git a/packages/modules/workflow-engine-redis/src/services/workflows-module.ts b/packages/modules/workflow-engine-redis/src/services/workflows-module.ts index 37c371356ff59..7463623e94497 100644 --- a/packages/modules/workflow-engine-redis/src/services/workflows-module.ts +++ b/packages/modules/workflow-engine-redis/src/services/workflows-module.ts @@ -1,6 +1,7 @@ import { Context, DAL, + InferEntityType, InternalModuleDeclaration, ModulesSdkTypes, WorkflowsSdkTypes, @@ -25,9 +26,11 @@ type InjectedDependencies = { } export class WorkflowsModuleService< - TWorkflowExecution extends WorkflowExecution = WorkflowExecution + TWorkflowExecution extends InferEntityType< + typeof WorkflowExecution + > = InferEntityType > extends ModulesSdkUtils.MedusaService<{ - WorkflowExecution: { dto: WorkflowExecution } + WorkflowExecution: { dto: InferEntityType } }>({ WorkflowExecution }) { protected baseRepository_: DAL.RepositoryService protected workflowExecutionService_: ModulesSdkTypes.IMedusaInternalService