@seeka-labs/cli-apps 1.1.24 → 1.1.25

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 (57) hide show
  1. package/LICENSE +19 -19
  2. package/dist/index.js +10 -10
  3. package/dist/index.js.map +4 -4
  4. package/dist/init-templates/aws-lambda/.example.gitignore +48 -48
  5. package/dist/init-templates/aws-lambda/.nvmrc +1 -1
  6. package/dist/init-templates/aws-lambda/README.md +76 -76
  7. package/dist/init-templates/aws-lambda/jest.config.js +4 -4
  8. package/dist/init-templates/aws-lambda/package.json +51 -54
  9. package/dist/init-templates/aws-lambda/src/index.test.ts +6 -6
  10. package/dist/init-templates/aws-lambda/src/lib/logging/index.ts +87 -87
  11. package/dist/init-templates/aws-lambda/src/lib/state/redis/index.ts +64 -64
  12. package/dist/init-templates/aws-lambda/src/lib/state/seeka/installations.ts +66 -66
  13. package/dist/init-templates/aws-lambda/src/routes/seekaAppWebhook.ts +193 -193
  14. package/dist/init-templates/azure-function/.example.gitignore +47 -47
  15. package/dist/init-templates/azure-function/README.md +107 -107
  16. package/dist/init-templates/azure-function/jest.config.js +4 -4
  17. package/dist/init-templates/azure-function/package.json +45 -48
  18. package/dist/init-templates/azure-function/scripts/dev-queue-setup.js +29 -29
  19. package/dist/init-templates/azure-function/src/functions/healthCheck.ts +13 -13
  20. package/dist/init-templates/azure-function/src/functions/pollingExample.ts +39 -39
  21. package/dist/init-templates/azure-function/src/functions/queueExample.ts +66 -66
  22. package/dist/init-templates/azure-function/src/functions/seekaAppWebhook.ts +236 -236
  23. package/dist/init-templates/azure-function/src/index.test.ts +6 -6
  24. package/dist/init-templates/azure-function/src/lib/browser/index.ts +54 -54
  25. package/dist/init-templates/azure-function/src/lib/browser/models/index.ts +6 -6
  26. package/dist/init-templates/azure-function/src/lib/jobs/index.ts +95 -95
  27. package/dist/init-templates/azure-function/src/lib/logging/index.ts +92 -92
  28. package/dist/init-templates/azure-function/src/lib/state/redis/index.ts +64 -64
  29. package/dist/init-templates/azure-function/src/lib/state/seeka/installations.ts +66 -66
  30. package/dist/init-templates/browser/.editorconfig +14 -14
  31. package/dist/init-templates/browser/.eslintrc.cjs +1 -1
  32. package/dist/init-templates/browser/.yarnrc +1 -1
  33. package/dist/init-templates/browser/jest.config.js +11 -11
  34. package/dist/init-templates/browser/package.json +3 -3
  35. package/dist/init-templates/browser/scripts/esbuild/build-browser-plugin.mjs +110 -110
  36. package/dist/init-templates/browser/scripts/esbuild/plugins/importAsGlobals.mjs +38 -38
  37. package/dist/init-templates/browser/src/browser.ts +12 -12
  38. package/dist/init-templates/browser/src/plugin/index.test.ts +6 -6
  39. package/dist/init-templates/browser/src/plugin/index.ts +49 -49
  40. package/dist/init-templates/browser/tsconfig.json +34 -34
  41. package/dist/init-templates/netlify-function/.env.example +17 -17
  42. package/dist/init-templates/netlify-function/.example.gitignore +36 -36
  43. package/dist/init-templates/netlify-function/.nvmrc +1 -1
  44. package/dist/init-templates/netlify-function/.vscode/launch.json +44 -44
  45. package/dist/init-templates/netlify-function/README.md +61 -61
  46. package/dist/init-templates/netlify-function/jest.config.js +4 -4
  47. package/dist/init-templates/netlify-function/netlify.toml +6 -6
  48. package/dist/init-templates/netlify-function/package.json +11 -14
  49. package/dist/init-templates/netlify-function/src/api/example-job-background/index.ts +51 -51
  50. package/dist/init-templates/netlify-function/src/api/polling-example-job-scheduled/index.ts +45 -45
  51. package/dist/init-templates/netlify-function/src/api/seeka-app-webhook/index.ts +216 -216
  52. package/dist/init-templates/netlify-function/src/index.test.ts +6 -6
  53. package/dist/init-templates/netlify-function/src/lib/jobs/index.ts +67 -67
  54. package/dist/init-templates/netlify-function/src/lib/logging/index.ts +90 -90
  55. package/dist/init-templates/netlify-function/src/lib/state/redis/index.ts +64 -64
  56. package/dist/init-templates/netlify-function/src/lib/state/seeka/installations.ts +66 -66
  57. package/package.json +7 -7
