@seeka-labs/cli-apps 3.8.10 → 3.9.2

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 (72) hide show
  1. package/README.md +286 -0
  2. package/dist/index.cjs +821 -612
  3. package/dist/init-template.zip +0 -0
  4. package/package.json +5 -3
  5. package/dist/ai-context/common/README.md +0 -110
  6. package/dist/ai-context/common/architecture/overview.md +0 -140
  7. package/dist/ai-context/common/guides/common-patterns.md +0 -471
  8. package/dist/ai-context/common/guides/data-flow.md +0 -455
  9. package/dist/ai-context/common/guides/getting-started.md +0 -349
  10. package/dist/ai-context/common/guides/testing.md +0 -592
  11. package/dist/ai-context/common/reference/cli-commands.md +0 -231
  12. package/dist/ai-context/common/reference/config-schema.md +0 -345
  13. package/dist/ai-context/common/reference/sdk-api.md +0 -445
  14. package/dist/ai-context/public/architecture/app-structure.md +0 -385
  15. package/dist/index.cjs.map +0 -7
  16. package/dist/init-template/.github/workflows/deploy-azurefunc.yml +0 -198
  17. package/dist/init-template/.gitlab-ci.yml +0 -67
  18. package/dist/init-template/.nvmrc +0 -1
  19. package/dist/init-template/AGENTS.md +0 -99
  20. package/dist/init-template/README.azurefunc.md +0 -238
  21. package/dist/init-template/app/.eslintrc.cjs +0 -13
  22. package/dist/init-template/app/browser/README.md +0 -1
  23. package/dist/init-template/app/browser/package.json +0 -36
  24. package/dist/init-template/app/browser/scripts/esbuild/build-browser-plugin.mjs +0 -130
  25. package/dist/init-template/app/browser/scripts/esbuild/plugins/importAsGlobals.mjs +0 -39
  26. package/dist/init-template/app/browser/src/browser.ts +0 -12
  27. package/dist/init-template/app/browser/src/plugin/index.ts +0 -61
  28. package/dist/init-template/app/browser/tsconfig.json +0 -34
  29. package/dist/init-template/app/lib/package.json +0 -46
  30. package/dist/init-template/app/lib/src/index.ts +0 -4
  31. package/dist/init-template/app/lib/src/models/index.ts +0 -29
  32. package/dist/init-template/app/lib/src/validation/index.ts +0 -14
  33. package/dist/init-template/app/lib/tsconfig.json +0 -22
  34. package/dist/init-template/app/server-azurefunc/.eslintrc.cjs +0 -4
  35. package/dist/init-template/app/server-azurefunc/.funcignore +0 -20
  36. package/dist/init-template/app/server-azurefunc/README.md +0 -105
  37. package/dist/init-template/app/server-azurefunc/host.json +0 -31
  38. package/dist/init-template/app/server-azurefunc/local.settings.template.json +0 -30
  39. package/dist/init-template/app/server-azurefunc/package.json +0 -68
  40. package/dist/init-template/app/server-azurefunc/scripts/build.mjs +0 -67
  41. package/dist/init-template/app/server-azurefunc/scripts/dev-queue-setup.js +0 -55
  42. package/dist/init-template/app/server-azurefunc/src/app/api/router.ts +0 -15
  43. package/dist/init-template/app/server-azurefunc/src/app/api/routes/getInstallationSettings.ts +0 -13
  44. package/dist/init-template/app/server-azurefunc/src/app/api/routes/setInstallationSettings.ts +0 -35
  45. package/dist/init-template/app/server-azurefunc/src/app/jobs/index.ts +0 -61
  46. package/dist/init-template/app/server-azurefunc/src/app/logging/index.ts +0 -4
  47. package/dist/init-template/app/server-azurefunc/src/app/models/index.ts +0 -12
  48. package/dist/init-template/app/server-azurefunc/src/app/services/activites.ts +0 -8
  49. package/dist/init-template/app/server-azurefunc/src/functions/healthCheck.ts +0 -19
  50. package/dist/init-template/app/server-azurefunc/src/functions/seekaAppWebhook.ts +0 -204
  51. package/dist/init-template/app/server-azurefunc/src/functions/trackActivityQueueHandler.ts +0 -48
  52. package/dist/init-template/app/server-azurefunc/src/functions/ui.ts +0 -49
  53. package/dist/init-template/app/server-azurefunc/tsconfig.json +0 -24
  54. package/dist/init-template/app/ui/README.md +0 -40
  55. package/dist/init-template/app/ui/index.html +0 -21
  56. package/dist/init-template/app/ui/package.json +0 -72
  57. package/dist/init-template/app/ui/public/favicon.ico +0 -0
  58. package/dist/init-template/app/ui/scripts/copy-output.mjs +0 -30
  59. package/dist/init-template/app/ui/src/App.tsx +0 -72
  60. package/dist/init-template/app/ui/src/assets/app-icon.svg +0 -1
  61. package/dist/init-template/app/ui/src/components/setup/steps/complete/index.tsx +0 -32
  62. package/dist/init-template/app/ui/src/components/setup/steps/first/index.tsx +0 -27
  63. package/dist/init-template/app/ui/src/components/setup/steps/index.tsx +0 -22
  64. package/dist/init-template/app/ui/src/components/setup/steps/second/index.tsx +0 -38
  65. package/dist/init-template/app/ui/src/index.tsx +0 -45
  66. package/dist/init-template/app/ui/src/routes/home/index.tsx +0 -21
  67. package/dist/init-template/app/ui/src/vite-env.d.ts +0 -13
  68. package/dist/init-template/app/ui/tsconfig.json +0 -35
  69. package/dist/init-template/app/ui/tsconfig.node.json +0 -10
  70. package/dist/init-template/app/ui/vite.config.mts +0 -48
  71. package/dist/init-template/package.json +0 -46
  72. package/dist/init-template/tsconfig.json +0 -24
