@botonic/plugin-flow-builder 0.57.1-alpha.0 → 0.58.0-alpha.0

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.
Files changed (53) hide show
  1. package/lib/cjs/action/contents-result.d.ts +5 -0
  2. package/lib/cjs/action/contents-result.js +3 -0
  3. package/lib/cjs/action/contents-result.js.map +1 -0
  4. package/lib/cjs/action/first-interaction.js +1 -1
  5. package/lib/cjs/action/first-interaction.js.map +1 -1
  6. package/lib/cjs/action/get-contents.d.ts +3 -2
  7. package/lib/cjs/action/get-contents.js +13 -6
  8. package/lib/cjs/action/get-contents.js.map +1 -1
  9. package/lib/cjs/action/index.d.ts +2 -0
  10. package/lib/cjs/action/index.js +16 -6
  11. package/lib/cjs/action/index.js.map +1 -1
  12. package/lib/cjs/action/payload.d.ts +2 -2
  13. package/lib/cjs/action/payload.js +12 -28
  14. package/lib/cjs/action/payload.js.map +1 -1
  15. package/lib/cjs/action/push-flow-resolver.d.ts +22 -0
  16. package/lib/cjs/action/push-flow-resolver.js +66 -0
  17. package/lib/cjs/action/push-flow-resolver.js.map +1 -0
  18. package/lib/cjs/api.d.ts +1 -1
  19. package/lib/cjs/api.js +7 -2
  20. package/lib/cjs/api.js.map +1 -1
  21. package/lib/cjs/content-fields/flow-do-nothing.js +1 -1
  22. package/lib/cjs/content-fields/flow-do-nothing.js.map +1 -1
  23. package/lib/esm/action/contents-result.d.ts +5 -0
  24. package/lib/esm/action/contents-result.js +3 -0
  25. package/lib/esm/action/contents-result.js.map +1 -0
  26. package/lib/esm/action/first-interaction.js +1 -1
  27. package/lib/esm/action/first-interaction.js.map +1 -1
  28. package/lib/esm/action/get-contents.d.ts +3 -2
  29. package/lib/esm/action/get-contents.js +13 -6
  30. package/lib/esm/action/get-contents.js.map +1 -1
  31. package/lib/esm/action/index.d.ts +2 -0
  32. package/lib/esm/action/index.js +16 -6
  33. package/lib/esm/action/index.js.map +1 -1
  34. package/lib/esm/action/payload.d.ts +2 -2
  35. package/lib/esm/action/payload.js +12 -28
  36. package/lib/esm/action/payload.js.map +1 -1
  37. package/lib/esm/action/push-flow-resolver.d.ts +22 -0
  38. package/lib/esm/action/push-flow-resolver.js +66 -0
  39. package/lib/esm/action/push-flow-resolver.js.map +1 -0
  40. package/lib/esm/api.d.ts +1 -1
  41. package/lib/esm/api.js +7 -2
  42. package/lib/esm/api.js.map +1 -1
  43. package/lib/esm/content-fields/flow-do-nothing.js +1 -1
  44. package/lib/esm/content-fields/flow-do-nothing.js.map +1 -1
  45. package/package.json +2 -2
  46. package/src/action/contents-result.ts +6 -0
  47. package/src/action/first-interaction.ts +7 -6
  48. package/src/action/get-contents.ts +17 -8
  49. package/src/action/index.tsx +40 -6
  50. package/src/action/payload.ts +16 -46
  51. package/src/action/push-flow-resolver.ts +101 -0
  52. package/src/api.ts +13 -3
  53. package/src/content-fields/flow-do-nothing.tsx +1 -1
@@ -1,25 +1,22 @@
1
1
  import { EventAction, type EventFeedback, storeCaseRating } from '@botonic/core'
2
2
  import { v7 as uuid } from 'uuid'
3
-
4
- import {
5
- AGENT_RATING_PAYLOAD,
6
- PUSH_FLOW_PAYLOAD,
7
- SEPARATOR,
8
- } from '../constants'
9
- import type { FlowContent } from '../content-fields'
3
+ import { AGENT_RATING_PAYLOAD, PUSH_FLOW_PAYLOAD } from '../constants'
10
4
  import type { HtNodeWithContent } from '../content-fields/hubtype-fields'
11
5
  import { trackEvent } from '../tracking'
6
+ import type { FlowBuilderContentsResult } from './contents-result'
12
7
  import type { FlowBuilderContext } from './context'
