Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/backend/src/apps/aibots/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import sendQuery from './send-query'

export default [sendQuery]
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { IDataOutMetadata, IExecutionStep } from '@plumber/types'

import { dataOutSchema } from './schema'

async function getDataOutMetadata(
step: IExecutionStep,
): Promise<IDataOutMetadata> {
const { dataOut: rawDataOut } = step

if (!rawDataOut) {
return null
}

dataOutSchema.parse(rawDataOut)

const metadata: IDataOutMetadata = {
data: {
response: {
content: {
label: 'Response',
},
},
},
}

// Recursively hide all fields except the ones explicitly defined in metadata
function hideAllFields(obj: any, metadataPath: any = {}) {
const result: any = { ...metadataPath }

if (obj && typeof obj === 'object' && !Array.isArray(obj)) {
Object.entries(obj).forEach(([key, value]) => {
// If this key is already defined in metadata, recurse into it
if (metadataPath[key]) {
result[key] = hideAllFields(value, metadataPath[key])
} else {
// Otherwise, hide this field
result[key] = { isHidden: true }
}
})
}

return result
}

const finalMetadata = hideAllFields(rawDataOut.data, metadata.data)

return {
data: finalMetadata,
}
}

export default getDataOutMetadata
98 changes: 98 additions & 0 deletions packages/backend/src/apps/aibots/actions/send-query/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { IRawAction } from '@plumber/types'

import { ZodError } from 'zod'
import { fromZodError } from 'zod-validation-error'

import HttpError from '@/errors/http'
import StepError, { GenericSolution } from '@/errors/step'

import getDataOutMetadata from './get-data-out-metadata'
import { parametersSchema } from './schema'

const action: IRawAction = {
name: 'Send Query',
key: 'sendQuery',
description: 'Sends a query to your customised AI Bot',
arguments: [
{
label: 'Bot API key',
key: 'apiKey',
description: 'Enter the API key for your bot',
type: 'string' as const,
required: true,
variables: false,
},
{
label: 'Query',
key: 'query',
type: 'string' as const,
required: true,
variables: true,
},
],

getDataOutMetadata,

async run($) {
try {
const parameters = parametersSchema.parse($.step.parameters)

const { query, apiKey } = parameters

const headers = { 'X-ATLAS-Key': apiKey }

// create a new chat
const response = await $.http.post(
`/chats`,
{ name: 'Chat from Plumber' },
{ headers },
)

// /chats/:chatId/messages requires FormData
const chatId = response.data.id
const formData = new FormData()
formData.append('content', query)

// send the query and get the response
const chatResponse = await $.http.post(
`/chats/:chatId/messages`,
formData,
{
headers,
urlPathParams: { chatId },
},
)

$.setActionItem({
raw: {
data: chatResponse.data,
},
})
} catch (error) {
// if the user enters an invalid API key, aibots will return a 401 error
if (error instanceof HttpError && error.response.status === 401) {
throw new StepError(
'Invalid API key',
'Please check that you have entered a valid API key',
$.step.position,
$.app.name,
)
}

if (error instanceof ZodError) {
const firstError = fromZodError(error).details[0]

throw new StepError(
`${firstError.message} under set up step`,
GenericSolution.ReconfigureInvalidField,
$.step.position,
$.app.name,
)
}

throw error
}
},
}

export default action
14 changes: 14 additions & 0 deletions packages/backend/src/apps/aibots/actions/send-query/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from 'zod'

export const parametersSchema = z.object({
apiKey: z.string().min(1),
query: z.string().min(1),
})

export const dataOutSchema = z.object({
data: z.object({
response: z.object({
content: z.string(),
}),
}),
})
39 changes: 39 additions & 0 deletions packages/backend/src/apps/aibots/assets/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions packages/backend/src/apps/aibots/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { IApp } from '@plumber/types'

import actions from './actions'

const app: IApp = {
name: 'AIBots',
key: 'aibots',
description: 'Integrate with your customised AI Bot',
iconUrl: '{BASE_URL}/apps/aibots/assets/favicon.svg',
authDocUrl: '',
// TODO: update this to PROD once aibots releases the PROD API
baseUrl: 'https://aibots.gov.sg',
apiBaseUrl: 'https://api.uat.aibots.gov.sg/v1.0/api',
primaryColor: '',
category: 'ai',
// TODO: add the actual auth here
// there is currently no verification available for aibots
// its done directly in the action itself
// (as requested by AiBots team)
// auth,
actions,
}

export default app
2 changes: 2 additions & 0 deletions packages/backend/src/apps/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { IApp } from '@plumber/types'

import aibotsApp from './aibots'
import aisayApp from './aisay'
import calculatorApp from './calculator'
import customApiApp from './custom-api'
Expand Down Expand Up @@ -40,6 +41,7 @@ const apps: Record<string, IApp> = {
[twilioApp.key]: twilioApp,
[vaultWorkspaceApp.key]: vaultWorkspaceApp,
[webhookApp.key]: webhookApp,
[aibotsApp.key]: aibotsApp,
[aisayApp.key]: aisayApp,
[gathersgApp.key]: gathersgApp,
}
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/graphql/queries/get-apps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { IApp } from '@plumber/types'

import aibotsApp from '@/apps/aibots'
import aisayApp from '@/apps/aisay'
import calculatorApp from '@/apps/calculator'
import customApiApp from '@/apps/custom-api'
Expand Down Expand Up @@ -52,6 +53,7 @@ export const ACTION_APPS_RANKING = [
postmanSmsApp.key,
telegramBotApp.key,
slackApp.key,
aibotsApp.key,
aisayApp.key,
gathersgApp.key,
customApiApp.key,
Expand Down
4 changes: 2 additions & 2 deletions packages/backend/src/helpers/http-client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ export default function createHttpClient({

if (
(status === 401 || status === 403) &&
$.app.auth.refreshToken &&
!$.app.auth.isRefreshTokenRequested
$.app?.auth?.refreshToken &&
!$.app?.auth?.isRefreshTokenRequested
) {
$.app.auth.isRefreshTokenRequested = true
await $.app.auth.refreshToken($)
Expand Down