@picked/client 0.0.98-next.1 → 0.0.98-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/{api-BgPyneD7.cjs → api-DKvG5BpW.cjs} +1 -1
- package/dist/{api-CONFKApU.js → api-Du1CXHwf.js} +1 -1
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
- package/dist/cjs/server/index.js +1 -1
- package/dist/docs/how-to/call-an-external-api-from-a-workflow.md +26 -3
- package/dist/docs/how-to/register-an-action-handler.md +19 -1
- package/dist/docs/how-to/wait-for-an-action-response.md +182 -0
- package/dist/docs/reference/dashboard/actions.mdx +5 -2
- package/dist/docs/reference/dashboard/external-apis.md +61 -29
- package/dist/docs/reference/dashboard/workflow-runs.md +30 -5
- package/dist/docs/reference/dashboard/workflow-test-runs.md +9 -5
- package/dist/docs/reference/delivery-api/workspace-schema.md +3 -0
- package/dist/docs/reference/sdk/actions.md +127 -3
- package/dist/docs/reference/sdk/type-generation.md +8 -0
- package/dist/esm/index.js +514 -328
- package/dist/esm/plugins/dynamic-experiences/index.js +4 -4
- package/dist/esm/server/index.js +3 -3
- package/dist/{experience-instance-DptBuxBJ.js → experience-instance-4KUBH8Gb.js} +278 -220
- package/dist/experience-instance-C9WV52iQ.cjs +1 -0
- package/dist/plugin-CsE8tmaX.cjs +1 -0
- package/dist/{plugin-BZg0v-pk.js → plugin-CttEcyFL.js} +96 -69
- package/dist/{preview-unavailable-zegsv152.js → preview-unavailable-ByX1cyKS.js} +1 -1
- package/dist/{preview-unavailable-BC8sZ_nk.cjs → preview-unavailable-DlWDUdpa.cjs} +1 -1
- package/dist/{provider-reK0A8tr.cjs → provider-B1TabtRC.cjs} +1 -1
- package/dist/{provider-DHsKj_FK.js → provider-DdRIjrTz.js} +1 -1
- package/dist/types/entities/action/action.d.ts +94 -23
- package/dist/types/entities/action/action.d.ts.map +1 -1
- package/dist/types/entities/experience-instance/experience-instance.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lifecycle/rules-logic.d.ts.map +1 -1
- package/dist/types/picked.d.ts +26 -6
- package/dist/types/picked.d.ts.map +1 -1
- package/dist/types/plugins/dynamic-experiences/index.d.ts +1 -0
- package/dist/types/plugins/dynamic-experiences/index.d.ts.map +1 -1
- package/dist/types/plugins/dynamic-experiences/plugin.d.ts.map +1 -1
- package/dist/types/schema-registry.d.ts +10 -0
- package/dist/types/schema-registry.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/experience-instance-CQvQHCNe.cjs +0 -1
- package/dist/plugin-CuYWwfVi.cjs +0 -1
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: how-to
|
|
3
|
+
layout: docs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# How to wait for the response of an action
|
|
7
|
+
|
|
8
|
+
This guide shows you how to run an action from your application and show its response. You
|
|
9
|
+
need an action in your project first, with a key and a workflow that the action starts. See
|
|
10
|
+
[Actions](/reference/dashboard/actions).
|
|
11
|
+
|
|
12
|
+
The response is the value that the action returns. For an action that starts a workflow in
|
|
13
|
+
Picked, the response is the payload of the **Action response** node in the workflow.
|
|
14
|
+
|
|
15
|
+
## 1. Generate the types of your project
|
|
16
|
+
|
|
17
|
+
Run type generation. The generated types name the actions that start a workflow. For those
|
|
18
|
+
keys, the types of `useAction` and `getAction` include `run`. At runtime, the value includes
|
|
19
|
+
`run` for each key. See [Type generation](/reference/sdk/type-generation).
|
|
20
|
+
|
|
21
|
+
## 2. Bind the action to a component
|
|
22
|
+
|
|
23
|
+
Call `useAction` in React, or `getAction` in Svelte. Neither runs the action. The action runs
|
|
24
|
+
only when you call `run`. The action receives the arguments of `run` in `args`.
|
|
25
|
+
|
|
26
|
+
All the components that use the same key and the same experience share one run state. When
|
|
27
|
+
one of them calls `run`, all of them show that run.
|
|
28
|
+
|
|
29
|
+
:::code-group
|
|
30
|
+
|
|
31
|
+
```tsx [React]
|
|
32
|
+
import { useAction } from '@picked/react/hooks'
|
|
33
|
+
|
|
34
|
+
function CallbackButton() {
|
|
35
|
+
const callback = useAction('request-callback')
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<button onClick={() => void callback.run()} disabled={callback.status === 'running'}>
|
|
39
|
+
Get a call back
|
|
40
|
+
</button>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```svelte [Svelte]
|
|
46
|
+
<script lang="ts">
|
|
47
|
+
import { getAction } from '@picked/svelte/hooks'
|
|
48
|
+
|
|
49
|
+
const callback = getAction('request-callback')
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<button onclick={() => $callback.run()} disabled={$callback.status === 'running'}>
|
|
53
|
+
Get a call back
|
|
54
|
+
</button>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
:::
|
|
58
|
+
|
|
59
|
+
## 3. Show the response
|
|
60
|
+
|
|
61
|
+
Read `status`, `response` and `error` for the last run. Compare `response` with the values
|
|
62
|
+
that your workflow sends.
|
|
63
|
+
|
|
64
|
+
:::code-group
|
|
65
|
+
|
|
66
|
+
```tsx [React]
|
|
67
|
+
import { useAction } from '@picked/react/hooks'
|
|
68
|
+
|
|
69
|
+
// Reads the status that the workflow sends in its response.
|
|
70
|
+
function answerStatus(response: unknown): unknown {
|
|
71
|
+
return typeof response === 'object' && response !== null && 'status' in response
|
|
72
|
+
? response.status
|
|
73
|
+
: undefined
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function CallbackButton() {
|
|
77
|
+
const callback = useAction('request-callback')
|
|
78
|
+
|
|
79
|
+
if (callback.status === 'failed') return <p>We could not send your request.</p>
|
|
80
|
+
if (callback.status === 'succeeded') {
|
|
81
|
+
if (answerStatus(callback.response) === 'closed') {
|
|
82
|
+
return <p>We call back on weekdays, 8 am to 5 pm.</p>
|
|
83
|
+
}
|
|
84
|
+
return <p>Thank you. We will call you soon.</p>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<button onClick={() => void callback.run()} disabled={callback.status === 'running'}>
|
|
89
|
+
Get a call back
|
|
90
|
+
</button>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```svelte [Svelte]
|
|
96
|
+
<script lang="ts">
|
|
97
|
+
import { getAction } from '@picked/svelte/hooks'
|
|
98
|
+
|
|
99
|
+
const callback = getAction('request-callback')
|
|
100
|
+
|
|
101
|
+
// Reads the status that the workflow sends in its response.
|
|
102
|
+
function answerStatus(response: unknown): unknown {
|
|
103
|
+
return typeof response === 'object' && response !== null && 'status' in response
|
|
104
|
+
? response.status
|
|
105
|
+
: undefined
|
|
106
|
+
}
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
{#if $callback.status === 'failed'}
|
|
110
|
+
<p>We could not send your request.</p>
|
|
111
|
+
{:else if $callback.status === 'succeeded' && answerStatus($callback.response) === 'closed'}
|
|
112
|
+
<p>We call back on weekdays, 8 am to 5 pm.</p>
|
|
113
|
+
{:else if $callback.status === 'succeeded'}
|
|
114
|
+
<p>Thank you. We will call you soon.</p>
|
|
115
|
+
{:else}
|
|
116
|
+
<button onclick={() => $callback.run()} disabled={$callback.status === 'running'}>
|
|
117
|
+
Get a call back
|
|
118
|
+
</button>
|
|
119
|
+
{/if}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
:::
|
|
123
|
+
|
|
124
|
+
`run` does not reject. It resolves to `{ status: 'succeeded', response }` or to
|
|
125
|
+
`{ status: 'failed', error }`.
|
|
126
|
+
|
|
127
|
+
## 4. Run the action outside a component
|
|
128
|
+
|
|
129
|
+
Call `client.actions.run` with the action key. It resolves to the response, and it rejects
|
|
130
|
+
when the action fails.
|
|
131
|
+
|
|
132
|
+
The client registers the actions of your project when it loads the configuration of your
|
|
133
|
+
project. For this, the client must have `dynamicExperiencesPlugin`, and you must start the
|
|
134
|
+
client. Until the client is ready, `run` rejects with `Action '<key>' is not registered`. A
|
|
135
|
+
handler that your application registers in `createClient({ actions })` is available
|
|
136
|
+
immediately.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { createClient } from 'picked'
|
|
140
|
+
import { dynamicExperiencesPlugin } from '@picked/client/plugins/dynamic-experiences'
|
|
141
|
+
|
|
142
|
+
const picked = createClient({
|
|
143
|
+
projectId: 'your-project-id',
|
|
144
|
+
plugins: [dynamicExperiencesPlugin()],
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
picked.start()
|
|
148
|
+
|
|
149
|
+
// Waits until the client is ready.
|
|
150
|
+
async function whenReady(): Promise<void> {
|
|
151
|
+
if (picked.readyStore.getState()) return
|
|
152
|
+
|
|
153
|
+
await new Promise<void>((resolve) => {
|
|
154
|
+
const subscription = picked.readyStore.updates.watch((ready) => {
|
|
155
|
+
if (!ready) return
|
|
156
|
+
subscription.unsubscribe()
|
|
157
|
+
resolve()
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function requestCallback(): Promise<void> {
|
|
163
|
+
await whenReady()
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const response = await picked.actions.run('request-callback', { timeoutMs: 15_000 })
|
|
167
|
+
console.info('Callback response', response)
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.warn('Callback request failed', error)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Set `timeoutMs` to stop the wait sooner. Set `signal` to stop the wait from your own code.
|
|
175
|
+
|
|
176
|
+
## Related
|
|
177
|
+
|
|
178
|
+
- [SDK actions](/reference/sdk/actions) — every outcome, error and limit.
|
|
179
|
+
- [How to register an action handler](/how-to/register-an-action-handler) — return a
|
|
180
|
+
response from your own handler.
|
|
181
|
+
- [How to call an external API from a workflow](/how-to/call-an-external-api-from-a-workflow)
|
|
182
|
+
— a workflow that sends a response.
|
|
@@ -24,8 +24,11 @@ not see it.
|
|
|
24
24
|
|
|
25
25
|
## Register in your application
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
A published action that starts a workflow needs no code in your application. The SDK
|
|
28
|
+
sends the request to Picked, and the workflow runs there.
|
|
29
|
+
|
|
30
|
+
To run your own code, pass a handler under the action key in `createClient()`. That handler
|
|
31
|
+
replaces the workflow for this key. See
|
|
29
32
|
[How to register an action handler](/how-to/register-an-action-handler) for the code, and
|
|
30
33
|
[SDK actions](/reference/sdk/actions) for what the handler receives and returns.
|
|
31
34
|
|
|
@@ -10,37 +10,37 @@ An external API is an origin that this project's workflows are allowed to call w
|
|
|
10
10
|
|
|
11
11
|
## Registration
|
|
12
12
|
|
|
13
|
-
| Field
|
|
14
|
-
|
|
|
15
|
-
| Display name
|
|
16
|
-
| Origin
|
|
17
|
-
| Authorization header | Optional. A header name and a value. The value is encrypted and is never returned. The dashboard shows the header name and a mask.
|
|
13
|
+
| Field | Rule |
|
|
14
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
15
|
+
| Display name | Required. 1 to 120 characters. For you only; it does not go out with the request. |
|
|
16
|
+
| Origin | Required. The scheme, the host and the optional port, with no path, query, fragment or sign-in details. `http` or `https` only, on port 80 or 443. Must not point at a private, loopback or link-local address. One live registration per origin per project. |
|
|
17
|
+
| Authorization header | Optional. A header name and a value. The value is encrypted and is never returned. The dashboard shows the header name and a mask. |
|
|
18
18
|
|
|
19
19
|
To change an origin, delete the registration and register the new origin. The origin is
|
|
20
20
|
the registration's identity.
|
|
21
21
|
|
|
22
22
|
## Node arguments
|
|
23
23
|
|
|
24
|
-
| Argument
|
|
25
|
-
|
|
|
26
|
-
| `method`
|
|
27
|
-
| `url`
|
|
28
|
-
| `headers`
|
|
29
|
-
| `body`
|
|
30
|
-
| `timeoutMs`
|
|
31
|
-
| `retry`
|
|
32
|
-
| `failOnErrorStatus` | on
|
|
33
|
-
| `recordBody`
|
|
24
|
+
| Argument | Default | Rule |
|
|
25
|
+
| ------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| `method` | — | Required. `GET`, `HEAD`, `POST`, `PUT`, `PATCH` or `DELETE`. |
|
|
27
|
+
| `url` | — | Required. The full address. Its origin must be registered to this project. |
|
|
28
|
+
| `headers` | none | A JSON object of headers to send. Picked refuses a credential here when you save. |
|
|
29
|
+
| `body` | none | The JSON body. Used by `POST`, `PUT` and `PATCH`. |
|
|
30
|
+
| `timeoutMs` | 10000 | 1000 to 30000. The time that the whole call has, with every retry. |
|
|
31
|
+
| `retry` | on for `GET`, `HEAD`, `PUT` and `DELETE`; off for `POST` and `PATCH` | Whether to send the request again after a failed connection, an unknown host, or a status of 408, 429 or 500 to 599. |
|
|
32
|
+
| `failOnErrorStatus` | on | Whether a status outside 200 to 299 fails the node. |
|
|
33
|
+
| `recordBody` | on | Whether to record the answer's body. |
|
|
34
34
|
|
|
35
35
|
## Output
|
|
36
36
|
|
|
37
|
-
| Name
|
|
38
|
-
|
|
|
39
|
-
| `status`
|
|
40
|
-
| `ok`
|
|
41
|
-
| `body`
|
|
42
|
-
| `bodyTruncated` | True when the answer was too large to record in full.
|
|
43
|
-
| `durationMs`
|
|
37
|
+
| Name | Value |
|
|
38
|
+
| --------------- | ------------------------------------------------------------------------ |
|
|
39
|
+
| `status` | The HTTP status. |
|
|
40
|
+
| `ok` | True for a status from 200 to 299. |
|
|
41
|
+
| `body` | The parsed JSON answer, or null when it is not JSON or was not recorded. |
|
|
42
|
+
| `bodyTruncated` | True when the answer was too large to record in full. |
|
|
43
|
+
| `durationMs` | The time the call took. |
|
|
44
44
|
|
|
45
45
|
## Limits
|
|
46
46
|
|
|
@@ -50,16 +50,48 @@ the registration's identity.
|
|
|
50
50
|
new address.
|
|
51
51
|
- Picked sends a request three times at most. Picked waits longer before each retry, and
|
|
52
52
|
waits for the time that a `Retry-After` header gives.
|
|
53
|
+
- A timeout uses all of `timeoutMs`, so Picked does not send the request again after a
|
|
54
|
+
timeout.
|
|
53
55
|
|
|
54
56
|
## Errors
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
When the node fails, the run stops at the node. If the call fails and an edge leaves the
|
|
59
|
+
node's error exit, the run continues on that edge. These failures stop the run at the
|
|
60
|
+
node, with or without that edge:
|
|
61
|
+
|
|
62
|
+
- A required argument has no value.
|
|
63
|
+
- An argument has a value that the tool does not accept. For example, the value has the
|
|
64
|
+
wrong type, `method` is not one of the six methods, or `timeoutMs` is not from
|
|
65
|
+
1000 to 30000.
|
|
66
|
+
- A template cannot be rendered.
|
|
67
|
+
- The workflow does not have a scope that the tool needs.
|
|
68
|
+
- An error occurs in Picked.
|
|
69
|
+
|
|
70
|
+
A later node reads why the call failed from `nodes.<node-id>.error`:
|
|
71
|
+
|
|
72
|
+
| Field | Value |
|
|
73
|
+
| ----------- | -------------------------------------------------------- |
|
|
74
|
+
| `code` | `http_failed`. |
|
|
75
|
+
| `message` | The node, the tool and the message from the table below. |
|
|
76
|
+
| `reason` | The reason from the table below. |
|
|
77
|
+
| `retryable` | True when the same request can pass at a later time. |
|
|
78
|
+
| `status` | The HTTP status, for the reason `error_status` only. |
|
|
79
|
+
|
|
80
|
+
After an error status, `nodes.<node-id>.status`, `ok` and `body` also keep the answer.
|
|
81
|
+
|
|
82
|
+
| Reason | Message ends with | Retryable | When |
|
|
83
|
+
| ------------------------ | ----------------------------------------------------- | --------------------------- | ----------------------------------------------------------------- |
|
|
84
|
+
| `error_status` | `the API answered <status>` | For 408, 429 and 500 to 599 | The status was outside 200 to 299, and `failOnErrorStatus` is on. |
|
|
85
|
+
| `timeout` | `no answer within the node's timeout` | Yes | Nothing answered within `timeoutMs`. |
|
|
86
|
+
| `transport` | `the request could not be delivered` | Yes | The connection failed. |
|
|
87
|
+
| `unresolvable` | `the destination hostname did not resolve` | Yes | No address was found for the host. |
|
|
88
|
+
| `refused` | `the destination was refused` | No | The URL points at a private, loopback or link-local address. |
|
|
89
|
+
| `refused` | `no external API is registered for <origin>` | No | The URL's origin has no live registration in this project. |
|
|
90
|
+
| `request_too_large` | `the request body is over 1 MiB` | No | The body is larger than the limit. |
|
|
91
|
+
| `invalid_request` | `the request is not one the tool can send` | No | The arguments do not make a valid request. |
|
|
92
|
+
| `invalid_request` | `the URL is not one the tool can send` | No | The tool cannot send to the URL. |
|
|
93
|
+
| `invalid_headers` | The header that Picked refused | No | A header name or value is not valid for HTTP. |
|
|
94
|
+
| `credential_unavailable` | `the credential for this external API is unavailable` | No | Picked cannot read the registration's authorization header. |
|
|
63
95
|
|
|
64
96
|
Picked also refuses a workflow when you save it if a node holds a credential in
|
|
65
97
|
`headers`. Register the external API, and put the credential on the registration.
|
|
@@ -22,11 +22,36 @@ you did not publish does not start runs.
|
|
|
22
22
|
|
|
23
23
|
## Run status
|
|
24
24
|
|
|
25
|
-
| Status | Meaning
|
|
26
|
-
| ----------- |
|
|
27
|
-
| `Running` | The run started and did not stop yet. A **Wait** node can keep a run in this status for days.
|
|
28
|
-
| `Completed` | Each node that the run reached did its work.
|
|
29
|
-
| `Failed` | A node failed. The run stopped at that node. The run shows the reason.
|
|
25
|
+
| Status | Meaning |
|
|
26
|
+
| ----------- | ----------------------------------------------------------------------------------------------- |
|
|
27
|
+
| `Running` | The run started and did not stop yet. A **Wait** node can keep a run in this status for days. |
|
|
28
|
+
| `Completed` | Each node that the run reached did its work, or failed and the run continued on its error exit. |
|
|
29
|
+
| `Failed` | A node failed. The run stopped at that node. The run shows the reason. |
|
|
30
|
+
|
|
31
|
+
A tool node that fails with an edge on its error exit shows `Failed` on its task. The run
|
|
32
|
+
continues on that edge only when the tool itself fails:
|
|
33
|
+
|
|
34
|
+
- An External API call fails. Refer to the [External APIs reference](/reference/dashboard/external-apis#errors).
|
|
35
|
+
- Another tool refuses the request. For example, a label value is not a number for a
|
|
36
|
+
`number` label, or the lead does not exist.
|
|
37
|
+
|
|
38
|
+
When the tool refuses the request, the error record has the code `tool_failed` and a
|
|
39
|
+
reason from the tool, for example `value_invalid_for_type` or `not_found`.
|
|
40
|
+
|
|
41
|
+
Other failures stop the run, with or without an edge on the error exit:
|
|
42
|
+
|
|
43
|
+
- A required argument has no value.
|
|
44
|
+
- An argument has a value that the tool does not accept:
|
|
45
|
+
- The value has the wrong type.
|
|
46
|
+
- The value is not one of the options.
|
|
47
|
+
- A number is not in the range that the tool accepts.
|
|
48
|
+
- A label namespace or key does not have the correct format.
|
|
49
|
+
- Two labels have the same namespace and key.
|
|
50
|
+
- A list of labels is empty.
|
|
51
|
+
- A template cannot be rendered.
|
|
52
|
+
- The workflow does not have a scope that the tool needs.
|
|
53
|
+
- The tool gives output that is not valid.
|
|
54
|
+
- An error occurs in Picked.
|
|
30
55
|
|
|
31
56
|
## Records
|
|
32
57
|
|
|
@@ -51,11 +51,15 @@ A later node that reads the output of a skipped node gets no value.
|
|
|
51
51
|
|
|
52
52
|
## Result
|
|
53
53
|
|
|
54
|
-
| Status | Meaning
|
|
55
|
-
| ----------- |
|
|
56
|
-
| `Succeeded` | The node did its work.
|
|
57
|
-
| `Skipped` | The tool writes, so the test run did not run it.
|
|
58
|
-
| `Failed` | The node failed
|
|
54
|
+
| Status | Meaning |
|
|
55
|
+
| ----------- | ------------------------------------------------ |
|
|
56
|
+
| `Succeeded` | The node did its work. |
|
|
57
|
+
| `Skipped` | The tool writes, so the test run did not run it. |
|
|
58
|
+
| `Failed` | The node failed, and shows the reason. |
|
|
59
|
+
|
|
60
|
+
When a node fails, the test run stops at that node and shows `Failed`. If an edge leaves
|
|
61
|
+
the error exit of the node, the test run continues on that edge as a run does, and can
|
|
62
|
+
show `Completed`. Refer to the [workflow runs reference](/reference/dashboard/workflow-runs#run-status).
|
|
59
63
|
|
|
60
64
|
Each node shows its input and its output. A test run does not show in **Runs**, and it
|
|
61
65
|
does not create or change leads or labels.
|
|
@@ -28,13 +28,16 @@ The response is a [JSON Schema draft 2020-12](https://json-schema.org/draft/2020
|
|
|
28
28
|
| `x-picked-source` | `"published"` or `"preview"` — mirrors the surface you requested. |
|
|
29
29
|
| `x-picked-version-id` | Published workspace version id, or `null` on preview. |
|
|
30
30
|
| `x-picked-step-content-models` | Map of step key → content model key, which gives each layout its type. |
|
|
31
|
+
| `x-picked-step-fields` | Map of step key → the keys of the fields on that step, in order. |
|
|
31
32
|
| `x-picked-feature-values` | Map of feature key → the values that the feature can serve. Picked adds this field only when the workspace has a Text feature flag. |
|
|
33
|
+
| `x-picked-workflow-action-keys` | Keys of the actions that start a workflow, sorted. Picked adds this field only when the workspace has such an action. |
|
|
32
34
|
| `$defs.contentModels` | JSON Schema definitions for each content model. |
|
|
33
35
|
| `$defs.contentBlocks` | JSON Schema definitions for reusable content blocks. |
|
|
34
36
|
| `$defs.fields` | Object schema for project field values. |
|
|
35
37
|
| `$defs.featureKeys` | String enum of feature keys. |
|
|
36
38
|
| `$defs.experienceKeys` | String enum of experience keys. |
|
|
37
39
|
| `$defs.stepKeys` | String enum of step keys. |
|
|
40
|
+
| `$defs.actionKeys` | String enum of action keys. A schema from a version that Picked published before this field existed has none. |
|
|
38
41
|
|
|
39
42
|
## Field values
|
|
40
43
|
|
|
@@ -5,8 +5,23 @@ layout: docs
|
|
|
5
5
|
|
|
6
6
|
# Actions
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
An action is a function that the SDK runs by its key. An action comes from one of two
|
|
9
|
+
places:
|
|
10
|
+
|
|
11
|
+
- **Your application** registers a handler in `createClient({ actions })`.
|
|
12
|
+
- **Your project** publishes the action. Its handler sends the visitor's answers to Picked,
|
|
13
|
+
and the workflow of the action runs there.
|
|
14
|
+
|
|
15
|
+
A handler from your application replaces a published action that has the same key. The
|
|
16
|
+
workflow then does not start.
|
|
17
|
+
|
|
18
|
+
## Response
|
|
19
|
+
|
|
20
|
+
The value that a handler returns is the response of the action. A handler can return any
|
|
21
|
+
value, or a promise of any value.
|
|
22
|
+
|
|
23
|
+
The response of a published action is the payload of the **Action response** node in its
|
|
24
|
+
workflow. A workflow that has no **Action response** node gives `undefined`.
|
|
10
25
|
|
|
11
26
|
## Outcome and branch
|
|
12
27
|
|
|
@@ -19,10 +34,119 @@ An action node can also carry one plain connection, with no success or failure b
|
|
|
19
34
|
flow then continues through it on either outcome.
|
|
20
35
|
|
|
21
36
|
An action does not interrupt your visitor. The current step stays on the screen while an
|
|
22
|
-
async action runs.
|
|
37
|
+
async action runs. While the action runs, the flow ignores a request to go to the next step
|
|
38
|
+
or to a specified step. The outcome of the action then chooses the branch. If your visitor
|
|
39
|
+
goes back before the action completes, the flow stays on that step. The signal that the
|
|
40
|
+
handler receives then stops.
|
|
41
|
+
|
|
42
|
+
## Published actions
|
|
43
|
+
|
|
44
|
+
A published action waits for the response of its workflow. It stops after 30 seconds.
|
|
45
|
+
|
|
46
|
+
| Cause | The action rejects with |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| Picked refuses the request | An `Error` that names the HTTP status |
|
|
49
|
+
| Picked does not accept the request in 25 seconds | A `TimeoutError` |
|
|
50
|
+
| Picked accepts the request, but gives no run to wait for | An `Error` that says that no run is available |
|
|
51
|
+
| The workflow fails before it sends a response | `ActionRunFailedError` |
|
|
52
|
+
| Picked refuses a request for the response with a `4xx` status | An `Error` that names the HTTP status |
|
|
53
|
+
| No response in 30 seconds | `ActionResponseTimeoutError` |
|
|
54
|
+
| Your code stops the wait with a signal | The reason of the signal |
|
|
55
|
+
|
|
56
|
+
A request for the response can fail because of a network error, a timeout, a `5xx` status or
|
|
57
|
+
a body that the SDK cannot read. The SDK then sends the request again. It waits 1 second after
|
|
58
|
+
the first failure, and two times longer after each failure that follows. If the requests
|
|
59
|
+
continue to fail, the 30-second limit stops the wait with `ActionResponseTimeoutError`.
|
|
60
|
+
|
|
61
|
+
To identify `ActionRunFailedError` and `ActionResponseTimeoutError` with `instanceof`, import
|
|
62
|
+
them from `@picked/client/plugins/dynamic-experiences`. The `name` of each error is the same
|
|
63
|
+
as the name that you import.
|
|
64
|
+
|
|
65
|
+
## Run an action from code
|
|
66
|
+
|
|
67
|
+
| API | Returns | Rejects |
|
|
68
|
+
| --- | --- | --- |
|
|
69
|
+
| `client.actions.run(key, options)` | A promise of the response | Yes, with the error of the action |
|
|
70
|
+
| `useAction(key, options)` (React) | For an action that starts a workflow: `{ run, status, response, error }`. For other actions: the action as a function. | `run`: No. The function: it returns or throws what the action returns or throws. |
|
|
71
|
+
| `getAction(key, options)` (Svelte) | A store of the same value | The same as `useAction` |
|
|
72
|
+
| `useActionResult(key, options)` (React) | The last result of your own action | No |
|
|
73
|
+
| `getActionResult(key, options)` (Svelte) | A store of the last result of your own action | No |
|
|
74
|
+
| `client.actions.getActionBinding(key, options)` | A store of the value that `useAction` and `getAction` give | The same as `useAction` |
|
|
75
|
+
| `client.actions.getActionResult(key, options)` | A store of the last result of your own action, which `useActionResult` and `getActionResult` read | No |
|
|
76
|
+
|
|
77
|
+
Type generation names the actions that start a workflow (`WorkflowActionKeys`). Without generated
|
|
78
|
+
types, `useAction` and `getAction` give the action as a function. For each key, the value also
|
|
79
|
+
includes `run`, `status`, `response` and `error`.
|
|
80
|
+
|
|
81
|
+
The handler receives the arguments of a call to the function in `args`. It also receives the
|
|
82
|
+
arguments of `run(...args)` in `args`.
|
|
83
|
+
|
|
84
|
+
`status` is `idle`, `running`, `succeeded` or `failed`, for the last run. When a run starts,
|
|
85
|
+
`response` and `error` are `undefined`. `run(...args)` resolves to
|
|
86
|
+
`{ status: 'succeeded', response }` or to `{ status: 'failed', error }`.
|
|
87
|
+
|
|
88
|
+
The SDK keeps one run state for each key and experience. All the components that use the
|
|
89
|
+
same key and the same experience share that run state, and they show the same last run. When
|
|
90
|
+
one of them calls `run`, all of them show that run.
|
|
91
|
+
|
|
92
|
+
`useActionResult` and `getActionResult` run your own action again when a response, a field or
|
|
93
|
+
the handler of the action changes. They also run it when a component that reads the result
|
|
94
|
+
starts. In React, the action runs when the component mounts, and when an `<Activity>` shows the
|
|
95
|
+
component again. In Svelte, it runs each time a component calls `getActionResult`. A change that
|
|
96
|
+
occurs outside Picked, for example a login, then shows in a component that starts after the
|
|
97
|
+
change.
|
|
98
|
+
|
|
99
|
+
The components that read the same key and the same experience share one result. The action
|
|
100
|
+
runs one time for each change. It also runs one time for the components that React renders
|
|
101
|
+
together. React can stop while it renders and continue later, for example in a transition. Then
|
|
102
|
+
the components that React renders after it continues run the action again.
|
|
103
|
+
|
|
104
|
+
The result is always from the run that started last. If that run throws, the result is
|
|
105
|
+
`undefined`. For a published action they give `undefined` and do not send a request. The types
|
|
106
|
+
refuse the key of an action that starts a workflow.
|
|
107
|
+
|
|
108
|
+
A result for an experience follows the changes only while `client.experienceInstances`
|
|
109
|
+
contains that experience. When `client.experienceInstances` adds the experience, the action
|
|
110
|
+
runs if a response, a field or the handler changed after the last run.
|
|
111
|
+
|
|
112
|
+
The options of `client.actions.run`:
|
|
113
|
+
|
|
114
|
+
| Option | Type | Description |
|
|
115
|
+
| --- | --- | --- |
|
|
116
|
+
| `args` | `unknown[]` | The values that the handler receives in `args`. |
|
|
117
|
+
| `experience` | `ExperienceInstance` | The experience that the handler receives as `context.experience`. |
|
|
118
|
+
| `signal` | `AbortSignal` | Stops the wait. The handler receives a signal that stops too. |
|
|
119
|
+
| `timeoutMs` | `number` | Stops the wait after this many milliseconds, with a `TimeoutError`. |
|
|
120
|
+
|
|
121
|
+
`useAction` and `getAction` take `experience` only.
|
|
122
|
+
|
|
123
|
+
## Other methods of `client.actions`
|
|
124
|
+
|
|
125
|
+
| Method | Description |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `refreshActionResult(key, options)` | Runs your own action again for the result that `getActionResult` gives. It does nothing until a component or your code reads that result. The calls in one task start one run. |
|
|
128
|
+
| `isPublished(key)` | Returns `true` when the handler of the key is a published action. |
|
|
129
|
+
| `unregister(key)` | Removes the handler of the key. If the key has no handler, nothing changes. |
|
|
130
|
+
|
|
131
|
+
`refreshActionResult` and `getActionResult` take `experience` only.
|
|
132
|
+
|
|
133
|
+
## Rule logic
|
|
134
|
+
|
|
135
|
+
A rule reads the response of an action with `{ "invoke": [{ "var": "actions.<key>" }] }`. A
|
|
136
|
+
response that is a string, a number, a Boolean, `null`, or an array or object of these
|
|
137
|
+
reaches the rule. A different response reaches the rule as `undefined`.
|
|
138
|
+
|
|
139
|
+
A rule runs the action each time the SDK evaluates the rule.
|
|
140
|
+
|
|
141
|
+
A condition node can have a rule that runs an action. While the condition node waits for that
|
|
142
|
+
rule, the flow ignores a request to go to the next step or to a specified step. The result of
|
|
143
|
+
the rule then chooses the branch. If your visitor goes back before the rule has a result, the
|
|
144
|
+
flow ignores the result.
|
|
23
145
|
|
|
24
146
|
## Related
|
|
25
147
|
|
|
148
|
+
- [How to wait for the response of an action](/how-to/wait-for-an-action-response) — show
|
|
149
|
+
the response in a component.
|
|
26
150
|
- [How to register an action handler](/how-to/register-an-action-handler) — the handler,
|
|
27
151
|
and when to throw.
|
|
28
152
|
- [About how Picked works](/explanation/how-picked-works#how-an-action-decides-which-way-to-go)
|
|
@@ -244,6 +244,14 @@ Each key type has a `Known` form that holds the workspace keys alone —
|
|
|
244
244
|
`KnownFeatureKeys`, `KnownActionKeys`, `KnownFieldKeys`, `KnownStepKeys`, and
|
|
245
245
|
`KnownExperienceKeys`. Use one where you want the workspace set and nothing else.
|
|
246
246
|
|
|
247
|
+
## Actions that start a workflow
|
|
248
|
+
|
|
249
|
+
`WorkflowActionKeys` holds the keys of the actions that start a workflow in the workspace. For one
|
|
250
|
+
of these keys, the types of `useAction` and `getAction` give `run`, `status`, `response` and
|
|
251
|
+
`error`. For every other key, the types give the action as a function. Without generated types,
|
|
252
|
+
and with a schema that has no such actions, `WorkflowActionKeys` is `never`. See
|
|
253
|
+
[Actions](/reference/sdk/actions).
|
|
254
|
+
|
|
247
255
|
## Cache
|
|
248
256
|
|
|
249
257
|
Type generation writes its artefacts under `.picked/`: the schema JSON and a manifest. The first run adds this directory to `.gitignore` for you.
|