@@ -1,67 +0,0 @@
1
- import * as esbuild from 'esbuild';
2
- import { glob } from 'glob';
3
- import * as path from 'path';
4
- import { fileURLToPath } from 'url';
5
-
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = path.dirname(__filename);
8
-
9
- // Check if watch mode is enabled
10
- const isWatch = process.argv.includes('--watch');
11
-
12
- // Find all function entry points
13
- const functionFiles = glob.sync('src/functions/*.ts', { cwd: path.resolve(__dirname, '..') });
14
-
15
- console.log(`${isWatch ? 'Watching' : 'Building'} Azure Functions with esbuild...`);
16
- console.log('Entry points:', functionFiles);
17
-
18
- const buildOptions = {
19
- entryPoints: functionFiles,
20
- bundle: true,
21
- platform: 'node',
22
- target: 'node22',
23
- format: 'cjs',
24
- outdir: 'dist',
25
- outbase: '.',
26
- sourcemap: true,
27
- external: [
28
- // Azure Functions runtime
29
- '@azure/functions',
30
- // Native modules
31
- '@azure/storage-queue',
32
- '@redis/client',
33
- '@redis/json',
34
- 'redis',
35
- // OpenTelemetry - keep external as they're large
36
- '@opentelemetry/*',
37
- // Other large dependencies that work fine as external
38
- 'google-ads-api',
39
- 'google-auth-library',
40
- 'winston',
41
- 'undici',
42
- 'memory-cache',
43
- 'openid-client',
44
- 'jsonwebtoken',
45
- 'lodash-es',
46
- 'yup',
47
- 'axios',
48
- // Seeka packages - keep external to share state across functions
49
- '@seeka-labs/sdk-apps-core',
50
- '@seeka-labs/sdk-apps-server',
51
- '@seeka-labs/sdk-apps-server-host',
52
- '@seeka-labs/sdk-apps-server-telemetry-logging'
53
- ],
54
-
55
- mainFields: ['module', 'main'],
56
- conditions: ['import', 'require', 'node'],
57
- logLevel: 'info',
58
- };
59
-
60
- if (isWatch) {
61
- const context = await esbuild.context(buildOptions);
62
- await context.watch();
63
- console.log('Watching for changes...');
64
- } else {
65
- await esbuild.build(buildOptions);
66
- console.log('Build completed successfully!');
67
- }
@@ -1,55 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-var-requires */
2
- /* eslint-disable no-undef */
3
- const { QueueClient } = require("@azure/storage-queue");
4
- const fs = require("fs");
5
- const path = require("path");
6
-
7
- function getAzureWebJobsStorageConnectionString() {
8
- // local.settings.json lives one level above this script (../local.settings.json)
9
- const localSettingsPath = path.join(__dirname, "..", "local.settings.json");
10
-
11
- let raw;
12
- try {
13
- raw = fs.readFileSync(localSettingsPath, "utf8");
14
- }
15
- catch (e) {
16
- throw new Error(
17
- `Unable to read ${localSettingsPath}. Ensure local.settings.json exists and is readable. Original error: ${e && e.message ? e.message : e}`
18
- );
19
- }
20
-
21
- let json;
22
- try {
23
- json = JSON.parse(raw);
24
- }
25
- catch (e) {
26
- throw new Error(
27
- `Invalid JSON in ${localSettingsPath}. Original error: ${e && e.message ? e.message : e}`
28
- );
29
- }
30
-
31
- const connectionString = json?.Values?.AzureWebJobsStorage;
32
- if (typeof connectionString !== "string" || connectionString.trim() === "") {
33
- throw new Error(
34
- `Missing or empty Values.AzureWebJobsStorage in ${localSettingsPath}`
35
- );
36
- }
37
-
38
- return connectionString;
39
- }
40
-
41
- (async function () {
42
- // https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite?tabs=visual-studio%2Cqueue-storage#azure-sdks
43
- // Dev / emulator / azurite
44
- const connectionString = getAzureWebJobsStorageConnectionString();
45
- var client = new QueueClient(
46
- connectionString, process.argv[2]
47
- );
48
- const res = await client.createIfNotExists();
49
- if (res.succeeded) {
50
- console.log("Queue created - " + process.argv[2]);
51
- }
52
- else {
53
- console.log("Queue already exists - " + process.argv[2]);
54
- }
55
- })();
@@ -1,15 +0,0 @@
1
- import {ExampleAppAppInstallContext} from "@example-org-name/example-app-name-lib";
2
- import {getInstallationSettings} from "./routes/getInstallationSettings";
3
- import {setInstallationSettings} from "./routes/setInstallationSettings";
4
- import { Logger } from "winston";
5
- import { AppUiHttpRequestResponse, HttpMethod } from "@seeka-labs/sdk-apps-server-host";
6
- import {HttpRequest, InvocationContext} from "@azure/functions";
7
-
8
- export const apiRouteHandler = async (req: HttpRequest, context: InvocationContext, requestedPath: string, requestedMethod: HttpMethod, installation: ExampleAppAppInstallContext, logger: Logger ): Promise<AppUiHttpRequestResponse | null> => {
9
- if (requestedPath === '/api/settings' && requestedMethod === 'GET') return await getInstallationSettings(installation, req, context, logger);
10
- if (requestedPath === '/api/settings' && requestedMethod === 'POST') return await setInstallationSettings(installation, req, context, logger);
11
-
12
- logger.warn(`No route handler found for requestedPath: ${requestedPath} and requestedMethod: ${requestedMethod}`);
13
-
14
- return null;
15
- }
@@ -1,13 +0,0 @@
1
- import { HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";
2
- import { ExampleAppAppInstallContext, ExampleAppAppInstallSettings } from "@example-org-name/example-app-name-lib";
3
- import { AppUiHttpRequestResponse } from "@seeka-labs/sdk-apps-server-host";
4
- import { Logger } from "winston";
5
-
6
- export const getInstallationSettings = async (installation: ExampleAppAppInstallContext, req: HttpRequest, context: InvocationContext, logger: Logger): Promise<AppUiHttpRequestResponse> => {
7
- // Important to not expose any sensitive data to the client via this endpoint
8
- // Be selective, no spread operator here, only the fields you want to expose
9
- return {
10
- status: 200,
11
- jsonBody: installation.installationState?.installationSettings as ExampleAppAppInstallSettings
12
- }
13
- }
@@ -1,35 +0,0 @@
1
- import { HttpRequest, InvocationContext } from "@azure/functions";
2
- import { Logger } from "winston";
3
-
4
- import { ExampleAppAppInstallContext, ExampleAppAppInstallSettings, validateInstallationSettings } from "@example-org-name/example-app-name-lib";
5
- import { AppUiHttpRequestResponse, createOrUpdateInstallation } from "@seeka-labs/sdk-apps-server-host";
6
-
7
- export const setInstallationSettings = async (installation: ExampleAppAppInstallContext, req: HttpRequest, context: InvocationContext, logger: Logger): Promise<AppUiHttpRequestResponse> => {
8
- // Validate
9
- const newSettings = (await req.json()) as ExampleAppAppInstallSettings | null;
10
- if (!newSettings) {
11
- return {
12
- status: 400,
13
- jsonBody: { error: 'Settings are required' }
14
- };
15
- }
16
-
17
- const error = await validateInstallationSettings(newSettings, logger);
18
- if (error) return {
19
- status: 400,
20
- jsonBody: { error }
21
- }
22
-
23
- installation.installationState = {
24
- ...installation.installationState,
25
- installationSettings: {
26
- ...installation.installationState.installationSettings,
27
- ...newSettings
28
- }
29
- };
30
- await createOrUpdateInstallation(installation, logger);
31
-
32
- return {
33
- status: 200
34
- };
35
- }
@@ -1,61 +0,0 @@
1
- import { QueueClient, QueueSendMessageOptions } from '@azure/storage-queue';
2
- import type { Logger } from 'winston';
3
- import {BackgroundJobRequestContext} from "@seeka-labs/sdk-apps-server-host";
4
-
5
- export const queueNames = {
6
- trackActivity: 'activity-track-queue',
7
- }
8
-
9
- export const triggerBackgroundJob = async <TPayload>(queueName: string, context: BackgroundJobRequestContext<TPayload>, logger: Logger, queueSendOptions?: QueueSendMessageOptions): Promise<void> => {
10
- const queueClient = new QueueClient(process.env.AzureWebJobsStorage as string, queueName);
11
- return await triggerBackgroundJobWithQueue(queueClient, context, logger, queueSendOptions);
12
- }
13
-
14
- const serialiseQueuePayload = (payload: unknown): string => {
15
- const jsonString = JSON.stringify(payload)
16
- return Buffer.from(jsonString).toString('base64')
17
- }
18
-
19
- export const deserialiseQueuePayload = <TPayload>(payload: string): TPayload => {
20
- const jsonString = Buffer.from(payload, 'base64').toString()
21
- return JSON.parse(jsonString) as TPayload;
22
- }
23
-
24
- export const triggerBackgroundJobWithQueue = async <TPayload>(queueClient: QueueClient, context: BackgroundJobRequestContext<TPayload>, logger: Logger, queueSendOptions?: QueueSendMessageOptions): Promise<void> => {
25
- const body = {
26
- ...context
27
- }
28
- const bodyStr = serialiseQueuePayload(body);
29
-
30
- const response = await queueClient.sendMessage(bodyStr, queueSendOptions);
31
-
32
- if (response.errorCode) {
33
- const { requestId, date, errorCode } = response;
34
- logger.error("Failed to trigger background job", { body, requestId, date, errorCode })
35
- throw new Error(`Failed to trigger background job: ${response.errorCode}`);
36
- }
37
- else {
38
- logger.info("Background job triggered for " + queueClient.name, { body, messageId: response.messageId, context })
39
- }
40
- }
41
-
42
- export const sendQueueMessageToPoisonQueue = async <TPayload>(queueName: string, context: BackgroundJobRequestContext<TPayload>, logger: Logger): Promise<void> => {
43
- const body = {
44
- ...context
45
- }
46
- const bodyStr = serialiseQueuePayload(body);
47
-
48
- // https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-queue-trigger?tabs=python-v2%2Cisolated-process%2Cnodejs-v4%2Cextensionv5&pivots=programming-language-typescript#poison-messages
49
- const queueClient = new QueueClient(process.env.AzureWebJobsStorage as string, `${queueName}-poison`);
50
-
51
- const response = await queueClient.sendMessage(bodyStr, { messageTimeToLive: -1 });
52
-
53
- if (response.errorCode) {
54
- const { requestId, date, errorCode } = response;
55
- logger.error("Failed to push to poison queue", { body, requestId, date, errorCode })
56
- throw new Error(`Failed to push to poison queue: ${response.errorCode}`);
57
- }
58
- else {
59
- logger.verbose("Message pushed to poison queue", { body, messageId: response.messageId, context })
60
- }
61
- }
@@ -1,4 +0,0 @@
1
- import * as packageJson from '../../../package.json';
2
- import {registerSeekaAppOpenTelemetry} from "@seeka-labs/sdk-apps-server-telemetry-logging";
3
-
4
- registerSeekaAppOpenTelemetry(packageJson.name, packageJson.version, 'azure', 'azure_function', process.env);
@@ -1,12 +0,0 @@
1
- 
2
- import {SeekaActivityAcceptedWebhookContent} from "@seeka-labs/sdk-apps-server";
3
- import { BackgroundJobRequestContext } from "@seeka-labs/sdk-apps-server-host";
4
-
5
- export type TrackExampleAppActivityQueueItem = BackgroundJobRequestContext<TrackSeekaExampleAppActivityPayload> & {
6
-
7
- }
8
-
9
- export type TrackSeekaExampleAppActivityPayload = {
10
- seekaActivity: SeekaActivityAcceptedWebhookContent
11
- }
12
-
@@ -1,8 +0,0 @@
1
- 
2
- import {Logger} from "winston";
3
- import {TrackSeekaExampleAppActivityPayload} from "../models";
4
- import {SeekaAppInstallContext} from "@seeka-labs/sdk-apps-core";
5
-
6
- export const sendActivityToAnotherSoftware = async (payload: TrackSeekaExampleAppActivityPayload, installation: SeekaAppInstallContext<any>, causationId: string, correlationId: string, logger: Logger): Promise<void> => {
7
- // Anything
8
- }
@@ -1,19 +0,0 @@
1
- import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';
2
- import '../app/logging'
3
- import { appLogger } from "@seeka-labs/sdk-apps-server-host";
4
-
5
- app.http('healthCheck', {
6
- methods: ['GET', 'HEAD'],
7
- authLevel: 'anonymous',
8
- route: 'api/health',
9
- handler: healthCheck
10
- });
11
-
12
- export async function healthCheck(req: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
13
- // logger.verbose('Health check request received', { status: res.status, url: req.url });
14
- appLogger.verbose('Health check', { url: req.url });
15
-
16
- return {
17
- status: 200
18
- }
19
- }
@@ -1,204 +0,0 @@
1
- import {app, HttpRequest, HttpResponseInit, InvocationContext} from '@azure/functions';
2
- import '../app/logging'
3
- import * as winston from 'winston';
4
-
5
- import {
6
- SeekaActivityAcceptedWebhookPayload, SeekaAppInstalledWebhookPayload,
7
- SeekaAppUninstalledWebhookPayload,
8
- SeekaIdentityChangedWebhookPayload, SeekaWebhookCallType, SeekaWebhookPayload,
9
- throwOnInvalidWebhookSignature
10
- } from '@seeka-labs/sdk-apps-server';
11
-
12
- import {tryGetInstallation,
13
- createOrUpdateInstallation, deleteInstallation, getSeekaBrowserPlugin, startServices,
14
- webhookLogger,
15
- childLogger,
16
- } from '@seeka-labs/sdk-apps-server-host';
17
-
18
- import { validateInstallationSettings, ExampleAppAppBrowserSdkPluginConfig, ExampleAppAppInstallContext} from '@example-org-name/example-app-name-lib'
19
-
20
- import type {Logger} from 'winston';
21
- import {queueNames, triggerBackgroundJob} from '../app/jobs';
22
- import {TrackExampleAppActivityQueueItem} from "../app/models";
23
- import path from "path";
24
-
25
- app.http('seekaAppWebhook', {
26
- methods: ['POST'],
27
- authLevel: 'anonymous',
28
- route: 'api/webhook/seeka/app',
29
- handler: seekaAppWebhook
30
- });
31
-
32
- export async function seekaAppWebhook(req: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
33
- const bodyStr = (await req.text()) as string;
34
- if (!bodyStr) {
35
- return {
36
- status: 400,
37
- jsonBody: {error: "Body missing. Ensure body is present and either specify the Content-Length request header OR set Transfer-Encoding request header to 'chunked'"}
38
- }
39
- }
40
- const body = JSON.parse(bodyStr) as SeekaWebhookPayload;
41
-
42
- let logger = webhookLogger(body, context);
43
- logger.profile('http.seeka.webhook.app')
44
- logger.verbose('Received webhook from Seeka', {body});
45
-
46
- // Handle probe
47
- if (body.type === SeekaWebhookCallType.Probe) {
48
- return {
49
- status: 204
50
- }
51
- }
52
-
53
- // Validate webhook
54
- try {
55
- throwOnInvalidWebhookSignature(process.env.SEEKA_APP_SECRET as string, req.headers as Headers, bodyStr);
56
- logger.debug('Webhook signature validated', {body});
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: ExampleAppAppInstallContext | 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
- logger = childLogger(null, installation, logger, context);
89
-
90
- // Do something
91
- let errorMessage: string | null = null;
92
- try {
93
- switch (body.type) {
94
- case SeekaWebhookCallType.AppInstalled: {
95
- errorMessage = await onInstallation(body as SeekaAppInstalledWebhookPayload, logger);
96
- break;
97
- }
98
- case SeekaWebhookCallType.AppUninstalled: {
99
- if (!body.isTest) {
100
- const payload = body as SeekaAppUninstalledWebhookPayload;
101
- await deleteInstallation(payload.context?.applicationInstallId as string, logger)
102
- }
103
- break;
104
- }
105
- case SeekaWebhookCallType.ActivityAccepted: {
106
- const payload = body as SeekaActivityAcceptedWebhookPayload;
107
- await handleSeekaActivity(payload, installation, context, logger);
108
-
109
- break;
110
- }
111
- case SeekaWebhookCallType.IdentityChanged: {
112
- const payload = body as SeekaIdentityChangedWebhookPayload;
113
-
114
- logger.debug('Identity changed webhook received', {personId: payload.content.personId });
115
-
116
- break;
117
- }
118
-
119
- case SeekaWebhookCallType.BrowserSdkPlugin: {
120
- // Configuration object passed to the Seeka browser plugin
121
- // Under no circumstances should internal or private api keys be exposed to the client/browser via the below object
122
- const pluginConfig = {
123
- appId: process.env.SEEKA_APP_ID,
124
- appInstallId: installation.applicationInstallId,
125
- appUrl: process.env.SELF_HOST_BASEURL,
126
- installationSettings: {
127
- browserPluginAppSetting1: installation.installationState?.installationSettings?.browserPluginAppSetting1
128
- }
129
- } as ExampleAppAppBrowserSdkPluginConfig;
130
- const browserPluginBasePath = path.resolve(`./dist/src/browser`);
131
- const plugin = await getSeekaBrowserPlugin<ExampleAppAppBrowserSdkPluginConfig>('ExampleAppAppConvergeSdkPlugin', browserPluginBasePath, pluginConfig, logger);
132
-
133
- logger.profile('http.seeka.webhook.app')
134
- return {
135
- status: 200,
136
- body: JSON.stringify(plugin),
137
- headers: {
138
- 'Content-Type': 'application/json'
139
- }
140
- }
141
- }
142
- }
143
- } catch (err) {
144
- logger.error('Failed to handle webhook', {ex: winston.exceptions.getAllInfo(err)});
145
- return {
146
- status: 500,
147
- jsonBody: {error: "Request failed"}
148
- }
149
- } finally {
150
- logger.profile('http.seeka.webhook.app')
151
- logger.verbose('Seeka webhook handled');
152
- }
153
-
154
- if (errorMessage) {
155
- logger.warn('Webhook call failed', {errorMessage});
156
- return {
157
- status: 400,
158
- jsonBody: {error: {message: errorMessage}}
159
- }
160
- }
161
-
162
- return {
163
- status: 204
164
- }
165
- }
166
-
167
- const onInstallation = async (payload: SeekaAppInstalledWebhookPayload, logger: Logger): Promise<string | null> => {
168
- if (payload.isTest) return null;
169
-
170
- const errorMessage = await validateInstallationSettings(payload.content?.installationSettings || {}, logger);
171
- if (errorMessage) return errorMessage;
172
-
173
- const installation = await createOrUpdateInstallation({
174
- ...payload.context,
175
- installationState: {
176
- grantedPermissions: payload.content?.grantedPermissions || [],
177
- installationSettings: payload.content?.installationSettings || {}
178
- },
179
- applicationInstallId: payload.context?.applicationInstallId as string,
180
- organisationBrandId: payload.context?.organisationBrandId as string,
181
- organisationId: payload.context?.organisationId as string,
182
- installedAt: new Date().toISOString(),
183
- }, logger)
184
-
185
- return null;
186
- }
187
-
188
- const handleSeekaActivity = async (activity: SeekaActivityAcceptedWebhookPayload, installation: ExampleAppAppInstallContext, context: InvocationContext, logger: Logger) => {
189
- logger = childLogger({
190
- 'Activity_ActivityIdentifier': activity?.content?.activity?.activityId,
191
- 'Tracking_Source_Url': activity?.content?.source?.loc,
192
- 'Activity_Name': activity?.content?.activity?.activityNameCustom || activity?.content?.activity?.activityName?.toString()
193
- }, installation, logger, context);
194
-
195
- await triggerBackgroundJob(queueNames.trackActivity, {
196
- payload: activity.content,
197
- organisationBrandId: installation.organisationBrandId,
198
- organisationId: installation.organisationId,
199
- applicationInstallId: installation.applicationInstallId,
200
- causationId: context.invocationId,
201
- correlationId: context.invocationId
202
- } as TrackExampleAppActivityQueueItem, logger)
203
- }
204
-
@@ -1,48 +0,0 @@
1
- import {app, InvocationContext} from '@azure/functions';
2
- import '../app/logging'
3
- import * as winston from 'winston';
4
-
5
- import {deserialiseQueuePayload, queueNames, sendQueueMessageToPoisonQueue} from '../app/jobs';
6
- import {sendActivityToAnotherSoftware} from "../app/services/activites";
7
- import {TrackExampleAppActivityQueueItem} from "../app/models";
8
- import {backgroundJobLogger, childLogger, startServices, tryGetInstallation} from "@seeka-labs/sdk-apps-server-host";
9
-
10
- app.storageQueue('trackActivityQueueHandler', {
11
- queueName: queueNames.trackActivity,
12
- connection: 'AzureWebJobsStorage',
13
- handler: trackActivityQueueHandler
14
- });
15
-
16
- export async function trackActivityQueueHandler(queueItem: unknown, context: InvocationContext): Promise<void> {
17
- const body = typeof queueItem === 'string' ? deserialiseQueuePayload<TrackExampleAppActivityQueueItem>(queueItem) : queueItem as TrackExampleAppActivityQueueItem;
18
- let logger = backgroundJobLogger(queueNames.trackActivity, body, context);
19
- logger.profile(`queue.${queueNames.trackActivity}`)
20
-
21
- await startServices(logger);
22
-
23
- const applicationInstallId = body.applicationInstallId as string || (body.payload as any)?.applicationInstallId as string; // Support legacy payload / previous bug
24
-
25
- const installation = await tryGetInstallation(applicationInstallId as string, true, logger);
26
-
27
- logger = childLogger({
28
- 'Activity_ActivityIdentifier': body?.payload?.seekaActivity?.activity?.activityId,
29
- 'Tracking_Source_Url': body?.payload?.seekaActivity?.source?.loc,
30
- 'Activity_Name': body?.payload?.seekaActivity?.activity?.activityNameCustom || body?.payload?.seekaActivity?.activity?.activityName?.toString()
31
- }, installation, logger, context)
32
-
33
- logger.info('Received request to track activity for ExampleApp', {body});
34
-
35
- try {
36
- await sendActivityToAnotherSoftware(body.payload, installation, context.invocationId, context.invocationId, logger);
37
- } catch (err) {
38
- const ex: any = winston.exceptions.getAllInfo(err);
39
- logger.error('Error handling track ExampleApp activity queue item', {ex});
40
-
41
- await sendQueueMessageToPoisonQueue(queueNames.trackActivity, {
42
- ...body,
43
- correlationId: context.invocationId,
44
- } as TrackExampleAppActivityQueueItem, logger);
45
- }
46
-
47
- logger.profile(`queue.${queueNames.trackActivity}`)
48
- }
@@ -1,49 +0,0 @@
1
- import {app, HttpRequest, HttpResponseInit, InvocationContext} from '@azure/functions';
2
- import '../app/logging'
3
- import {
4
- ExampleAppAppInstallContext,
5
- ExampleAppAppUiClientInitState
6
- } from "@example-org-name/example-app-name-lib";
7
-
8
- import { generateAppUiHttpRequestResponse, HttpMethod} from '@seeka-labs/sdk-apps-server-host';
9
- import {Logger} from "winston";
10
- import {apiRouteHandler} from "../app/api/router";
11
-
12
- // Define your route and handler for the /ui path
13
- app.http('ui', {
14
- methods: ['GET', 'HEAD', 'POST', 'DELETE'],
15
- authLevel: 'anonymous',
16
- route: 'app/{*route}', // Wildcard route under /ui
17
- handler: ui
18
- });
19
-
20
- // Function to serve the React app under /app
21
- export async function ui(req: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
22
- const createBrowserInitState = async (installation: ExampleAppAppInstallContext, logger: Logger ): Promise<ExampleAppAppUiClientInitState> => {
23
- // Under no circumstances should internal or private api keys be exposed to the client/browser via the below object
24
- // Below state will be passed in plain text to the UI app, important to be selective (no spread operators on installation state)
25
- // and not expose any internal or private api keys to the client/browser
26
- return {
27
- exampleInstallSetting1: installation?.installationState?.installationSettings?.exampleInstallSetting1
28
- }
29
- }
30
-
31
- const res = await generateAppUiHttpRequestResponse(
32
- req.url,
33
- req.method as HttpMethod,
34
- req.headers as Headers,
35
- context,
36
- createBrowserInitState,
37
- async (requestedPath, requestedMethod, installation, logger) => {
38
- return await apiRouteHandler(req, context, requestedPath, requestedMethod, installation, logger,)
39
- }
40
- )
41
-
42
- return {
43
- status: res.status,
44
- headers: res.headers as HeadersInit,
45
- cookies: res.cookies,
46
- body: res.body as HttpResponseInit['body'],
47
- jsonBody: res.jsonBody
48
- };
49
- }
@@ -1,24 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "allowJs": true,
4
- "skipLibCheck": true,
5
- "strict": false,
6
- "esModuleInterop": true,
7
- "outDir": "dist",
8
- "rootDir": ".",
9
- "sourceMap": true,
10
- "module": "CommonJS",
11
- "target": "ES6",
12
- "resolveJsonModule": true,
13
- "allowSyntheticDefaultImports": true,
14
- "moduleResolution": "node",
15
- "isolatedModules": false
16
- },
17
- "include": [
18
- "src/**/*.ts",
19
- "src/definitions.d.ts"
20
- ],
21
- "exclude": [
22
- "node_modules"
23
- ]
24
- }
@@ -1,40 +0,0 @@
1
- # Material UI - Create React App example in TypeScript
2
-
3
- ## How to use
4
-
5
- Download the example [or clone the repo](https://github.com/mui/material-ui):
6
-
7
- <!-- #default-branch-switch -->
8
-
9
- ```bash
10
- curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-cra-ts
11
- cd material-ui-cra-ts
12
- ```
13
-
14
- Install it and run:
15
-
16
- ```bash
17
- npm install
18
- npm start
19
- ```
20
-
21
- or:
22
-
23
- <!-- #default-branch-switch -->
24
-
25
- [![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-cra-ts)
26
-
27
- [![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-cra-ts)
28
-
29
- ## The idea behind the example
30
-
31
- This example demonstrates how you can use Material UI with [Create React App](https://github.com/facebookincubator/create-react-app) in [TypeScript](https://github.com/Microsoft/TypeScript).
32
- It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v6.
33
- If you prefer, you can [use styled-components instead](https://mui.com/material-ui/integrations/interoperability/#styled-components).
34
-
35
- ## What's next?
36
-
37
- <!-- #default-branch-switch -->
38
-
39
- You now have a working example project.
40
- You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section.