8
+ import { PushFlowResolver } from './push-flow-resolver'
13
9
 
14
10
  export async function getContentsByPayload(
15
11
  context: FlowBuilderContext
16
- ): Promise<FlowContent[]> {
12
+ ): Promise<FlowBuilderContentsResult> {
17
13
  const { cmsApi, flowBuilderPlugin, request, contentID } = context
18
14
  if (request.input.payload?.startsWith(AGENT_RATING_PAYLOAD)) {
19
15
  return await resolveRatingPayload(context)
20
16
  }
21
17
  if (request.input.payload?.startsWith(PUSH_FLOW_PAYLOAD)) {
22
- return await resolvePushFlowPayload(context)
18
+ const pushFlow = new PushFlowResolver(context)
19
+ return await pushFlow.getContents()
23
20
  }
24
21
 
25
22
  const id = contentID
@@ -28,19 +25,21 @@ export async function getContentsByPayload(
28
25
  const targetNode = id ? cmsApi.getNodeById<HtNodeWithContent>(id) : undefined
29
26
 
30
27
  if (targetNode) {
31
- return await flowBuilderPlugin.getContentsByNode(targetNode)
28
+ return {
29
+ contents: await flowBuilderPlugin.getContentsByNode(targetNode),
30
+ }
32
31
  }
33
32
 
34
- return []
33
+ return { contents: [] }
35
34
  }
36
35
 
37
36
  async function resolveRatingPayload(
38
37
  context: FlowBuilderContext
39
- ): Promise<FlowContent[]> {
38
+ ): Promise<FlowBuilderContentsResult> {
40
39
  const { cmsApi, flowBuilderPlugin, request } = context
41
40
 
42
41
  if (!request.input.payload) {
43
- return []
42
+ return { contents: [] }
44
43
  }
45
44
 
46
45
  const { target, text, value, possibleOptions, possibleValues } =
@@ -66,39 +65,10 @@ async function resolveRatingPayload(
66
65
  : undefined
67
66
 
68
67
  if (targetNode) {
69
- return await flowBuilderPlugin.getContentsByNode(targetNode)
70
- }
71
-
72
- return []
73
- }
74
-
75
- async function resolvePushFlowPayload(
76
- context: FlowBuilderContext
77
- ): Promise<FlowContent[]> {
78
- const { cmsApi, flowBuilderPlugin, request } = context
79
-
80
- if (!request.input.payload) {
81
- return []
82
- }
83
-
84
- const parts = request.input.payload.split(SEPARATOR)
85
- const pushFlowId = parts.length > 1 ? parts[1] : ''
86
-
87
- if (!pushFlowId) {
88
- return []
89
- }
90
-
91
- try {
92
- const pushFlowNode =
93
- cmsApi.getNodeByCampaignId<HtNodeWithContent>(pushFlowId)
94
-
95
- if (!pushFlowNode) {
96
- return []
68
+ return {
69
+ contents: await flowBuilderPlugin.getContentsByNode(targetNode),
97
70
  }
98
-
99
- return await flowBuilderPlugin.getContentsByNode(pushFlowNode)
100
- } catch {
101
- // Campaign not found - return empty to trigger fallback
102
- return []
103
71
  }
72
+
73
+ return { contents: [] }
104
74
  }
@@ -0,0 +1,101 @@
1
+ import type { BotContext } from '@botonic/core'
2
+ import type { FlowBuilderApi } from '../api'
3
+ import { SEPARATOR } from '../constants'
4
+ import type { HtNodeWithContent } from '../content-fields/hubtype-fields'
5
+ import type BotonicPluginFlowBuilder from '../index'
6
+ import type { FlowBuilderContentsResult } from './contents-result'
7
+ import type { FlowBuilderContext } from './context'
8
+
9
+ export enum PushFlowErrorCode {
10
+ NoInputPayload = 'NoInputPayload',
11
+ NoPushFlowId = 'NoPushFlowId',
12
+ NoCampaignStartNode = 'NoCampaignStartNode',
13
+ NoCampaignId = 'NoCampaignId',
14
+ }
15
+
16
+ export class PushFlowError extends Error {
17
+ readonly code: PushFlowErrorCode
18
+
19
+ constructor(code: PushFlowErrorCode, reason: string) {
20
+ super(`push-flow failed: ${reason}`)
21
+ this.code = code
22
+ this.name = 'PushFlowError'
23
+ }
24
+ }
25
+
26
+ export class PushFlowResolver {
27
+ private botContext: BotContext
28
+ private cmsApi: FlowBuilderApi
29
+ private flowBuilderPlugin: BotonicPluginFlowBuilder
30
+
31
+ constructor(context: FlowBuilderContext) {
32
+ this.botContext = context.request
33
+ this.cmsApi = context.cmsApi
34
+ this.flowBuilderPlugin = context.flowBuilderPlugin
35
+ }
36
+
37
+ async getContents(): Promise<FlowBuilderContentsResult> {
38
+ const pushFlowId = this.getPushFlowId()
39
+ const pushFlowNode = this.getPushFlowNode(pushFlowId)
40
+ const campaignId = this.getCampaignId()
41
+
42
+ return {
43
+ contents: await this.flowBuilderPlugin.getContentsByNode(pushFlowNode),
44
+ campaignFirstMessageId: campaignId,
45
+ }
46
+ }
47
+
48
+ private getPushFlowId(): string {
49
+ const payload = this.botContext.input.payload
50
+
51
+ if (!payload) {
52
+ throw new PushFlowError(
53
+ PushFlowErrorCode.NoInputPayload,
54
+ 'No payload found in input'
55
+ )
56
+ }
57
+
58
+ const parts = payload.split(SEPARATOR)
59
+ const pushFlowId = parts.length > 1 ? parts[1] : ''
60
+
61
+ if (!pushFlowId) {
62
+ throw new PushFlowError(
63
+ PushFlowErrorCode.NoPushFlowId,
64
+ 'No push flow id found in input payload'
65
+ )
66
+ }
67
+
68
+ return pushFlowId
69
+ }
70
+
71
+ private getPushFlowNode(pushFlowId: string): HtNodeWithContent {
72
+ const pushFlowNode =
73
+ this.cmsApi.getNodeByCampaignId<HtNodeWithContent>(pushFlowId)
74
+
75
+ if (!pushFlowNode) {
76
+ throw new PushFlowError(
77
+ PushFlowErrorCode.NoCampaignStartNode,
78
+ 'No campaign start node found'
79
+ )
80
+ }
81
+
82
+ return pushFlowNode
83
+ }
84
+
85
+ private getCampaignId(): string {
86
+ const campaignId =
87
+ this.botContext.input.context &&
88
+ 'campaign_id' in this.botContext.input.context
89
+ ? this.botContext.input.context.campaign_id
90
+ : undefined
91
+
92
+ if (!campaignId) {
93
+ throw new PushFlowError(
94
+ PushFlowErrorCode.NoCampaignId,
95
+ 'No campaign id found in input context'
96
+ )
97
+ }
98
+
99
+ return campaignId
100
+ }
101
+ }
package/src/api.ts CHANGED
@@ -82,12 +82,22 @@ export class FlowBuilderApi {
82
82
  return this.getNodeById<HtNodeWithContent>(subFlow.start_node_id)
83
83
  }
84
84
 
85
- getNodeByCampaignId<T extends HtNodeComponent>(id: string): T {
85
+ getNodeByCampaignId<T extends HtNodeComponent>(id: string): T | undefined {
86
86
  const campaign = this.flow.campaigns.find(campaign => campaign.id === id)
87
87
  if (!campaign) {
88
- throw Error(`Campaign with id: '${id}' not found`)
88
+ return undefined
89
89
  }
90
- return this.getNodeById<T>(campaign.start_node_id)
90
+
91
+ const node = this.flow.nodes.find(
92
+ flowNode => flowNode.id === campaign.start_node_id
93
+ )
94
+
95
+ if (!node) {
96
+ console.error(`Node with id: '${campaign.start_node_id}' not found`)
97
+ return undefined
98
+ }
99
+
100
+ return node as T
91
101
  }
92
102
 
93
103
  getNodeById<T extends HtNodeComponent>(id: string): T {
@@ -8,7 +8,7 @@ export class FlowDoNothing extends ContentFieldsBase {
8
8
  static fromHubtypeCMS(cmsDoNothing: HtDoNothingNode): FlowDoNothing {
9
9
  const newDoNothing = new FlowDoNothing(cmsDoNothing.id)
10
10
  newDoNothing.code = cmsDoNothing.code
11
- newDoNothing.followUp = cmsDoNothing.follow_up
11
+ newDoNothing.followUp = undefined
12
12
 
13
13
  return newDoNothing
14
14
  }