@@ -1,67 +1,67 @@
1
- import winston from 'winston';
2
-
3
- import { app, InvocationContext } from '@azure/functions';
4
-
5
- import { BackgroundJobRequestContext, deserialiseQueuePayload, queueNames, sendQueueMessageToPoisonQueue } from '../lib/jobs';
6
- import { backgroundJobLogger } from '../lib/logging';
7
- import { startServices } from '../lib/services';
8
- import { groupBy } from 'lodash';
9
- import { SeekaAppInstallState, tryGetInstallation } from '../lib/state/seeka/installations';
10
- import { SeekaActivityAcceptedWebhookContent } from '@seeka-labs/sdk-apps-server';
11
-
12
- app.storageQueue('queueExample', {
13
- queueName: queueNames.queueItemExampleQueueName,
14
- connection: 'AzureWebJobsStorage',
15
- handler: queueExample
16
- });
17
-
18
- export interface MyQueueItem extends BackgroundJobRequestContext {
19
- items: SeekaActivityAcceptedWebhookContent[];
20
- }
21
-
22
- export async function queueExample(queueItem: any, context: InvocationContext): Promise<void> {
23
- const logger = backgroundJobLogger(queueNames.queueItemExampleQueueName, undefined, context);
24
- logger.profile(`queue.${queueNames.queueItemExampleQueueName}`)
25
- try {
26
- // queueItem can either be a single item or an array of items
27
- const payload = deserialiseQueuePayload<MyQueueItem>(queueItem, logger);
28
-
29
- // Group by applicationInstallId
30
- const grouped = groupBy(payload, e => e.applicationInstallId);
31
-
32
- logger.verbose('Received queue batch to handle queue message', { batchSize: payload.length });
33
-
34
- // Process each group
35
- await startServices(logger);
36
- for (const [applicationInstallId, items] of Object.entries(grouped)) {
37
- if (items.length === 0) {
38
- logger.warn('No items to process for applicationInstallId', { applicationInstallId });
39
- continue;
40
- }
41
- const thisLogger = backgroundJobLogger(queueNames.queueItemExampleQueueName, items[0], context);
42
- try {
43
- const installation = await tryGetInstallation(applicationInstallId, true, thisLogger) as SeekaAppInstallState;
44
-
45
- // Execute sync
46
- // const batchItems = items.flatMap(e => e.items || []).filter(Boolean)
47
- // await executeLongRunningTask(batchItems, logger);
48
- }
49
- catch (err) {
50
- thisLogger.error('Error handling queue item to handle queue message', { ex: winston.exceptions.getAllInfo(err) })
51
- await sendQueueMessageToPoisonQueue(queueNames.queueItemExampleQueueName, {
52
- ...items[0],
53
- causationId: items[0].causationId,
54
- correlationId: context.invocationId,
55
- rows: items.flatMap(e => e.items || []).filter(Boolean)
56
- } as MyQueueItem, thisLogger);
57
- }
58
- }
59
- }
60
- catch (err) {
61
- logger.error('Error handling queue item to handle queue message', { ex: winston.exceptions.getAllInfo(err) })
62
- throw err; // Will retry based on host.json > extensions.queues.maxDequeueCount and then push to poison queue
63
- }
64
- finally {
65
- logger.profile(`queue.${queueNames.queueItemExampleQueueName}`)
66
- }
1
+ import winston from 'winston';
2
+
3
+ import { app, InvocationContext } from '@azure/functions';
4
+
5
+ import { BackgroundJobRequestContext, deserialiseQueuePayload, queueNames, sendQueueMessageToPoisonQueue } from '../lib/jobs';
6
+ import { backgroundJobLogger } from '../lib/logging';
7
+ import { startServices } from '../lib/services';
8
+ import { groupBy } from 'lodash';
9
+ import { SeekaAppInstallState, tryGetInstallation } from '../lib/state/seeka/installations';
10
+ import { SeekaActivityAcceptedWebhookContent } from '@seeka-labs/sdk-apps-server';
11
+
12
+ app.storageQueue('queueExample', {
13
+ queueName: queueNames.queueItemExampleQueueName,
14
+ connection: 'AzureWebJobsStorage',
15
+ handler: queueExample
16
+ });
17
+
18
+ export interface MyQueueItem extends BackgroundJobRequestContext {
19
+ items: SeekaActivityAcceptedWebhookContent[];
20
+ }
21
+
22
+ export async function queueExample(queueItem: any, context: InvocationContext): Promise<void> {
23
+ const logger = backgroundJobLogger(queueNames.queueItemExampleQueueName, undefined, context);
24
+ logger.profile(`queue.${queueNames.queueItemExampleQueueName}`)
25
+ try {
26
+ // queueItem can either be a single item or an array of items
27
+ const payload = deserialiseQueuePayload<MyQueueItem>(queueItem, logger);
28
+
29
+ // Group by applicationInstallId
30
+ const grouped = groupBy(payload, e => e.applicationInstallId);
31
+
32
+ logger.verbose('Received queue batch to handle queue message', { batchSize: payload.length });
33
+
34
+ // Process each group
35
+ await startServices(logger);
36
+ for (const [applicationInstallId, items] of Object.entries(grouped)) {
37
+ if (items.length === 0) {
38
+ logger.warn('No items to process for applicationInstallId', { applicationInstallId });
39
+ continue;
40
+ }
41
+ const thisLogger = backgroundJobLogger(queueNames.queueItemExampleQueueName, items[0], context);
42
+ try {
43
+ const installation = await tryGetInstallation(applicationInstallId, true, thisLogger) as SeekaAppInstallState;
44
+
45
+ // Execute sync
46
+ // const batchItems = items.flatMap(e => e.items || []).filter(Boolean)
47
+ // await executeLongRunningTask(batchItems, logger);
48
+ }
49
+ catch (err) {
50
+ thisLogger.error('Error handling queue item to handle queue message', { ex: winston.exceptions.getAllInfo(err) })
51
+ await sendQueueMessageToPoisonQueue(queueNames.queueItemExampleQueueName, {
52
+ ...items[0],
53
+ causationId: items[0].causationId,
54
+ correlationId: context.invocationId,
55
+ rows: items.flatMap(e => e.items || []).filter(Boolean)
56
+ } as MyQueueItem, thisLogger);
57
+ }
58
+ }
59
+ }
60
+ catch (err) {
61
+ logger.error('Error handling queue item to handle queue message', { ex: winston.exceptions.getAllInfo(err) })
62
+ throw err; // Will retry based on host.json > extensions.queues.maxDequeueCount and then push to poison queue
63
+ }
64
+ finally {
65
+ logger.profile(`queue.${queueNames.queueItemExampleQueueName}`)
66
+ }
67
67
  }
@@ -1,237 +1,237 @@
1
- import winston from 'winston';
2
-
3
- import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';
4
- import {
5
- PersonIdentifiers, SeekaActivityAcceptedWebhookPayload, SeekaAppInstalledWebhookPayload,
6
- SeekaAppInstallSettingsUpdatedWebhookPayload, SeekaAppUninstalledWebhookPayload,
7
- SeekaIdentityChangedWebhookPayload, SeekaWebhookCallType, SeekaWebhookPayload,
8
- throwOnInvalidWebhookSignature
9
- } from '@seeka-labs/sdk-apps-server';
10
-
11
- import { queueNames, triggerBackgroundJob } from '../lib/jobs';
12
- // START: component:browser
13
- import { getSeekaBrowserPlugin } from '../lib/browser'
14
- // END: component:browser
15
-
16
- import { webhookLogger } from '../lib/logging';
17
- import { startServices } from '../lib/services';
18
- import {
19
- createOrUpdateInstallation, deleteInstallation, SampleAppInstallSettings, SeekaAppInstallState, tryGetInstallation
20
- } from '../lib/state/seeka/installations';
21
-
22
- import type { Logger } from 'winston';
23
- import { MyQueueItem } from './queueExample';
24
- app.http('seekaAppWebhook', {
25
- methods: ['POST'],
26
- authLevel: 'anonymous',
27
- route: 'webhook/seeka/app',
28
- handler: seekaAppWebhook
29
- });
30
-
31
- export async function seekaAppWebhook(req: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
32
- const bodyStr = (await req.text()) as string;
33
- if (!bodyStr) {
34
- return {
35
- status: 400,
36
- jsonBody: { error: "Body missing. Ensure body is present and either specify the Content-Length request header OR set Transfer-Encoding request header to 'chunked'" }
37
- }
38
- }
39
- const body = JSON.parse(bodyStr) as SeekaWebhookPayload;
40
-
41
- const logger = webhookLogger(body, context);
42
- logger.profile('http.seeka.webhook.app')
43
- logger.verbose('Received webhook from Seeka', { body });
44
-
45
- // Handle probe
46
- if (body.type === SeekaWebhookCallType.Probe) {
47
- return {
48
- status: 204
49
- }
50
- }
51
-
52
- // Validate webhook
53
- try {
54
- throwOnInvalidWebhookSignature(process.env.SEEKA_APP_SECRET as string, req.headers, bodyStr);
55
- logger.debug('Webhook signature validated', { body });
56
- }
57
- catch {
58
- logger.warn('Webhook signature invalid', { body });
59
- return {
60
- status: 401,
61
- jsonBody: { error: "Webhook call invalid" }
62
- }
63
- }
64
-
65
- if (body.isTest) {
66
- // This is a test webhook call
67
- return {
68
- status: 204
69
- }
70
- }
71
-
72
- await startServices(logger);
73
-
74
- // Check if the webhook is for an app we have installed
75
- let installation: SeekaAppInstallState | null = null;
76
- if (body.type != SeekaWebhookCallType.AppInstalled) {
77
- installation = await tryGetInstallation((body as SeekaAppInstalledWebhookPayload).context?.applicationInstallId as string, false, logger);
78
- if (installation == null) {
79
- logger.warn('Webhook call cannot be processed as the installation ID is not known by this app', { body });
80
-
81
- return {
82
- status: 422,
83
- jsonBody: { error: "App not installed" }
84
- }
85
- }
86
- }
87
-
88
- // Do something
89
- let errorMessage: string | null = null;
90
- try {
91
- switch (body.type) {
92
- case SeekaWebhookCallType.AppInstalled:
93
- {
94
- errorMessage = await onInstallation(body as SeekaAppInstalledWebhookPayload, logger);
95
- break;
96
- }
97
- case SeekaWebhookCallType.AppInstallSettingsUpdated:
98
- {
99
- errorMessage = await onInstallationSettingsUpdate(body as SeekaAppInstallSettingsUpdatedWebhookPayload, logger);
100
- break;
101
- }
102
- case SeekaWebhookCallType.AppUninstalled:
103
- {
104
- if (!body.isTest) {
105
- const payload = body as SeekaAppUninstalledWebhookPayload;
106
- await deleteInstallation(payload.context?.applicationInstallId as string, logger)
107
- }
108
- break;
109
- }
110
- case SeekaWebhookCallType.ActivityAccepted:
111
- {
112
- const payload = body as SeekaActivityAcceptedWebhookPayload;
113
- await handleSeekaActivity(payload, logger);
114
-
115
- break;
116
- }
117
- case SeekaWebhookCallType.IdentityChanged:
118
- {
119
- const payload = body as SeekaIdentityChangedWebhookPayload;
120
-
121
- break;
122
- }
123
- // START: component:browser
124
- case SeekaWebhookCallType.BrowserSdkPlugin: {
125
- const plugin = await getSeekaBrowserPlugin(installation as SeekaAppInstallState, logger);
126
-
127
- logger.profile('http.seeka.webhook.app')
128
- return {
129
- status: 200,
130
- body: JSON.stringify(plugin),
131
- headers: {
132
- 'Content-Type': 'application/json'
133
- }
134
- }
135
- }
136
- // END: component:browser
137
- }
138
- }
139
- catch (err) {
140
- logger.error('Failed to handle webhook', { ex: winston.exceptions.getAllInfo(err) });
141
- return {
142
- status: 500,
143
- jsonBody: { error: "Request failed" }
144
- }
145
- }
146
- finally {
147
- logger.profile('http.seeka.webhook.app')
148
- logger.verbose('Seeka webhook handled');
149
- }
150
-
151
- if (errorMessage) {
152
- logger.warn('Webhook call failed', { errorMessage });
153
- return {
154
- status: 400,
155
- jsonBody: { error: { message: errorMessage } }
156
- }
157
- }
158
-
159
- return {
160
- status: 204
161
- }
162
- }
163
-
164
- const validateInstallationSettings = async (installSettings: SampleAppInstallSettings, logger: Logger): Promise<string | null> => {
165
- // Returning an error message string here will block the installation request or settings update request by the user installing the app
166
-
167
-
168
- return null;
169
- }
170
-
171
- const onInstallation = async (payload: SeekaAppInstalledWebhookPayload, logger: Logger): Promise<string | null> => {
172
- if (payload.isTest) return null;
173
-
174
- const errorMessage = await validateInstallationSettings(payload.content?.installationSettings || {}, logger);
175
- if (errorMessage) return errorMessage;
176
-
177
- const installation = await createOrUpdateInstallation({
178
- ...payload.context,
179
- installationState: {
180
- grantedPermissions: payload.content?.grantedPermissions || []
181
- },
182
- applicationInstallId: payload.context?.applicationInstallId as string,
183
- organisationBrandId: payload.context?.organisationBrandId as string,
184
- organisationId: payload.context?.organisationId as string,
185
- installedAt: new Date().toISOString(),
186
- installationSettings: payload.content?.installationSettings || {}
187
- }, logger)
188
-
189
- try {
190
- await triggerBackgroundJob(queueNames.queueItemExampleQueueName, {
191
- ...payload.context,
192
- causationId: payload.causationId,
193
- correlationId: payload.requestId
194
- }, logger)
195
- }
196
- catch (err) {
197
- await deleteInstallation(installation.applicationInstallId, logger)
198
- return 'Failed to complete install';
199
- }
200
-
201
- return null;
202
- }
203
-
204
- const onInstallationSettingsUpdate = async (payload: SeekaAppInstallSettingsUpdatedWebhookPayload, logger: Logger): Promise<string | null> => {
205
- if (payload.isTest) return null;
206
-
207
- const errorMessage = await validateInstallationSettings(payload.content?.installationSettings || {}, logger);
208
- if (errorMessage) return errorMessage;
209
-
210
- const existingInstallation = await tryGetInstallation(payload.context?.applicationInstallId as string, true, logger) as SeekaAppInstallState;
211
- const installation = await createOrUpdateInstallation({
212
- ...existingInstallation,
213
- installationState: {
214
- ...existingInstallation.installationState,
215
- grantedPermissions: payload.content?.grantedPermissions || []
216
- },
217
- installationSettings: payload.content?.installationSettings || {},
218
- }, logger)
219
-
220
- await triggerBackgroundJob(queueNames.queueItemExampleQueueName, {
221
- ...payload.context,
222
- causationId: payload.causationId,
223
- correlationId: payload.requestId
224
- }, logger)
225
-
226
- return null;
227
- }
228
-
229
- const handleSeekaActivity = async (activity: SeekaActivityAcceptedWebhookPayload, logger: Logger) => {
230
- // Will be handled by queueExample
231
- await triggerBackgroundJob(queueNames.queueItemExampleQueueName, {
232
- ...activity.context,
233
- causationId: activity.causationId,
234
- correlationId: activity.requestId,
235
- items: [activity]
236
- } as MyQueueItem, logger)
1
+ import winston from 'winston';
2
+
3
+ import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';
4
+ import {
5
+ PersonIdentifiers, SeekaActivityAcceptedWebhookPayload, SeekaAppInstalledWebhookPayload,
6
+ SeekaAppInstallSettingsUpdatedWebhookPayload, SeekaAppUninstalledWebhookPayload,
7
+ SeekaIdentityChangedWebhookPayload, SeekaWebhookCallType, SeekaWebhookPayload,
8
+ throwOnInvalidWebhookSignature
9
+ } from '@seeka-labs/sdk-apps-server';
10
+
11
+ import { queueNames, triggerBackgroundJob } from '../lib/jobs';
12
+ // START: component:browser
13
+ import { getSeekaBrowserPlugin } from '../lib/browser'
14
+ // END: component:browser
15
+
16
+ import { webhookLogger } from '../lib/logging';
17
+ import { startServices } from '../lib/services';
18
+ import {
19
+ createOrUpdateInstallation, deleteInstallation, SampleAppInstallSettings, SeekaAppInstallState, tryGetInstallation
20
+ } from '../lib/state/seeka/installations';
21
+
22
+ import type { Logger } from 'winston';
23
+ import { MyQueueItem } from './queueExample';
24
+ app.http('seekaAppWebhook', {
25
+ methods: ['POST'],
26
+ authLevel: 'anonymous',
27
+ route: 'webhook/seeka/app',
28
+ handler: seekaAppWebhook
29
+ });
30
+
31
+ export async function seekaAppWebhook(req: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
32
+ const bodyStr = (await req.text()) as string;
33
+ if (!bodyStr) {
34
+ return {
35
+ status: 400,
36
+ jsonBody: { error: "Body missing. Ensure body is present and either specify the Content-Length request header OR set Transfer-Encoding request header to 'chunked'" }
37
+ }
38
+ }
39
+ const body = JSON.parse(bodyStr) as SeekaWebhookPayload;
40
+
41
+ const logger = webhookLogger(body, context);
42
+ logger.profile('http.seeka.webhook.app')
43
+ logger.verbose('Received webhook from Seeka', { body });
44
+
45
+ // Handle probe
46
+ if (body.type === SeekaWebhookCallType.Probe) {
47
+ return {
48
+ status: 204
49
+ }
50
+ }
51
+
52
+ // Validate webhook
53
+ try {
54
+ throwOnInvalidWebhookSignature(process.env.SEEKA_APP_SECRET as string, req.headers, bodyStr);
55
+ logger.debug('Webhook signature validated', { body });
56
+ }
57
+ catch {
58
+ logger.warn('Webhook signature invalid', { body });
59
+ return {
60
+ status: 401,
61
+ jsonBody: { error: "Webhook call invalid" }
62
+ }
63
+ }
64
+
65
+ if (body.isTest) {
66
+ // This is a test webhook call
67
+ return {
68
+ status: 204
69
+ }
70
+ }
71
+
72
+ await startServices(logger);
73
+
74
+ // Check if the webhook is for an app we have installed
75
+ let installation: SeekaAppInstallState | null = null;
76
+ if (body.type != SeekaWebhookCallType.AppInstalled) {
77
+ installation = await tryGetInstallation((body as SeekaAppInstalledWebhookPayload).context?.applicationInstallId as string, false, logger);
78
+ if (installation == null) {
79
+ logger.warn('Webhook call cannot be processed as the installation ID is not known by this app', { body });
80
+
81
+ return {
82
+ status: 422,
83
+ jsonBody: { error: "App not installed" }
84
+ }
85
+ }
86
+ }
87
+
88
+ // Do something
89
+ let errorMessage: string | null = null;
90
+ try {
91
+ switch (body.type) {
92
+ case SeekaWebhookCallType.AppInstalled:
93
+ {
94
+ errorMessage = await onInstallation(body as SeekaAppInstalledWebhookPayload, logger);
95
+ break;
96
+ }
97
+ case SeekaWebhookCallType.AppInstallSettingsUpdated:
98
+ {
99
+ errorMessage = await onInstallationSettingsUpdate(body as SeekaAppInstallSettingsUpdatedWebhookPayload, logger);
100
+ break;
101
+ }
102
+ case SeekaWebhookCallType.AppUninstalled:
103
+ {
104
+ if (!body.isTest) {
105
+ const payload = body as SeekaAppUninstalledWebhookPayload;
106
+ await deleteInstallation(payload.context?.applicationInstallId as string, logger)
107
+ }
108
+ break;
109
+ }
110
+ case SeekaWebhookCallType.ActivityAccepted:
111
+ {
112
+ const payload = body as SeekaActivityAcceptedWebhookPayload;
113
+ await handleSeekaActivity(payload, logger);
114
+
115
+ break;
116
+ }
117
+ case SeekaWebhookCallType.IdentityChanged:
118
+ {
119
+ const payload = body as SeekaIdentityChangedWebhookPayload;
120
+
121
+ break;
122
+ }
123
+ // START: component:browser
124
+ case SeekaWebhookCallType.BrowserSdkPlugin: {
125
+ const plugin = await getSeekaBrowserPlugin(installation as SeekaAppInstallState, logger);
126
+
127
+ logger.profile('http.seeka.webhook.app')
128
+ return {
129
+ status: 200,
130
+ body: JSON.stringify(plugin),
131
+ headers: {
132
+ 'Content-Type': 'application/json'
133
+ }
134
+ }
135
+ }
136
+ // END: component:browser
137
+ }
138
+ }
139
+ catch (err) {
140
+ logger.error('Failed to handle webhook', { ex: winston.exceptions.getAllInfo(err) });
141
+ return {
142
+ status: 500,
143
+ jsonBody: { error: "Request failed" }
144
+ }
145
+ }
146
+ finally {
147
+ logger.profile('http.seeka.webhook.app')
148
+ logger.verbose('Seeka webhook handled');
149
+ }
150
+
151
+ if (errorMessage) {
152
+ logger.warn('Webhook call failed', { errorMessage });
153
+ return {
154
+ status: 400,
155
+ jsonBody: { error: { message: errorMessage } }
156
+ }
157
+ }
158
+
159
+ return {
160
+ status: 204
161
+ }
162
+ }
163
+
164
+ const validateInstallationSettings = async (installSettings: SampleAppInstallSettings, logger: Logger): Promise<string | null> => {
165
+ // Returning an error message string here will block the installation request or settings update request by the user installing the app
166
+
167
+
168
+ return null;
169
+ }
170
+
171
+ const onInstallation = async (payload: SeekaAppInstalledWebhookPayload, logger: Logger): Promise<string | null> => {
172
+ if (payload.isTest) return null;
173
+
174
+ const errorMessage = await validateInstallationSettings(payload.content?.installationSettings || {}, logger);
175
+ if (errorMessage) return errorMessage;
176
+
177
+ const installation = await createOrUpdateInstallation({
178
+ ...payload.context,
179
+ installationState: {
180
+ grantedPermissions: payload.content?.grantedPermissions || []
181
+ },
182
+ applicationInstallId: payload.context?.applicationInstallId as string,
183
+ organisationBrandId: payload.context?.organisationBrandId as string,
184
+ organisationId: payload.context?.organisationId as string,
185
+ installedAt: new Date().toISOString(),
186
+ installationSettings: payload.content?.installationSettings || {}
187
+ }, logger)
188
+
189
+ try {
190
+ await triggerBackgroundJob(queueNames.queueItemExampleQueueName, {
191
+ ...payload.context,
192
+ causationId: payload.causationId,
193
+ correlationId: payload.requestId
194
+ }, logger)
195
+ }
196
+ catch (err) {
197
+ await deleteInstallation(installation.applicationInstallId, logger)
198
+ return 'Failed to complete install';
199
+ }
200
+
201
+ return null;
202
+ }
203
+
204
+ const onInstallationSettingsUpdate = async (payload: SeekaAppInstallSettingsUpdatedWebhookPayload, logger: Logger): Promise<string | null> => {
205
+ if (payload.isTest) return null;
206
+
207
+ const errorMessage = await validateInstallationSettings(payload.content?.installationSettings || {}, logger);
208
+ if (errorMessage) return errorMessage;
209
+
210
+ const existingInstallation = await tryGetInstallation(payload.context?.applicationInstallId as string, true, logger) as SeekaAppInstallState;
211
+ const installation = await createOrUpdateInstallation({
212
+ ...existingInstallation,
213
+ installationState: {
214
+ ...existingInstallation.installationState,
215
+ grantedPermissions: payload.content?.grantedPermissions || []
216
+ },
217
+ installationSettings: payload.content?.installationSettings || {},
218
+ }, logger)
219
+
220
+ await triggerBackgroundJob(queueNames.queueItemExampleQueueName, {
221
+ ...payload.context,
222
+ causationId: payload.causationId,
223
+ correlationId: payload.requestId
224
+ }, logger)
225
+
226
+ return null;
227
+ }
228
+
229
+ const handleSeekaActivity = async (activity: SeekaActivityAcceptedWebhookPayload, logger: Logger) => {
230
+ // Will be handled by queueExample
231
+ await triggerBackgroundJob(queueNames.queueItemExampleQueueName, {
232
+ ...activity.context,
233
+ causationId: activity.causationId,
234
+ correlationId: activity.requestId,
235
+ items: [activity]
236
+ } as MyQueueItem, logger)
237
237
  }
@@ -1,7 +1,7 @@
1
- import { describe, expect, test } from '@jest/globals';
2
-
3
- describe('test example module', () => {
4
- test('should be false', () => {
5
- expect(false).toBeFalsy();
6
- });
1
+ import { describe, expect, test } from '@jest/globals';
2
+
3
+ describe('test example module', () => {
4
+ test('should be false', () => {
5
+ expect(false).toBeFalsy();
6
+ });
7
7
  });