Merge branch 'develop' into docs/revise-recipe-examples
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
export interface S3FileServiceOptions {
|
export interface S3FileServiceOptions {
|
||||||
file_url: string
|
file_url: string
|
||||||
access_key_id: string
|
access_key_id?: string
|
||||||
secret_access_key: string
|
secret_access_key?: string
|
||||||
|
authentication_method?: "access-key" | "s3-iam-role"
|
||||||
region: string
|
region: string
|
||||||
bucket: string
|
bucket: string
|
||||||
prefix?: string
|
prefix?: string
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest --passWithNoTests src",
|
"test": "jest --passWithNoTests src",
|
||||||
"test:integration": "jest --forceExit -- integration-tests/**/__tests__/**/*.spec.ts",
|
"test:integration": "jest --forceExit -- integration-tests/__tests__/*.spec.ts",
|
||||||
"build": "rimraf dist && tsc --build ./tsconfig.json",
|
"build": "rimraf dist && tsc --build ./tsconfig.json",
|
||||||
"watch": "tsc --watch"
|
"watch": "tsc --watch"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ type InjectedDependencies = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface S3FileServiceConfig {
|
interface S3FileServiceConfig {
|
||||||
// TODO: We probably don't need this as either the service should return it or we should be able to calculate it.
|
|
||||||
fileUrl: string
|
fileUrl: string
|
||||||
accessKeyId: string
|
accessKeyId?: string
|
||||||
secretAccessKey: string
|
secretAccessKey?: string
|
||||||
|
authenticationMethod?: "access-key" | "s3-iam-role"
|
||||||
region: string
|
region: string
|
||||||
bucket: string
|
bucket: string
|
||||||
prefix?: string
|
prefix?: string
|
||||||
@@ -36,7 +36,6 @@ interface S3FileServiceConfig {
|
|||||||
additionalClientConfig?: Record<string, any>
|
additionalClientConfig?: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
// FUTURE: At one point we will probably need to support authenticating with IAM roles instead.
|
|
||||||
export class S3FileService extends AbstractFileProviderService {
|
export class S3FileService extends AbstractFileProviderService {
|
||||||
static identifier = "s3"
|
static identifier = "s3"
|
||||||
protected config_: S3FileServiceConfig
|
protected config_: S3FileServiceConfig
|
||||||
@@ -46,10 +45,23 @@ export class S3FileService extends AbstractFileProviderService {
|
|||||||
constructor({ logger }: InjectedDependencies, options: S3FileServiceOptions) {
|
constructor({ logger }: InjectedDependencies, options: S3FileServiceOptions) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
const authenticationMethod = options.authentication_method ?? "access-key"
|
||||||
|
|
||||||
|
if (
|
||||||
|
authenticationMethod === "access-key" &&
|
||||||
|
(!options.access_key_id || !options.secret_access_key)
|
||||||
|
) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Access key ID and secret access key are required when using access key authentication`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
this.config_ = {
|
this.config_ = {
|
||||||
fileUrl: options.file_url,
|
fileUrl: options.file_url,
|
||||||
accessKeyId: options.access_key_id,
|
accessKeyId: options.access_key_id,
|
||||||
secretAccessKey: options.secret_access_key,
|
secretAccessKey: options.secret_access_key,
|
||||||
|
authenticationMethod: authenticationMethod,
|
||||||
region: options.region,
|
region: options.region,
|
||||||
bucket: options.bucket,
|
bucket: options.bucket,
|
||||||
prefix: options.prefix ?? "",
|
prefix: options.prefix ?? "",
|
||||||
@@ -63,11 +75,17 @@ export class S3FileService extends AbstractFileProviderService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected getClient() {
|
protected getClient() {
|
||||||
|
// If none is provided, the SDK will use the default credentials provider chain, see https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-envvars.html
|
||||||
|
const credentials =
|
||||||
|
this.config_.authenticationMethod === "access-key"
|
||||||
|
? {
|
||||||
|
accessKeyId: this.config_.accessKeyId!,
|
||||||
|
secretAccessKey: this.config_.secretAccessKey!,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
|
||||||
const config: S3ClientConfigType = {
|
const config: S3ClientConfigType = {
|
||||||
credentials: {
|
credentials,
|
||||||
accessKeyId: this.config_.accessKeyId,
|
|
||||||
secretAccessKey: this.config_.secretAccessKey,
|
|
||||||
},
|
|
||||||
region: this.config_.region,
|
region: this.config_.region,
|
||||||
endpoint: this.config_.endpoint,
|
endpoint: this.config_.endpoint,
|
||||||
...this.config_.additionalClientConfig,
|
...this.config_.additionalClientConfig,
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ Add the `validateAndTransformQuery` middleware to the API route in the file `src
|
|||||||
```ts title="src/api/middlewares.ts"
|
```ts title="src/api/middlewares.ts"
|
||||||
import {
|
import {
|
||||||
validateAndTransformQuery,
|
validateAndTransformQuery,
|
||||||
defineMiddlewares
|
defineMiddlewares,
|
||||||
} from "@medusajs/framework/http"
|
} from "@medusajs/framework/http"
|
||||||
import { PostStoreCustomSchema } from "./custom/validators"
|
import { PostStoreCustomSchema } from "./custom/validators"
|
||||||
|
|
||||||
|
|||||||
@@ -300,7 +300,7 @@ The first step is to use the `validateAndTransformQuery` middleware on the `GET`
|
|||||||
```ts title="src/api/middlewares.ts"
|
```ts title="src/api/middlewares.ts"
|
||||||
import {
|
import {
|
||||||
validateAndTransformQuery,
|
validateAndTransformQuery,
|
||||||
defineMiddlewares
|
defineMiddlewares,
|
||||||
} from "@medusajs/framework/http"
|
} from "@medusajs/framework/http"
|
||||||
import { createFindParams } from "@medusajs/medusa/api/utils/validators"
|
import { createFindParams } from "@medusajs/medusa/api/utils/validators"
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export const syncBrandsWorkflow = createWorkflow(
|
|||||||
() => {
|
() => {
|
||||||
const brands = retrieveBrandsStep()
|
const brands = retrieveBrandsStep()
|
||||||
|
|
||||||
updateBrandsInCmsStep({ brands })
|
createBrandsInCmsStep({ brands })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -8,19 +8,17 @@ In this chapter, you'll learn how to execute an action based on a condition in a
|
|||||||
|
|
||||||
## Why If-Conditions Aren't Allowed in Workflows?
|
## Why If-Conditions Aren't Allowed in Workflows?
|
||||||
|
|
||||||
Medusa creates an internal representation of the workflow definition you pass to `createWorkflow` to track and store its steps.
|
Medusa creates an internal representation of the workflow definition you pass to `createWorkflow` to track and store its steps. At that point, variables in the workflow don't have any values. They only do when you execute the workflow.
|
||||||
|
|
||||||
At that point, variables in the workflow don't have any values. They only do when you execute the workflow.
|
|
||||||
|
|
||||||
So, you can't use an if-condition that checks a variable's value, as the condition will be evaluated when Medusa creates the internal representation of the workflow, rather than during execution.
|
So, you can't use an if-condition that checks a variable's value, as the condition will be evaluated when Medusa creates the internal representation of the workflow, rather than during execution.
|
||||||
|
|
||||||
Instead, use when-then from the Workflows SDK.
|
Instead, use when-then from the Workflows SDK. It allows you to perform steps in a workflow only if a condition that you specify is satisified.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What is the When-Then Utility?
|
## How to use When-Then?
|
||||||
|
|
||||||
when-then from the Workflows SDK executes an action if a condition is satisfied. The `when` function accepts as a parameter a function that returns a boolean value, and the `then` function is chained to `when`. `then` accepts as a parameter a function that's executed if `when`'s parameter function returns a `true` value.
|
The Workflows SDK provides a `when` function that is used to check whether a condition is true. You chain a `then` function to `when` that specifies the steps to execute if the condition in `when` is satisfied.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
@@ -78,3 +76,100 @@ In this code snippet, you execute the `isActiveStep` only if the `input.is_activ
|
|||||||
To specify the action to perform if the condition is satisfied, chain a `then` function to `when` and pass it a callback function.
|
To specify the action to perform if the condition is satisfied, chain a `then` function to `when` and pass it a callback function.
|
||||||
|
|
||||||
The callback function is only executed if `when`'s second parameter function returns a `true` value.
|
The callback function is only executed if `when`'s second parameter function returns a `true` value.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementing If-Else with When-Then
|
||||||
|
|
||||||
|
when-then doesn't support if-else conditions. Instead, use two `when-then` conditions in your workflow.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
export const ifElseHighlights = [
|
||||||
|
["7", "when", "This when-then block acts as an if condition."],
|
||||||
|
["16", "when", "This when-then block acts as an else condiiton."]
|
||||||
|
]
|
||||||
|
|
||||||
|
```ts highlights={ifElseHighlights}
|
||||||
|
const workflow = createWorkflow(
|
||||||
|
"workflow",
|
||||||
|
function (input: {
|
||||||
|
is_active: boolean
|
||||||
|
}) {
|
||||||
|
|
||||||
|
const isActiveResult = when(
|
||||||
|
input,
|
||||||
|
(input) => {
|
||||||
|
return input.is_active
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
return isActiveStep()
|
||||||
|
})
|
||||||
|
|
||||||
|
const notIsActiveResult = when(
|
||||||
|
input,
|
||||||
|
(input) => {
|
||||||
|
return input.is_active
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
return notIsActiveStep()
|
||||||
|
})
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above workflow, you use two `when-then` blocks. The first one performs a step if `input.is_active` is `true`, and the second performs a step if `input.is_active` is `false`, acting as an else condition.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Specify Name for When-Then
|
||||||
|
|
||||||
|
Internally, `when-then` blocks have a unique name similar to a step. When you return a step's result in a `when-then` block, the block's name is derived from the step's name. For example:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const isActiveResult = when(
|
||||||
|
input,
|
||||||
|
(input) => {
|
||||||
|
return input.is_active
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
return isActiveStep()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
This `when-then` block's internal name will be `when-then-is-active`, where `is-active` is the step's name.
|
||||||
|
|
||||||
|
However, if you need to return in your `when-then` block something other than a step's result, you need to specify a unique step name for that block. Otherwise, Medusa will generate a random name for it which can cause unexpected errors in production.
|
||||||
|
|
||||||
|
You pass a name for `when-then` as a first parameter of `when`, whose signature can accept three parameters in this case. For example:
|
||||||
|
|
||||||
|
export const nameHighlights = [
|
||||||
|
["2", `"check-is-active"`, "The when-then block's name."],
|
||||||
|
["10", "return", "`then` returns a value other than the step's result."]
|
||||||
|
]
|
||||||
|
|
||||||
|
```ts highlights={nameHighlights}
|
||||||
|
const { isActive } = when(
|
||||||
|
"check-is-active",
|
||||||
|
input,
|
||||||
|
(input) => {
|
||||||
|
return input.is_active
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
const isActive = isActiveStep()
|
||||||
|
|
||||||
|
return {
|
||||||
|
isActive,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Since `then` returns a value different than the step's result, you pass to the `when` function the following parameters:
|
||||||
|
|
||||||
|
1. A unique name to be assigned to the `when-then` block.
|
||||||
|
2. Either an object or the workflow's input. This data is passed as a parameter to the function in `when`'s second parameter.
|
||||||
|
3. A function that returns a boolean indicating whether to execute the action in `then`.
|
||||||
|
|
||||||
|
The second and third parameters are the same as the parameters you previously passed to `when`.
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ const myWorkflow = createWorkflow(
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also pair multiple `when-then` blocks to implement an `if-else` condition as explained in [this chapter](../conditions/page.mdx).
|
||||||
|
|
||||||
### No Conditional Operators
|
### No Conditional Operators
|
||||||
|
|
||||||
You can't use conditional operators in a workflow, such as `??` or `||`.
|
You can't use conditional operators in a workflow, such as `??` or `||`.
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ export const generatedEditDates = {
|
|||||||
"app/learn/fundamentals/data-models/default-properties/page.mdx": "2024-10-21T13:30:21.368Z",
|
"app/learn/fundamentals/data-models/default-properties/page.mdx": "2024-10-21T13:30:21.368Z",
|
||||||
"app/learn/fundamentals/workflows/advanced-example/page.mdx": "2024-09-11T10:46:59.975Z",
|
"app/learn/fundamentals/workflows/advanced-example/page.mdx": "2024-09-11T10:46:59.975Z",
|
||||||
"app/learn/fundamentals/events-and-subscribers/emit-event/page.mdx": "2024-11-25T16:19:32.168Z",
|
"app/learn/fundamentals/events-and-subscribers/emit-event/page.mdx": "2024-11-25T16:19:32.168Z",
|
||||||
"app/learn/fundamentals/workflows/conditions/page.mdx": "2024-12-09T15:55:51.565Z",
|
"app/learn/fundamentals/workflows/conditions/page.mdx": "2024-12-11T08:44:00.239Z",
|
||||||
"app/learn/fundamentals/modules/module-link-directions/page.mdx": "2024-07-24T09:16:01+02:00",
|
"app/learn/fundamentals/modules/module-link-directions/page.mdx": "2024-07-24T09:16:01+02:00",
|
||||||
"app/learn/fundamentals/admin/page.mdx": "2024-10-23T07:08:55.898Z",
|
"app/learn/fundamentals/admin/page.mdx": "2024-10-23T07:08:55.898Z",
|
||||||
"app/learn/fundamentals/workflows/long-running-workflow/page.mdx": "2024-12-04T07:37:59.822Z",
|
"app/learn/fundamentals/workflows/long-running-workflow/page.mdx": "2024-12-04T07:37:59.822Z",
|
||||||
"app/learn/fundamentals/workflows/constructor-constraints/page.mdx": "2024-12-09T14:43:35.160Z",
|
"app/learn/fundamentals/workflows/constructor-constraints/page.mdx": "2024-12-11T08:36:08.282Z",
|
||||||
"app/learn/fundamentals/data-models/write-migration/page.mdx": "2024-11-11T15:27:59.794Z",
|
"app/learn/fundamentals/data-models/write-migration/page.mdx": "2024-11-11T15:27:59.794Z",
|
||||||
"app/learn/fundamentals/data-models/manage-relationships/page.mdx": "2024-10-28T04:22:21.328Z",
|
"app/learn/fundamentals/data-models/manage-relationships/page.mdx": "2024-10-28T04:22:21.328Z",
|
||||||
"app/learn/fundamentals/modules/remote-query/page.mdx": "2024-07-21T21:20:24+02:00",
|
"app/learn/fundamentals/modules/remote-query/page.mdx": "2024-07-21T21:20:24+02:00",
|
||||||
|
|||||||
@@ -529,12 +529,15 @@ In the workflow, you retrieve the cart's linked `Custom` record using Query.
|
|||||||
Next, replace the `TODO` with the following:
|
Next, replace the `TODO` with the following:
|
||||||
|
|
||||||
```ts title="src/workflows/update-custom-from-cart/index.ts"
|
```ts title="src/workflows/update-custom-from-cart/index.ts"
|
||||||
const created = when({
|
const created = when(
|
||||||
input,
|
"create-cart-custom-link",
|
||||||
carts,
|
{
|
||||||
}, (data) =>
|
input,
|
||||||
!data.carts[0].custom &&
|
carts,
|
||||||
data.input.additional_data?.custom_name?.length > 0
|
},
|
||||||
|
(data) =>
|
||||||
|
!data.carts[0].custom &&
|
||||||
|
data.input.additional_data?.custom_name?.length > 0
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const custom = createCustomStep({
|
const custom = createCustomStep({
|
||||||
@@ -563,14 +566,16 @@ To create the `Custom` record, you use the `createCustomStep` you created in an
|
|||||||
Next, replace the new `TODO` with the following:
|
Next, replace the new `TODO` with the following:
|
||||||
|
|
||||||
```ts title="src/workflows/update-custom-from-cart/index.ts"
|
```ts title="src/workflows/update-custom-from-cart/index.ts"
|
||||||
const deleted = when({
|
const deleted = when(
|
||||||
input,
|
"delete-cart-custom-link",
|
||||||
carts,
|
{
|
||||||
}, (data) =>
|
input,
|
||||||
data.carts[0].custom && (
|
carts,
|
||||||
data.input.additional_data?.custom_name === null ||
|
}, (data) =>
|
||||||
data.input.additional_data?.custom_name.length === 0
|
data.carts[0].custom && (
|
||||||
)
|
data.input.additional_data?.custom_name === null ||
|
||||||
|
data.input.additional_data?.custom_name.length === 0
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
deleteCustomStep({
|
deleteCustomStep({
|
||||||
@@ -599,12 +604,10 @@ const updated = when({
|
|||||||
carts,
|
carts,
|
||||||
}, (data) => data.carts[0].custom && data.input.additional_data?.custom_name?.length > 0)
|
}, (data) => data.carts[0].custom && data.input.additional_data?.custom_name?.length > 0)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const custom = updateCustomStep({
|
return updateCustomStep({
|
||||||
id: carts[0].custom.id,
|
id: carts[0].custom.id,
|
||||||
custom_name: input.additional_data.custom_name,
|
custom_name: input.additional_data.custom_name,
|
||||||
})
|
})
|
||||||
|
|
||||||
return custom
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return new WorkflowResponse({
|
return new WorkflowResponse({
|
||||||
|
|||||||
@@ -541,12 +541,14 @@ In the workflow, you retrieve the customer's linked `Custom` record using Query.
|
|||||||
Next, replace the `TODO` with the following:
|
Next, replace the `TODO` with the following:
|
||||||
|
|
||||||
```ts title="src/workflows/update-custom-from-customer/index.ts"
|
```ts title="src/workflows/update-custom-from-customer/index.ts"
|
||||||
const created = when({
|
const created = when(
|
||||||
input,
|
"create-customer-custom-link",
|
||||||
customers,
|
{
|
||||||
}, (data) =>
|
input,
|
||||||
!data.customers[0].custom &&
|
customers,
|
||||||
data.input.additional_data?.custom_name?.length > 0
|
}, (data) =>
|
||||||
|
!data.customers[0].custom &&
|
||||||
|
data.input.additional_data?.custom_name?.length > 0
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const custom = createCustomStep({
|
const custom = createCustomStep({
|
||||||
@@ -575,14 +577,16 @@ To create the `Custom` record, you use the `createCustomStep` you created in an
|
|||||||
Next, replace the new `TODO` with the following:
|
Next, replace the new `TODO` with the following:
|
||||||
|
|
||||||
```ts title="src/workflows/update-custom-from-customer/index.ts"
|
```ts title="src/workflows/update-custom-from-customer/index.ts"
|
||||||
const deleted = when({
|
const deleted = when(
|
||||||
input,
|
"delete-customer-custom-link",
|
||||||
customers,
|
{
|
||||||
}, (data) =>
|
input,
|
||||||
data.customers[0].custom && (
|
customers,
|
||||||
data.input.additional_data?.custom_name === null ||
|
}, (data) =>
|
||||||
data.input.additional_data?.custom_name.length === 0
|
data.customers[0].custom && (
|
||||||
)
|
data.input.additional_data?.custom_name === null ||
|
||||||
|
data.input.additional_data?.custom_name.length === 0
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
deleteCustomStep({
|
deleteCustomStep({
|
||||||
@@ -611,12 +615,10 @@ const updated = when({
|
|||||||
customers,
|
customers,
|
||||||
}, (data) => data.customers[0].custom && data.input.additional_data?.custom_name?.length > 0)
|
}, (data) => data.customers[0].custom && data.input.additional_data?.custom_name?.length > 0)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const custom = updateCustomStep({
|
return updateCustomStep({
|
||||||
id: customers[0].custom.id,
|
id: customers[0].custom.id,
|
||||||
custom_name: input.additional_data.custom_name,
|
custom_name: input.additional_data.custom_name,
|
||||||
})
|
})
|
||||||
|
|
||||||
return custom
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return new WorkflowResponse({
|
return new WorkflowResponse({
|
||||||
|
|||||||
@@ -547,12 +547,14 @@ In the workflow, you retrieve the product's linked `Custom` record using Query.
|
|||||||
Next, replace the `TODO` with the following:
|
Next, replace the `TODO` with the following:
|
||||||
|
|
||||||
```ts title="src/workflows/update-custom-from-product/index.ts"
|
```ts title="src/workflows/update-custom-from-product/index.ts"
|
||||||
const created = when({
|
const created = when(
|
||||||
input,
|
"create-product-custom-link",
|
||||||
products,
|
{
|
||||||
}, (data) =>
|
input,
|
||||||
!data.products[0].custom &&
|
products,
|
||||||
data.input.additional_data?.custom_name?.length > 0
|
}, (data) =>
|
||||||
|
!data.products[0].custom &&
|
||||||
|
data.input.additional_data?.custom_name?.length > 0
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const custom = createCustomStep({
|
const custom = createCustomStep({
|
||||||
@@ -581,14 +583,16 @@ To create the `Custom` record, you use the `createCustomStep` you created in an
|
|||||||
Next, replace the new `TODO` with the following:
|
Next, replace the new `TODO` with the following:
|
||||||
|
|
||||||
```ts title="src/workflows/update-custom-from-product/index.ts"
|
```ts title="src/workflows/update-custom-from-product/index.ts"
|
||||||
const deleted = when({
|
const deleted = when(
|
||||||
input,
|
"delete-product-custom-link",
|
||||||
products,
|
{
|
||||||
}, (data) =>
|
input,
|
||||||
data.products[0].custom && (
|
products,
|
||||||
data.input.additional_data?.custom_name === null ||
|
}, (data) =>
|
||||||
data.input.additional_data?.custom_name.length === 0
|
data.products[0].custom && (
|
||||||
)
|
data.input.additional_data?.custom_name === null ||
|
||||||
|
data.input.additional_data?.custom_name.length === 0
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
deleteCustomStep({
|
deleteCustomStep({
|
||||||
@@ -617,12 +621,10 @@ const updated = when({
|
|||||||
products,
|
products,
|
||||||
}, (data) => data.products[0].custom && data.input.additional_data?.custom_name?.length > 0)
|
}, (data) => data.products[0].custom && data.input.additional_data?.custom_name?.length > 0)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const custom = updateCustomStep({
|
return updateCustomStep({
|
||||||
id: products[0].custom.id,
|
id: products[0].custom.id,
|
||||||
custom_name: input.additional_data.custom_name,
|
custom_name: input.additional_data.custom_name,
|
||||||
})
|
})
|
||||||
|
|
||||||
return custom
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return new WorkflowResponse({
|
return new WorkflowResponse({
|
||||||
|
|||||||
@@ -553,12 +553,14 @@ In the workflow, you retrieve the promotion's linked `Custom` record using Query
|
|||||||
Next, replace the `TODO` with the following:
|
Next, replace the `TODO` with the following:
|
||||||
|
|
||||||
```ts title="src/workflows/update-custom-from-promotion/index.ts"
|
```ts title="src/workflows/update-custom-from-promotion/index.ts"
|
||||||
const created = when({
|
const created = when(
|
||||||
input,
|
"create-promotion-custom-link",
|
||||||
promotions,
|
{
|
||||||
}, (data) =>
|
input,
|
||||||
!data.promotions[0].custom &&
|
promotions,
|
||||||
data.input.additional_data?.custom_name?.length > 0
|
}, (data) =>
|
||||||
|
!data.promotions[0].custom &&
|
||||||
|
data.input.additional_data?.custom_name?.length > 0
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const custom = createCustomStep({
|
const custom = createCustomStep({
|
||||||
@@ -587,14 +589,16 @@ To create the `Custom` record, you use the `createCustomStep` you created in an
|
|||||||
Next, replace the new `TODO` with the following:
|
Next, replace the new `TODO` with the following:
|
||||||
|
|
||||||
```ts title="src/workflows/update-custom-from-promotion/index.ts"
|
```ts title="src/workflows/update-custom-from-promotion/index.ts"
|
||||||
const deleted = when({
|
const deleted = when(
|
||||||
input,
|
"delete-promotion-custom-link",
|
||||||
promotions,
|
{
|
||||||
}, (data) =>
|
input,
|
||||||
data.promotions[0].custom && (
|
promotions,
|
||||||
data.input.additional_data?.custom_name === null ||
|
}, (data) =>
|
||||||
data.input.additional_data?.custom_name.length === 0
|
data.promotions[0].custom && (
|
||||||
)
|
data.input.additional_data?.custom_name === null ||
|
||||||
|
data.input.additional_data?.custom_name.length === 0
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
deleteCustomStep({
|
deleteCustomStep({
|
||||||
@@ -623,12 +627,10 @@ const updated = when({
|
|||||||
promotions,
|
promotions,
|
||||||
}, (data) => data.promotions[0].custom && data.input.additional_data?.custom_name?.length > 0)
|
}, (data) => data.promotions[0].custom && data.input.additional_data?.custom_name?.length > 0)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const custom = updateCustomStep({
|
return updateCustomStep({
|
||||||
id: promotions[0].custom.id,
|
id: promotions[0].custom.id,
|
||||||
custom_name: input.additional_data.custom_name,
|
custom_name: input.additional_data.custom_name,
|
||||||
})
|
})
|
||||||
|
|
||||||
return custom
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return new WorkflowResponse({
|
return new WorkflowResponse({
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ export const PostStoreCustomSchema = z.object({
|
|||||||
```ts title="src/api/middlewares.ts" highlights={[["13", "validateAndTransformBody"]]}
|
```ts title="src/api/middlewares.ts" highlights={[["13", "validateAndTransformBody"]]}
|
||||||
import {
|
import {
|
||||||
validateAndTransformBody,
|
validateAndTransformBody,
|
||||||
defineMiddlewares
|
defineMiddlewares,
|
||||||
} from "@medusajs/framework/http"
|
} from "@medusajs/framework/http"
|
||||||
import { PostStoreCustomSchema } from "./custom/validators"
|
import { PostStoreCustomSchema } from "./custom/validators"
|
||||||
|
|
||||||
@@ -629,7 +629,7 @@ import type {
|
|||||||
MedusaNextFunction,
|
MedusaNextFunction,
|
||||||
MedusaRequest,
|
MedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
defineMiddlewares
|
defineMiddlewares,
|
||||||
} from "@medusajs/framework/http"
|
} from "@medusajs/framework/http"
|
||||||
import { ConfigModule } from "@medusajs/framework/types"
|
import { ConfigModule } from "@medusajs/framework/types"
|
||||||
import { parseCorsOrigins } from "@medusajs/framework/utils"
|
import { parseCorsOrigins } from "@medusajs/framework/utils"
|
||||||
@@ -2296,8 +2296,7 @@ const workflow = createWorkflow(
|
|||||||
return input.is_active
|
return input.is_active
|
||||||
}
|
}
|
||||||
).then(() => {
|
).then(() => {
|
||||||
const stepResult = isActiveStep()
|
return isActiveStep()
|
||||||
return stepResult
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// executed without condition
|
// executed without condition
|
||||||
|
|||||||
@@ -695,17 +695,17 @@ export const syncStep = createStep(
|
|||||||
const sanityModule: SanityModuleService = container.resolve(SANITY_MODULE)
|
const sanityModule: SanityModuleService = container.resolve(SANITY_MODULE)
|
||||||
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
||||||
|
|
||||||
let total = 0;
|
const total = 0
|
||||||
const upsertMap: {
|
const upsertMap: {
|
||||||
before: any
|
before: any
|
||||||
after: any
|
after: any
|
||||||
}[] = []
|
}[] = []
|
||||||
|
|
||||||
const batchSize = 200;
|
const batchSize = 200
|
||||||
let hasMore = true;
|
const hasMore = true
|
||||||
let offset = 0;
|
const offset = 0
|
||||||
let filters = input.product_ids ? {
|
const filters = input.product_ids ? {
|
||||||
id: input.product_ids
|
id: input.product_ids,
|
||||||
} : {}
|
} : {}
|
||||||
|
|
||||||
while (hasMore) {
|
while (hasMore) {
|
||||||
@@ -772,16 +772,16 @@ while (hasMore) {
|
|||||||
const after = await sanityModule.upsertSyncDocument(
|
const after = await sanityModule.upsertSyncDocument(
|
||||||
"product",
|
"product",
|
||||||
prod as ProductDTO
|
prod as ProductDTO
|
||||||
);
|
)
|
||||||
|
|
||||||
upsertMap.push({
|
upsertMap.push({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
before: prod.sanity_product,
|
before: prod.sanity_product,
|
||||||
after
|
after,
|
||||||
})
|
})
|
||||||
|
|
||||||
return after
|
return after
|
||||||
}),
|
})
|
||||||
)
|
)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return StepResponse.permanentFailure(
|
return StepResponse.permanentFailure(
|
||||||
@@ -805,7 +805,7 @@ You also wrap the `promiseAll` function within a try-catch block. In the catch b
|
|||||||
Finally, after the `while` loop and at the end of the step, add the following return statement:
|
Finally, after the `while` loop and at the end of the step, add the following return statement:
|
||||||
|
|
||||||
```ts title="src/workflows/sanity-sync-products/steps/sync.ts"
|
```ts title="src/workflows/sanity-sync-products/steps/sync.ts"
|
||||||
return new StepResponse({ total }, upsertMap);
|
return new StepResponse({ total }, upsertMap)
|
||||||
```
|
```
|
||||||
|
|
||||||
If no errors occur, the step returns an instance of `StepResponse`, which must be returned by any step. It accepts as a first parameter the data to return to the workflow that executed this step.
|
If no errors occur, the step returns an instance of `StepResponse`, which must be returned by any step. It accepts as a first parameter the data to return to the workflow that executed this step.
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ export const validateVariantOutOfStockStep = createStep(
|
|||||||
const query = container.resolve("query")
|
const query = container.resolve("query")
|
||||||
const availability = await getVariantAvailability(query, {
|
const availability = await getVariantAvailability(query, {
|
||||||
variant_ids: [variant_id],
|
variant_ids: [variant_id],
|
||||||
sales_channel_id
|
sales_channel_id,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (availability[variant_id].availability > 0) {
|
if (availability[variant_id].availability > 0) {
|
||||||
@@ -599,7 +599,7 @@ export const subscriptionWorkflow1Highlights = [
|
|||||||
["16", "createWorkflow", "Create a workflow."],
|
["16", "createWorkflow", "Create a workflow."],
|
||||||
["23", "transform", "Set the customer ID to an empty string if not provided."],
|
["23", "transform", "Set the customer ID to an empty string if not provided."],
|
||||||
["28", "when", "If email is not set, try to retrieve customer by its ID."],
|
["28", "when", "If email is not set, try to retrieve customer by its ID."],
|
||||||
["44", "transform", "Set the email either to the one in the input or the specified customer's email."],
|
["48", "transform", "Set the email either to the one in the input or the specified customer's email."],
|
||||||
]
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/create-restock-subscription/index.ts" highlights={subscriptionWorkflow1Highlights}
|
```ts title="src/workflows/create-restock-subscription/index.ts" highlights={subscriptionWorkflow1Highlights}
|
||||||
@@ -623,24 +623,28 @@ export const createRestockSubscriptionWorkflow = createWorkflow(
|
|||||||
({
|
({
|
||||||
variant_id,
|
variant_id,
|
||||||
sales_channel_id,
|
sales_channel_id,
|
||||||
customer
|
customer,
|
||||||
}: CreateRestockSubscriptionWorkflowInput) => {
|
}: CreateRestockSubscriptionWorkflowInput) => {
|
||||||
const customerId = transform({
|
const customerId = transform({
|
||||||
customer
|
customer,
|
||||||
}, (data) => {
|
}, (data) => {
|
||||||
return data.customer.customer_id || ""
|
return data.customer.customer_id || ""
|
||||||
})
|
})
|
||||||
const retrievedCustomer = when({ customer }, ({ customer }) => {
|
const retrievedCustomer = when(
|
||||||
return !customer.email
|
"retrieve-customer-by-id",
|
||||||
}).then(() => {
|
{ customer },
|
||||||
|
({ customer }) => {
|
||||||
|
return !customer.email
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const { data } = useQueryGraphStep({
|
const { data } = useQueryGraphStep({
|
||||||
entity: "customer",
|
entity: "customer",
|
||||||
fields: ["email"],
|
fields: ["email"],
|
||||||
filters: { id: customerId },
|
filters: { id: customerId },
|
||||||
options: {
|
options: {
|
||||||
throwIfKeyNotFound: true
|
throwIfKeyNotFound: true,
|
||||||
}
|
},
|
||||||
}).config({ name: "retrieve-customer" })
|
}).config({ name: "retrieve-customer" })
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@@ -648,7 +652,7 @@ export const createRestockSubscriptionWorkflow = createWorkflow(
|
|||||||
|
|
||||||
const email = transform({
|
const email = transform({
|
||||||
retrievedCustomer,
|
retrievedCustomer,
|
||||||
customer
|
customer,
|
||||||
}, (data) => {
|
}, (data) => {
|
||||||
return data.customer?.email ?? data.retrievedCustomer?.[0].email
|
return data.customer?.email ?? data.retrievedCustomer?.[0].email
|
||||||
})
|
})
|
||||||
@@ -688,7 +692,7 @@ export const subscriptionWorkflow2Highlights = [
|
|||||||
```ts title="src/workflows/create-restock-subscription/index.ts" highlights={subscriptionWorkflow2Highlights}
|
```ts title="src/workflows/create-restock-subscription/index.ts" highlights={subscriptionWorkflow2Highlights}
|
||||||
validateVariantOutOfStockStep({
|
validateVariantOutOfStockStep({
|
||||||
variant_id,
|
variant_id,
|
||||||
sales_channel_id
|
sales_channel_id,
|
||||||
})
|
})
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -698,8 +702,8 @@ const { data: restockSubscriptions } = useQueryGraphStep({
|
|||||||
filters: {
|
filters: {
|
||||||
email,
|
email,
|
||||||
variant_id,
|
variant_id,
|
||||||
sales_channel_id
|
sales_channel_id,
|
||||||
}
|
},
|
||||||
}).config({ name: "retrieve-subscriptions" })
|
}).config({ name: "retrieve-subscriptions" })
|
||||||
|
|
||||||
when({ restockSubscriptions }, ({ restockSubscriptions }) => {
|
when({ restockSubscriptions }, ({ restockSubscriptions }) => {
|
||||||
@@ -710,7 +714,7 @@ when({ restockSubscriptions }, ({ restockSubscriptions }) => {
|
|||||||
variant_id,
|
variant_id,
|
||||||
sales_channel_id,
|
sales_channel_id,
|
||||||
email,
|
email,
|
||||||
customer_id: customer.customer_id
|
customer_id: customer.customer_id,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -720,7 +724,7 @@ when({ restockSubscriptions }, ({ restockSubscriptions }) => {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
updateRestockSubscriptionStep({
|
updateRestockSubscriptionStep({
|
||||||
id: restockSubscriptions[0].id,
|
id: restockSubscriptions[0].id,
|
||||||
customer_id: customer.customer_id
|
customer_id: customer.customer_id,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -731,8 +735,8 @@ const { data: restockSubscription } = useQueryGraphStep({
|
|||||||
filters: {
|
filters: {
|
||||||
email,
|
email,
|
||||||
variant_id,
|
variant_id,
|
||||||
sales_channel_id
|
sales_channel_id,
|
||||||
}
|
},
|
||||||
}).config({ name: "retrieve-restock-subscription" })
|
}).config({ name: "retrieve-restock-subscription" })
|
||||||
|
|
||||||
return new WorkflowResponse(
|
return new WorkflowResponse(
|
||||||
@@ -1116,12 +1120,12 @@ Before adding the step that does this, you'll add a method in the `RestockModule
|
|||||||
|
|
||||||
```ts title="src/modules/restock/service.ts"
|
```ts title="src/modules/restock/service.ts"
|
||||||
// other imports...
|
// other imports...
|
||||||
import { InjectManager, MedusaContext } from "@medusajs/framework/utils";
|
import { InjectManager, MedusaContext } from "@medusajs/framework/utils"
|
||||||
import { Context } from "@medusajs/framework/types"
|
import { Context } from "@medusajs/framework/types"
|
||||||
import { EntityManager } from "@mikro-orm/knex";
|
import { EntityManager } from "@mikro-orm/knex"
|
||||||
|
|
||||||
class RestockModuleService extends MedusaService({
|
class RestockModuleService extends MedusaService({
|
||||||
RestockSubscription
|
RestockSubscription,
|
||||||
}) {
|
}) {
|
||||||
// ...
|
// ...
|
||||||
@InjectManager()
|
@InjectManager()
|
||||||
@@ -1147,9 +1151,9 @@ You'll use this method in the step. To create the step, create the file `src/wor
|
|||||||

|

|
||||||
|
|
||||||
```ts title="src/workflows/send-restock-notifications/steps/get-distinct-subscriptions.ts"
|
```ts title="src/workflows/send-restock-notifications/steps/get-distinct-subscriptions.ts"
|
||||||
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk";
|
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
|
||||||
import RestockModuleService from "../../../modules/restock/service";
|
import RestockModuleService from "../../../modules/restock/service"
|
||||||
import { RESTOCK_MODULE } from "../../../modules/restock";
|
import { RESTOCK_MODULE } from "../../../modules/restock"
|
||||||
|
|
||||||
export const getDistinctSubscriptionsStep = createStep(
|
export const getDistinctSubscriptionsStep = createStep(
|
||||||
"get-distinct-subscriptions",
|
"get-distinct-subscriptions",
|
||||||
@@ -1194,7 +1198,7 @@ export const getRestockedStep = createStep(
|
|||||||
input.map(async (restockSubscription) => {
|
input.map(async (restockSubscription) => {
|
||||||
const variantAvailability = await getVariantAvailability(query, {
|
const variantAvailability = await getVariantAvailability(query, {
|
||||||
variant_ids: [restockSubscription.variant_id],
|
variant_ids: [restockSubscription.variant_id],
|
||||||
sales_channel_id: restockSubscription.sales_channel_id
|
sales_channel_id: restockSubscription.sales_channel_id,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (variantAvailability[restockSubscription.variant_id].availability > 0) {
|
if (variantAvailability[restockSubscription.variant_id].availability > 0) {
|
||||||
@@ -1318,12 +1322,12 @@ Create the file `src/workflows/send-restock-notifications/index.ts` with the fol
|
|||||||

|

|
||||||
|
|
||||||
```ts title="src/workflows/send-restock-notifications/index.ts"
|
```ts title="src/workflows/send-restock-notifications/index.ts"
|
||||||
import { createWorkflow, transform, WorkflowResponse } from "@medusajs/framework/workflows-sdk";
|
import { createWorkflow, transform, WorkflowResponse } from "@medusajs/framework/workflows-sdk"
|
||||||
import { useQueryGraphStep } from "@medusajs/medusa/core-flows";
|
import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
||||||
import { getRestockedStep } from "./steps/get-restocked";
|
import { getRestockedStep } from "./steps/get-restocked"
|
||||||
import { sendRestockNotificationStep } from "./steps/send-restock-notification";
|
import { sendRestockNotificationStep } from "./steps/send-restock-notification"
|
||||||
import { deleteRestockSubscriptionStep } from "./steps/delete-restock-subscriptions";
|
import { deleteRestockSubscriptionStep } from "./steps/delete-restock-subscriptions"
|
||||||
import { getDistinctSubscriptionsStep } from "./steps/get-distinct-subscriptions";
|
import { getDistinctSubscriptionsStep } from "./steps/get-distinct-subscriptions"
|
||||||
|
|
||||||
export const sendRestockNotificationsWorkflow = createWorkflow(
|
export const sendRestockNotificationsWorkflow = createWorkflow(
|
||||||
"send-restock-notifications",
|
"send-restock-notifications",
|
||||||
@@ -1334,11 +1338,11 @@ export const sendRestockNotificationsWorkflow = createWorkflow(
|
|||||||
const restockedSubscriptions = getRestockedStep(subscriptions)
|
const restockedSubscriptions = getRestockedStep(subscriptions)
|
||||||
|
|
||||||
const { variant_ids, sales_channel_ids } = transform({
|
const { variant_ids, sales_channel_ids } = transform({
|
||||||
restockedSubscriptions
|
restockedSubscriptions,
|
||||||
}, (data) => {
|
}, (data) => {
|
||||||
const filters: Record<string, string[]> = {
|
const filters: Record<string, string[]> = {
|
||||||
variant_ids: [],
|
variant_ids: [],
|
||||||
sales_channel_ids: []
|
sales_channel_ids: [],
|
||||||
}
|
}
|
||||||
data.restockedSubscriptions.map((subscription) => {
|
data.restockedSubscriptions.map((subscription) => {
|
||||||
filters.variant_ids.push(subscription.variant_id)
|
filters.variant_ids.push(subscription.variant_id)
|
||||||
@@ -1354,8 +1358,8 @@ export const sendRestockNotificationsWorkflow = createWorkflow(
|
|||||||
fields: ["*", "product_variant.*"],
|
fields: ["*", "product_variant.*"],
|
||||||
filters: {
|
filters: {
|
||||||
variant_id: variant_ids,
|
variant_id: variant_ids,
|
||||||
sales_channel_id: sales_channel_ids
|
sales_channel_id: sales_channel_ids,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -1365,7 +1369,7 @@ export const sendRestockNotificationsWorkflow = createWorkflow(
|
|||||||
deleteRestockSubscriptionStep(restockedSubscriptionsWithEmails)
|
deleteRestockSubscriptionStep(restockedSubscriptionsWithEmails)
|
||||||
|
|
||||||
return new WorkflowResponse({
|
return new WorkflowResponse({
|
||||||
subscriptions: restockedSubscriptionsWithEmails
|
subscriptions: restockedSubscriptionsWithEmails,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1705,11 +1705,11 @@ export const createDpoWorkflowHighlights = [
|
|||||||
["27", "completeCartWorkflow", "Create an order for the cart."],
|
["27", "completeCartWorkflow", "Create an order for the cart."],
|
||||||
["33", "useQueryGraphStep", "Retrieve the order's items and their associated variants and linked digital products."],
|
["33", "useQueryGraphStep", "Retrieve the order's items and their associated variants and linked digital products."],
|
||||||
["57", "when", "Check whether the order has any digital products."],
|
["57", "when", "Check whether the order has any digital products."],
|
||||||
["60", "then", "Perform the callback function if an order has digital products."],
|
["63", "then", "Perform the callback function if an order has digital products."],
|
||||||
["63", "createDigitalProductOrderStep", "Create the digital product order."],
|
["66", "createDigitalProductOrderStep", "Create the digital product order."],
|
||||||
["67", "createRemoteLinkStep", "Link the digital product order to the Medusa order."],
|
["70", "createRemoteLinkStep", "Link the digital product order to the Medusa order."],
|
||||||
["76", "createOrderFulfillmentWorkflow", "Create a fulfillment for the digital products in the order."],
|
["79", "createOrderFulfillmentWorkflow", "Create a fulfillment for the digital products in the order."],
|
||||||
["90", "emitEventStep", "Emit the `digital_product_order.created` event."]
|
["93", "emitEventStep", "Emit the `digital_product_order.created` event."]
|
||||||
]
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/create-digital-product-order/index.ts" highlights={createDpoWorkflowHighlights} collapsibleLines="1-17" expandMoreLabel="Show Imports"
|
```ts title="src/workflows/create-digital-product-order/index.ts" highlights={createDpoWorkflowHighlights} collapsibleLines="1-17" expandMoreLabel="Show Imports"
|
||||||
@@ -1769,10 +1769,13 @@ const createDigitalProductOrderWorkflow = createWorkflow(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const digital_product_order = when(itemsWithDigitalProducts, (itemsWithDigitalProducts) => {
|
const digital_product_order = when(
|
||||||
return itemsWithDigitalProducts.length
|
"create-digital-product-order-condition",
|
||||||
})
|
itemsWithDigitalProducts,
|
||||||
.then(() => {
|
(itemsWithDigitalProducts) => {
|
||||||
|
return itemsWithDigitalProducts.length
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
const {
|
const {
|
||||||
digital_product_order,
|
digital_product_order,
|
||||||
} = createDigitalProductOrderStep({
|
} = createDigitalProductOrderStep({
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
export const metadata = {
|
||||||
|
title: `Workflow Errors`,
|
||||||
|
}
|
||||||
|
|
||||||
|
# {metadata.title}
|
||||||
|
|
||||||
|
## When-Then Error: Handler for action X Not Found
|
||||||
|
|
||||||
|
The following error may occur in production if you use a `when-then` block in your workflow:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
custom-workflow:when-then-01JE8Z0M1FXSE2NCK1G04S0RR2:invoke - Handler for action \"when-then-01JE8Z0M1FXSE2NCK1G04S0RR2\" not found...
|
||||||
|
```
|
||||||
|
|
||||||
|
This occurs if the `when-then` block doesn't return a step's result and doesn't have a name specified. You can resolve it by passing a name as a first parameter of `when`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const result = when(
|
||||||
|
"custom-when-condition"
|
||||||
|
// ... rest of the parameters
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Learn more about passing a name for `when-then` in [this documentation](!docs!/learn/fundamentals/workflows/conditions#specify-name-for-when-then)
|
||||||
@@ -2180,15 +2180,15 @@ export const generatedEditDates = {
|
|||||||
"app/commerce-modules/auth/reset-password/page.mdx": "2024-11-27T13:33:55.940Z",
|
"app/commerce-modules/auth/reset-password/page.mdx": "2024-11-27T13:33:55.940Z",
|
||||||
"app/storefront-development/customers/reset-password/page.mdx": "2024-09-25T10:21:46.647Z",
|
"app/storefront-development/customers/reset-password/page.mdx": "2024-09-25T10:21:46.647Z",
|
||||||
"app/commerce-modules/api-key/links-to-other-modules/page.mdx": "2024-10-08T08:05:36.596Z",
|
"app/commerce-modules/api-key/links-to-other-modules/page.mdx": "2024-10-08T08:05:36.596Z",
|
||||||
"app/commerce-modules/cart/extend/page.mdx": "2024-12-09T16:11:39.857Z",
|
"app/commerce-modules/cart/extend/page.mdx": "2024-12-11T09:05:37.041Z",
|
||||||
"app/commerce-modules/cart/links-to-other-modules/page.mdx": "2024-10-08T08:22:35.190Z",
|
"app/commerce-modules/cart/links-to-other-modules/page.mdx": "2024-10-08T08:22:35.190Z",
|
||||||
"app/commerce-modules/customer/extend/page.mdx": "2024-12-09T16:15:01.163Z",
|
"app/commerce-modules/customer/extend/page.mdx": "2024-12-11T09:05:35.368Z",
|
||||||
"app/commerce-modules/fulfillment/links-to-other-modules/page.mdx": "2024-10-08T14:58:24.935Z",
|
"app/commerce-modules/fulfillment/links-to-other-modules/page.mdx": "2024-10-08T14:58:24.935Z",
|
||||||
"app/commerce-modules/inventory/links-to-other-modules/page.mdx": "2024-10-08T15:18:30.109Z",
|
"app/commerce-modules/inventory/links-to-other-modules/page.mdx": "2024-10-08T15:18:30.109Z",
|
||||||
"app/commerce-modules/pricing/links-to-other-modules/page.mdx": "2024-10-09T13:51:49.986Z",
|
"app/commerce-modules/pricing/links-to-other-modules/page.mdx": "2024-10-09T13:51:49.986Z",
|
||||||
"app/commerce-modules/product/extend/page.mdx": "2024-12-09T16:15:01.163Z",
|
"app/commerce-modules/product/extend/page.mdx": "2024-12-11T09:07:25.252Z",
|
||||||
"app/commerce-modules/product/links-to-other-modules/page.mdx": "2024-10-09T14:14:09.401Z",
|
"app/commerce-modules/product/links-to-other-modules/page.mdx": "2024-10-09T14:14:09.401Z",
|
||||||
"app/commerce-modules/promotion/extend/page.mdx": "2024-12-09T16:19:19.364Z",
|
"app/commerce-modules/promotion/extend/page.mdx": "2024-12-11T09:07:24.137Z",
|
||||||
"app/commerce-modules/promotion/links-to-other-modules/page.mdx": "2024-10-09T14:51:37.194Z",
|
"app/commerce-modules/promotion/links-to-other-modules/page.mdx": "2024-10-09T14:51:37.194Z",
|
||||||
"app/commerce-modules/order/edit/page.mdx": "2024-10-09T08:50:05.334Z",
|
"app/commerce-modules/order/edit/page.mdx": "2024-10-09T08:50:05.334Z",
|
||||||
"app/commerce-modules/order/links-to-other-modules/page.mdx": "2024-10-09T11:23:05.488Z",
|
"app/commerce-modules/order/links-to-other-modules/page.mdx": "2024-10-09T11:23:05.488Z",
|
||||||
@@ -2246,7 +2246,7 @@ export const generatedEditDates = {
|
|||||||
"app/commerce-modules/sales-channel/links-to-other-modules/page.mdx": "2024-10-15T14:25:29.097Z",
|
"app/commerce-modules/sales-channel/links-to-other-modules/page.mdx": "2024-10-15T14:25:29.097Z",
|
||||||
"app/commerce-modules/stock-location/links-to-other-modules/page.mdx": "2024-10-15T14:33:11.483Z",
|
"app/commerce-modules/stock-location/links-to-other-modules/page.mdx": "2024-10-15T14:33:11.483Z",
|
||||||
"app/commerce-modules/store/links-to-other-modules/page.mdx": "2024-06-26T07:19:49.931Z",
|
"app/commerce-modules/store/links-to-other-modules/page.mdx": "2024-06-26T07:19:49.931Z",
|
||||||
"app/examples/page.mdx": "2024-12-09T16:19:18.598Z",
|
"app/examples/page.mdx": "2024-12-11T09:07:47.589Z",
|
||||||
"app/medusa-cli/commands/build/page.mdx": "2024-11-11T11:00:49.665Z",
|
"app/medusa-cli/commands/build/page.mdx": "2024-11-11T11:00:49.665Z",
|
||||||
"app/js-sdk/page.mdx": "2024-10-16T12:12:34.512Z",
|
"app/js-sdk/page.mdx": "2024-10-16T12:12:34.512Z",
|
||||||
"references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.apiKey/page.mdx": "2024-12-09T13:21:58.136Z",
|
"references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.apiKey/page.mdx": "2024-12-09T13:21:58.136Z",
|
||||||
@@ -5687,5 +5687,6 @@ export const generatedEditDates = {
|
|||||||
"references/modules/sales_channel_models/page.mdx": "2024-12-10T14:55:13.205Z",
|
"references/modules/sales_channel_models/page.mdx": "2024-12-10T14:55:13.205Z",
|
||||||
"references/types/DmlTypes/types/types.DmlTypes.KnownDataTypes/page.mdx": "2024-12-10T14:54:55.434Z",
|
"references/types/DmlTypes/types/types.DmlTypes.KnownDataTypes/page.mdx": "2024-12-10T14:54:55.434Z",
|
||||||
"references/types/DmlTypes/types/types.DmlTypes.RelationshipTypes/page.mdx": "2024-12-10T14:54:55.435Z",
|
"references/types/DmlTypes/types/types.DmlTypes.RelationshipTypes/page.mdx": "2024-12-10T14:54:55.435Z",
|
||||||
"app/recipes/commerce-automation/restock-notification/page.mdx": "2024-12-10T14:15:39.178Z"
|
"app/recipes/commerce-automation/restock-notification/page.mdx": "2024-12-11T08:47:27.471Z",
|
||||||
|
"app/troubleshooting/workflow-errors/page.mdx": "2024-12-11T08:44:36.598Z"
|
||||||
}
|
}
|
||||||
@@ -1019,6 +1019,10 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/troubleshooting/s3/page.mdx",
|
"filePath": "/www/apps/resources/app/troubleshooting/s3/page.mdx",
|
||||||
"pathname": "/troubleshooting/s3"
|
"pathname": "/troubleshooting/s3"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"filePath": "/www/apps/resources/app/troubleshooting/workflow-errors/page.mdx",
|
||||||
|
"pathname": "/troubleshooting/workflow-errors"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/usage/page.mdx",
|
"filePath": "/www/apps/resources/app/usage/page.mdx",
|
||||||
"pathname": "/usage"
|
"pathname": "/usage"
|
||||||
|
|||||||
@@ -9309,6 +9309,14 @@ export const generatedSidebar = [
|
|||||||
"path": "/troubleshooting/dist-imports",
|
"path": "/troubleshooting/dist-imports",
|
||||||
"title": "Importing from /dist",
|
"title": "Importing from /dist",
|
||||||
"children": []
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"loaded": true,
|
||||||
|
"isPathHref": true,
|
||||||
|
"type": "link",
|
||||||
|
"path": "/troubleshooting/workflow-errors",
|
||||||
|
"title": "Workflow Errors",
|
||||||
|
"children": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2249,6 +2249,11 @@ export const sidebar = sidebarAttachHrefCommonOptions([
|
|||||||
path: "/troubleshooting/dist-imports",
|
path: "/troubleshooting/dist-imports",
|
||||||
title: "Importing from /dist",
|
title: "Importing from /dist",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
path: "/troubleshooting/workflow-errors",
|
||||||
|
title: "Workflow Errors",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user