chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import rootConfig from '../../eslint.config.mjs'
|
||||
|
||||
export default [
|
||||
...rootConfig,
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,36 @@
|
||||
# Monday Integration
|
||||
|
||||
`@botpresshub/monday` is a Botpress integration that integrates with Monday.com, enabling your bots to create Monday.com items.
|
||||
|
||||
## Configuration
|
||||
|
||||
This integration connects to Monday.com with OAuth by default. You can also select manual configuration and provide a personal access token if you prefer to configure access manually.
|
||||
|
||||
### Monday
|
||||
|
||||
You will need to identify the Board IDs of the Monday.com boards you would like your bot to interact with.
|
||||
|
||||
#### OAuth
|
||||
|
||||
Use the authorization button in Botpress to connect your Monday.com account. During the OAuth flow, Monday will ask you to approve access for this integration.
|
||||
|
||||
#### Personal access token
|
||||
|
||||
If you prefer manual configuration, refer to the [Authentication Guide](https://developer.monday.com/api-reference/docs/authentication#get-your-token) in the Monday documentation to learn how to acquire your token.
|
||||
|
||||
#### Board ID
|
||||
|
||||
In order to find your Board ID, log into Monday and navigate to your Board. Then, inspect the URL in your address bar. See the following example:
|
||||
|
||||
```
|
||||
https://<your-workspace-id>.monday.com/boards/9012345678
|
||||
```
|
||||
|
||||
In the URL provided above, the Board ID would be `9012345678`. Keep this (and any other relevant board IDs) around for when configure actions using the Botpress integration.
|
||||
|
||||
### Botpress
|
||||
|
||||
1. Install the Monday integration in your Botpress bot.
|
||||
2. Use the default OAuth configuration and click the authorization button to complete the OAuth flow.
|
||||
3. To configure manually, select **Manual Configuration** and paste your Monday.com personal access token.
|
||||
4. Save configuration.
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 120 120" version="1.1"><path d="M 87.183 66.829 C 85.439 68.458, 83.872 71.163, 83.489 73.207 C 83.032 75.643, 83.337 77.750, 84.460 79.923 C 85.360 81.664, 87.538 83.992, 89.299 85.097 C 91.630 86.559, 93.587 86.972, 96.499 86.614 C 99.475 86.249, 101.361 85.182, 103.867 82.448 C 106.922 79.114, 107.186 78.347, 106.716 74.169 C 106.319 70.653, 105.460 68.907, 103.083 66.782 C 100.578 64.545, 99.013 64, 95.090 64 C 91.115 64, 89.650 64.524, 87.183 66.829" stroke="none" fill="#00d647" fill-rule="evenodd"/><path d="M 38.301 32.772 C 37.091 33.723, 30.904 42.792, 24.551 52.926 C 15.023 68.126, 13 72.024, 13 75.191 C 13 78.128, 13.764 79.863, 16.251 82.576 C 18.642 85.186, 20.558 86.253, 23.501 86.614 C 26.385 86.968, 28.367 86.561, 30.609 85.153 C 32.641 83.877, 37.438 77.266, 44.459 66.065 C 50.367 56.640, 55.672 47.681, 56.247 46.156 C 56.823 44.630, 56.988 41.748, 56.613 39.751 C 56.189 37.490, 54.784 35.155, 52.889 33.560 C 50.561 31.601, 48.749 31.005, 45.173 31.022 C 42.290 31.036, 39.658 31.706, 38.301 32.772" stroke="none" fill="#fc3c54" fill-rule="evenodd"/><path d="M 74.301 32.198 C 73.092 32.782, 66.807 41.639, 60.335 51.880 C 49.562 68.925, 48.564 70.909, 48.533 75.332 C 48.504 79.540, 48.919 80.538, 51.742 83.061 C 53.526 84.654, 56.113 86.241, 57.492 86.587 C 58.872 86.934, 61.128 86.934, 62.508 86.587 C 63.887 86.241, 66.307 84.730, 67.886 83.229 C 69.465 81.728, 75.537 72.794, 81.379 63.374 C 90.495 48.676, 92 45.650, 92 42.024 C 92 38.554, 91.394 37.194, 88.600 34.400 C 85.739 31.539, 84.510 31.011, 80.850 31.068 C 78.457 31.105, 75.511 31.613, 74.301 32.198" stroke="none" fill="#fccc04" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,54 @@
|
||||
import { IntegrationDefinition, z } from '@botpress/sdk'
|
||||
import { configurationSchema, createItemSchema, manualConfigurationSchema } from 'src/misc/custom-schemas'
|
||||
|
||||
export default new IntegrationDefinition({
|
||||
name: 'monday',
|
||||
title: 'Monday',
|
||||
description: 'Manage items in Monday boards.',
|
||||
version: '1.1.5',
|
||||
readme: 'hub.md',
|
||||
icon: 'icon.svg',
|
||||
states: {
|
||||
oAuthCredentials: {
|
||||
type: 'integration',
|
||||
schema: z.object({
|
||||
accessToken: z.string().secret().title('Access Token').describe('The Monday OAuth access token.'),
|
||||
}),
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
createItem: {
|
||||
title: 'Create Item',
|
||||
description: 'Create a new item.',
|
||||
input: { schema: createItemSchema },
|
||||
output: {
|
||||
schema: z.object({}),
|
||||
},
|
||||
},
|
||||
},
|
||||
configuration: {
|
||||
schema: configurationSchema,
|
||||
identifier: {
|
||||
linkTemplateScript: 'linkTemplate.vrl',
|
||||
},
|
||||
},
|
||||
configurations: {
|
||||
manual: {
|
||||
title: 'Manual Configuration',
|
||||
description: 'Configure with your Personal Access Token',
|
||||
schema: manualConfigurationSchema,
|
||||
},
|
||||
},
|
||||
secrets: {
|
||||
CLIENT_ID: {
|
||||
description: 'The client ID of the OAuth app.',
|
||||
},
|
||||
CLIENT_SECRET: {
|
||||
description: 'The client secret of the OAuth app.',
|
||||
},
|
||||
},
|
||||
attributes: {
|
||||
category: 'Project Management',
|
||||
repo: 'botpress',
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
webhookId = to_string!(.webhookId)
|
||||
webhookUrl = to_string!(.webhookUrl)
|
||||
|
||||
"{{ webhookUrl }}/oauth/wizard/start?state={{ webhookId }}"
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@botpresshub/monday",
|
||||
"scripts": {
|
||||
"check:type": "tsc --noEmit",
|
||||
"check:bplint": "bp lint",
|
||||
"build": "bp add -y && bp build",
|
||||
"test": "vitest --run"
|
||||
},
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@botpress/cli": "workspace:*",
|
||||
"@botpress/sdk": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@botpress/client": "workspace:*",
|
||||
"@botpress/common": "workspace:*",
|
||||
"@botpress/sdk": "workspace:*",
|
||||
"axios": "^1.4.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { getMondayClient } from 'src/misc/auth'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
type CreateItem = bp.IntegrationProps['actions']['createItem']
|
||||
|
||||
export const createItem: CreateItem = async ({ input, ctx, client }) => {
|
||||
const mondayClient = await getMondayClient({ client, ctx })
|
||||
|
||||
await mondayClient.createItem(input.boardId, {
|
||||
name: input.itemName,
|
||||
})
|
||||
|
||||
return {}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { RuntimeError } from '@botpress/sdk'
|
||||
import * as actions from 'src/actions'
|
||||
import { getMondayClient } from 'src/misc/auth'
|
||||
import { isOAuthWizardUrl, oauthWizardHandler } from './oauth-wizard'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export default new bp.Integration({
|
||||
register: async ({ client, ctx }) => {
|
||||
try {
|
||||
const mondayClient = await getMondayClient({ client, ctx })
|
||||
await mondayClient.validateAccessToken()
|
||||
|
||||
await client.configureIntegration({
|
||||
identifier: ctx.webhookId,
|
||||
})
|
||||
} catch (thrown) {
|
||||
if (thrown instanceof RuntimeError) {
|
||||
throw thrown
|
||||
}
|
||||
|
||||
const message = thrown instanceof Error ? thrown.message : String(thrown)
|
||||
throw new RuntimeError(`Failed to configure Monday integration. Please reconnect your account. (${message})`)
|
||||
}
|
||||
},
|
||||
unregister: async () => {},
|
||||
actions,
|
||||
channels: {},
|
||||
handler: async (props) => {
|
||||
if (isOAuthWizardUrl(props.req.path)) {
|
||||
return await oauthWizardHandler(props)
|
||||
}
|
||||
return { status: 404, body: 'Invalid endpoint' }
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,57 @@
|
||||
import { RuntimeError, isApiError } from '@botpress/sdk'
|
||||
import { MondayClient } from './monday-client'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
type AuthProps = {
|
||||
client: bp.Client
|
||||
ctx: bp.Context
|
||||
}
|
||||
|
||||
export const getOAuthAccessToken = async ({ client, ctx }: AuthProps) => {
|
||||
try {
|
||||
const { state } = await client.getState({
|
||||
type: 'integration',
|
||||
name: 'oAuthCredentials',
|
||||
id: ctx.integrationId,
|
||||
})
|
||||
|
||||
return state?.payload.accessToken || undefined
|
||||
} catch (thrown) {
|
||||
if (isApiError(thrown) && thrown.code === 404) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
throw thrown
|
||||
}
|
||||
}
|
||||
|
||||
export const getMondayClient = async (props: AuthProps) => {
|
||||
if (props.ctx.configurationType === 'manual') {
|
||||
const { personalAccessToken } = props.ctx.configuration
|
||||
|
||||
if (!personalAccessToken) {
|
||||
throw new RuntimeError('Monday credentials are missing. Please provide a personal access token.')
|
||||
}
|
||||
|
||||
return MondayClient.create({ authorization: personalAccessToken })
|
||||
}
|
||||
|
||||
let oAuthAccessToken: string | undefined
|
||||
try {
|
||||
oAuthAccessToken = await getOAuthAccessToken(props)
|
||||
} catch (thrown) {
|
||||
const message = thrown instanceof Error ? thrown.message : String(thrown)
|
||||
throw new RuntimeError(`Failed to load Monday OAuth credentials. Please reconnect your account. (${message})`)
|
||||
}
|
||||
|
||||
if (!oAuthAccessToken) {
|
||||
throw new RuntimeError(
|
||||
'Monday credentials are missing. Please connect your Monday account or provide a personal access token.'
|
||||
)
|
||||
}
|
||||
|
||||
return createOAuthMondayClient(oAuthAccessToken)
|
||||
}
|
||||
|
||||
export const createOAuthMondayClient = (accessToken: string) =>
|
||||
MondayClient.create({ authorization: `Bearer ${accessToken}` })
|
||||
@@ -0,0 +1,20 @@
|
||||
import { z } from '@botpress/sdk'
|
||||
|
||||
export const configurationSchema = z.object({})
|
||||
|
||||
export const manualConfigurationSchema = z.object({
|
||||
personalAccessToken: z
|
||||
.string()
|
||||
.min(1)
|
||||
.secret()
|
||||
.title('Personal Access Token')
|
||||
.describe('A Monday.com personal access token with sufficient access to manage items on your Monday.com boards.'),
|
||||
})
|
||||
|
||||
export const createItemSchema = z.object({
|
||||
boardId: z
|
||||
.string()
|
||||
.title('Board ID')
|
||||
.describe("The board's unique identifier. The new item will be added to this board."),
|
||||
itemName: z.string().min(1).title('Name').describe("The new item's name."),
|
||||
})
|
||||
@@ -0,0 +1,46 @@
|
||||
const QUERY_INPUT = Symbol('graphqlInputType')
|
||||
const QUERY_RESPONSE = Symbol('graphqlResponseType')
|
||||
|
||||
type GraphQLQuery<TInput, TResponse> = {
|
||||
query: string
|
||||
[QUERY_INPUT]: TInput
|
||||
[QUERY_RESPONSE]: TResponse
|
||||
}
|
||||
|
||||
export const GRAPHQL_QUERIES = {
|
||||
validateAccessToken: {
|
||||
query: `
|
||||
query ValidateAccessToken {
|
||||
boards(limit: 1) {
|
||||
id
|
||||
}
|
||||
}`,
|
||||
[QUERY_INPUT]: {} as Record<string, never>,
|
||||
[QUERY_RESPONSE]: {} as {
|
||||
boards: {
|
||||
id: string
|
||||
}[]
|
||||
},
|
||||
},
|
||||
createItem: {
|
||||
query: `
|
||||
mutation CreateNewItem($boardId: ID!, $itemName: String!) {
|
||||
create_item (board_id: $boardId, item_name: $itemName) {
|
||||
id
|
||||
}
|
||||
}`,
|
||||
[QUERY_INPUT]: {} as {
|
||||
boardId: string
|
||||
itemName: string
|
||||
},
|
||||
[QUERY_RESPONSE]: {} as {
|
||||
create_item: {
|
||||
id: string
|
||||
}
|
||||
},
|
||||
},
|
||||
} as const satisfies Record<string, GraphQLQuery<object, object>>
|
||||
|
||||
export type GRAPHQL_QUERIES = typeof GRAPHQL_QUERIES
|
||||
export type QUERY_INPUT = typeof QUERY_INPUT
|
||||
export type QUERY_RESPONSE = typeof QUERY_RESPONSE
|
||||
@@ -0,0 +1,104 @@
|
||||
import axios, { Axios } from 'axios'
|
||||
import { GRAPHQL_QUERIES, QUERY_INPUT, QUERY_RESPONSE } from './graphql-queries'
|
||||
|
||||
export type MondayClientConfiguration = {
|
||||
authorization: string
|
||||
}
|
||||
|
||||
type MondayOAuthTokenResponse = {
|
||||
access_token: string
|
||||
}
|
||||
|
||||
export type MondayOAuthCredentials = {
|
||||
accessToken: string
|
||||
}
|
||||
|
||||
export type ExchangeCodeForTokensInput = {
|
||||
clientId: string
|
||||
clientSecret: string
|
||||
redirectUri: string
|
||||
code: string
|
||||
}
|
||||
|
||||
export type CreateItemOptions = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export type Item = {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export type ItemsPageResponse = {
|
||||
items: Item[]
|
||||
nextToken: string | undefined
|
||||
}
|
||||
|
||||
export const exchangeCodeForTokens = async ({
|
||||
clientId,
|
||||
clientSecret,
|
||||
redirectUri,
|
||||
code,
|
||||
}: ExchangeCodeForTokensInput): Promise<MondayOAuthCredentials> => {
|
||||
const response = await axios.post<MondayOAuthTokenResponse>('https://auth.monday.com/oauth2/token', {
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
redirect_uri: redirectUri,
|
||||
code,
|
||||
})
|
||||
|
||||
return {
|
||||
accessToken: response.data.access_token,
|
||||
}
|
||||
}
|
||||
|
||||
export class MondayClient {
|
||||
private constructor(private readonly _client: Axios) {}
|
||||
|
||||
public static create(config: MondayClientConfiguration) {
|
||||
const client = axios.create({
|
||||
baseURL: 'https://api.monday.com/v2',
|
||||
timeout: 10_000,
|
||||
headers: {
|
||||
Authorization: config.authorization,
|
||||
'API-Version': '2023-07',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
return new MondayClient(client)
|
||||
}
|
||||
|
||||
private async _executeGraphqlQuery<K extends keyof GRAPHQL_QUERIES>(
|
||||
queryName: K,
|
||||
variables: GRAPHQL_QUERIES[K][QUERY_INPUT]
|
||||
): Promise<GRAPHQL_QUERIES[K][QUERY_RESPONSE]> {
|
||||
const response = await this._client.post('', {
|
||||
query: GRAPHQL_QUERIES[queryName].query,
|
||||
variables,
|
||||
})
|
||||
|
||||
return response.data.data
|
||||
}
|
||||
|
||||
public async validateAccessToken(): Promise<void> {
|
||||
try {
|
||||
const response = await this._executeGraphqlQuery('validateAccessToken', {})
|
||||
if (!Array.isArray(response.boards)) {
|
||||
throw new Error('Monday credentials validation returned an unexpected response.')
|
||||
}
|
||||
} catch (thrown) {
|
||||
throw thrown instanceof Error ? thrown : new Error('Monday credentials validation failed.')
|
||||
}
|
||||
}
|
||||
|
||||
public async createItem(
|
||||
boardId: string,
|
||||
item: CreateItemOptions
|
||||
): Promise<GRAPHQL_QUERIES['createItem'][QUERY_RESPONSE]> {
|
||||
return await this._executeGraphqlQuery('createItem', {
|
||||
boardId,
|
||||
itemName: item.name,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import * as wizard from './wizard'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export const oauthWizardHandler: bp.IntegrationProps['handler'] = async (props) => {
|
||||
const { logger } = props
|
||||
try {
|
||||
return await wizard.handler(props)
|
||||
} catch (thrown) {
|
||||
const error = thrown instanceof Error ? thrown : new Error(String(thrown))
|
||||
logger.forBot().error(`OAuth wizard error: ${error.message}`)
|
||||
return wizard.redirectToInterstitial(false, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
export const isOAuthWizardUrl = wizard.isOAuthWizardUrl
|
||||
@@ -0,0 +1,182 @@
|
||||
import * as oauthWizard from '@botpress/common/src/oauth-wizard'
|
||||
import { RuntimeError, type Response } from '@botpress/sdk'
|
||||
import { createOAuthMondayClient } from 'src/misc/auth'
|
||||
import { exchangeCodeForTokens } from 'src/misc/monday-client'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
const OAUTH_CONFIGURATION_ERROR_MESSAGE = 'Unable to complete the Monday OAuth setup. Please try again.'
|
||||
const DISABLE_INTERSTITIAL_HEADER = { 'x-bp-disable-interstitial': 'true' } as const
|
||||
const SCOPES = 'boards:read boards:write'
|
||||
|
||||
type WizardHandler = oauthWizard.WizardStepHandler<bp.HandlerProps>
|
||||
|
||||
const getMondayInstallUrl = () => {
|
||||
const url = new URL('https://auth.monday.com/oauth2/authorize')
|
||||
url.search = new URLSearchParams({
|
||||
client_id: bp.secrets.CLIENT_ID,
|
||||
response_type: 'install',
|
||||
}).toString()
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
export const handler = async (props: bp.HandlerProps) => {
|
||||
const wizard = new oauthWizard.OAuthWizardBuilder(props)
|
||||
.addStep({ id: 'start', handler: _startHandler })
|
||||
.addStep({ id: 'confirm-install', handler: _confirmInstallHandler })
|
||||
.addStep({ id: 'oauth-redirect', handler: _oauthRedirectHandler })
|
||||
.addStep({ id: 'oauth-callback', handler: _oauthCallbackHandler })
|
||||
.build()
|
||||
|
||||
return await wizard.handleRequest()
|
||||
}
|
||||
|
||||
const _startHandler: WizardHandler = ({ ctx, responses }) => {
|
||||
return responses.displayButtons({
|
||||
pageTitle: 'Connect Monday.com',
|
||||
htmlOrMarkdownPageContents:
|
||||
'1. Click **Next step** to open the Monday.com install page in a new tab.\n' +
|
||||
'2. Install the Botpress app in your Monday.com workspace.\n' +
|
||||
'3. Return to this wizard and confirm the installation to start the OAuth connection.' +
|
||||
`
|
||||
<script>
|
||||
function installMondayAppAndContinue() {
|
||||
window.open(${JSON.stringify(getMondayInstallUrl())}, '_blank', 'noopener,noreferrer');
|
||||
window.location.href = ${JSON.stringify(getWizardStepUrl('confirm-install', ctx).toString())};
|
||||
}
|
||||
</script>
|
||||
`,
|
||||
buttons: [
|
||||
{
|
||||
action: 'javascript',
|
||||
label: 'Next step',
|
||||
callFunction: 'installMondayAppAndContinue',
|
||||
buttonType: 'primary',
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
const _confirmInstallHandler: WizardHandler = ({ responses }) => {
|
||||
return responses.displayButtons({
|
||||
pageTitle: 'Confirm Monday.com installation',
|
||||
htmlOrMarkdownPageContents:
|
||||
'Have you installed the Botpress app in your Monday.com workspace?\n\n' +
|
||||
'Start OAuth only after the Monday app is installed in your workspace.',
|
||||
buttons: [
|
||||
{
|
||||
action: 'navigate',
|
||||
label: 'Yes, continue',
|
||||
navigateToStep: 'oauth-redirect',
|
||||
buttonType: 'primary',
|
||||
},
|
||||
{
|
||||
action: 'navigate',
|
||||
label: 'Go back',
|
||||
navigateToStep: 'start',
|
||||
buttonType: 'secondary',
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
const _oauthRedirectHandler: WizardHandler = async ({ ctx, responses }) => {
|
||||
try {
|
||||
const url = new URL('https://auth.monday.com/oauth2/authorize')
|
||||
const params = new URLSearchParams({
|
||||
client_id: bp.secrets.CLIENT_ID,
|
||||
redirect_uri: getOAuthRedirectUri().toString(),
|
||||
response_type: 'code',
|
||||
scope: SCOPES,
|
||||
state: ctx.webhookId,
|
||||
force_install_if_needed: String(true),
|
||||
})
|
||||
url.search = params.toString()
|
||||
|
||||
return responses.redirectToExternalUrl(url.toString())
|
||||
} catch (thrown) {
|
||||
return responses.endWizard({
|
||||
success: false,
|
||||
errorMessage: _formatWizardError(thrown, OAUTH_CONFIGURATION_ERROR_MESSAGE),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const _oauthCallbackHandler: WizardHandler = async ({ ctx, client, query, responses, setIntegrationIdentifier }) => {
|
||||
try {
|
||||
const code = query.get('code')
|
||||
const state = query.get('state')
|
||||
|
||||
if (!code) {
|
||||
return responses.endWizard({ success: false, errorMessage: 'Missing OAuth code' })
|
||||
}
|
||||
|
||||
if (state !== ctx.webhookId) {
|
||||
return responses.endWizard({ success: false, errorMessage: 'Invalid OAuth state' })
|
||||
}
|
||||
|
||||
const credentials = await _exchangeCodeForTokens({ code, redirectUri: getOAuthRedirectUri().toString() })
|
||||
const mondayClient = createOAuthMondayClient(credentials.accessToken)
|
||||
await mondayClient.validateAccessToken()
|
||||
|
||||
await client.setState({
|
||||
type: 'integration',
|
||||
name: 'oAuthCredentials',
|
||||
id: ctx.integrationId,
|
||||
payload: {
|
||||
accessToken: credentials.accessToken,
|
||||
},
|
||||
})
|
||||
|
||||
setIntegrationIdentifier(ctx.webhookId)
|
||||
|
||||
return responses.endWizard({ success: true })
|
||||
} catch (thrown) {
|
||||
return responses.endWizard({
|
||||
success: false,
|
||||
errorMessage: _formatWizardError(thrown, OAUTH_CONFIGURATION_ERROR_MESSAGE),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const _formatWizardError = (thrown: unknown, fallbackMessage: string) => {
|
||||
const message = thrown instanceof Error ? thrown.message : String(thrown)
|
||||
return message ? `${fallbackMessage} (${message})` : fallbackMessage
|
||||
}
|
||||
|
||||
const _exchangeCodeForTokens = async ({ code, redirectUri }: { code: string; redirectUri: string }) => {
|
||||
try {
|
||||
return await exchangeCodeForTokens({
|
||||
clientId: bp.secrets.CLIENT_ID,
|
||||
clientSecret: bp.secrets.CLIENT_SECRET,
|
||||
redirectUri,
|
||||
code,
|
||||
})
|
||||
} catch (thrown) {
|
||||
const message = thrown instanceof Error ? thrown.message : String(thrown)
|
||||
throw new RuntimeError(`Failed to exchange Monday OAuth code for tokens. ${message}`)
|
||||
}
|
||||
}
|
||||
|
||||
const getWizardStepUrl = (stepId: string, ctx?: { webhookId: string }) => oauthWizard.getWizardStepUrl(stepId, ctx)
|
||||
|
||||
const getOAuthRedirectUri = () => getWizardStepUrl('oauth-callback')
|
||||
|
||||
export const isOAuthWizardUrl = oauthWizard.isOAuthWizardUrl
|
||||
|
||||
const redirectToUrl = (url: URL): Response => ({
|
||||
status: 303,
|
||||
headers: {
|
||||
...DISABLE_INTERSTITIAL_HEADER,
|
||||
location: url.toString(),
|
||||
},
|
||||
})
|
||||
|
||||
export const redirectToInterstitial = (success: boolean, message?: string): Response => {
|
||||
const url = new URL(
|
||||
`${process.env.BP_WEBHOOK_URL?.replace('webhook', 'app')}/oauth/interstitial?success=${success}${
|
||||
message ? `&errorMessage=${message}` : ''
|
||||
}`
|
||||
)
|
||||
|
||||
return redirectToUrl(url)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "preact",
|
||||
"paths": { "*": ["./*"] },
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": [".botpress/**/*", "definitions/**/*", "src/**/*", "*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import config from '../../vitest.config'
|
||||
export default config
|
||||
Reference in New Issue
Block a user