chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import { addCommentToTaskInputSchema } from '../misc/custom-schemas'
|
||||
import type { IntegrationProps } from '../misc/types'
|
||||
|
||||
import { getClient } from '../utils'
|
||||
|
||||
export const addCommentToTask: IntegrationProps['actions']['addCommentToTask'] = async ({ ctx, input, logger }) => {
|
||||
const validatedInput = addCommentToTaskInputSchema.parse(input)
|
||||
const asanaClient = getClient(ctx.configuration)
|
||||
let response
|
||||
try {
|
||||
response = await asanaClient.addCommentToTask(validatedInput.taskId, validatedInput.comment)
|
||||
logger.forBot().info(`Successful - Add Comment to Task - ${response.text}`)
|
||||
} catch (error) {
|
||||
logger.forBot().debug(`'Add Comment to Task' exception ${JSON.stringify(error)}`)
|
||||
response = {}
|
||||
}
|
||||
return { text: response.text || '' }
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { createTaskInputSchema } from '../misc/custom-schemas'
|
||||
import type { IntegrationProps } from '../misc/types'
|
||||
|
||||
import { getClient } from '../utils'
|
||||
|
||||
export const createTask: IntegrationProps['actions']['createTask'] = async ({ ctx, input, logger }) => {
|
||||
const validatedInput = createTaskInputSchema.parse(input)
|
||||
const asanaClient = getClient(ctx.configuration)
|
||||
const task = {
|
||||
workspace: ctx.configuration.workspaceGid,
|
||||
name: validatedInput.name,
|
||||
notes: validatedInput.notes || undefined,
|
||||
assignee: validatedInput.assignee || undefined,
|
||||
projects: validatedInput.projects?.split(',').map((project) => project.trim()) || undefined,
|
||||
parent: validatedInput.parent || undefined,
|
||||
due_on: validatedInput.due_on || validatedInput.start_on || undefined,
|
||||
start_on: validatedInput.start_on || undefined,
|
||||
}
|
||||
let response
|
||||
try {
|
||||
response = await asanaClient.createTask(task)
|
||||
logger.forBot().info(`Successful - Create Task - ${response.permalink_url}`)
|
||||
} catch (error) {
|
||||
logger.forBot().debug(`'Create Task' exception ${JSON.stringify(error)}`)
|
||||
response = {}
|
||||
}
|
||||
return { permalink_url: response.permalink_url || '' }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { findUserInputSchema } from '../misc/custom-schemas'
|
||||
import type { IntegrationProps } from '../misc/types'
|
||||
|
||||
import { getClient } from '../utils'
|
||||
|
||||
export const findUser: IntegrationProps['actions']['findUser'] = async ({ ctx, input, logger }) => {
|
||||
const validatedInput = findUserInputSchema.parse(input)
|
||||
const asanaClient = getClient(ctx.configuration)
|
||||
let response
|
||||
try {
|
||||
response = await asanaClient.findUser(validatedInput.userEmail)
|
||||
logger.forBot().info(`Successful - Find User - ${response.name}`)
|
||||
} catch (error) {
|
||||
logger.forBot().debug(`'Find User' exception ${JSON.stringify(error)}`)
|
||||
response = {}
|
||||
}
|
||||
return response
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { addCommentToTask } from './add-comment-to-task'
|
||||
import { createTask } from './create-task'
|
||||
import { findUser } from './find-user'
|
||||
import { updateTask } from './update-task'
|
||||
|
||||
export default { createTask, updateTask, findUser, addCommentToTask }
|
||||
@@ -0,0 +1,26 @@
|
||||
import { updateTaskInputSchema } from '../misc/custom-schemas'
|
||||
import type { IntegrationProps } from '../misc/types'
|
||||
|
||||
import { getClient } from '../utils'
|
||||
|
||||
export const updateTask: IntegrationProps['actions']['updateTask'] = async ({ ctx, input, logger }) => {
|
||||
const validatedInput = updateTaskInputSchema.parse(input)
|
||||
const asanaClient = getClient(ctx.configuration)
|
||||
const task = {
|
||||
name: validatedInput.name,
|
||||
notes: validatedInput.notes || undefined,
|
||||
assignee: validatedInput.assignee || undefined,
|
||||
due_on: validatedInput.due_on || validatedInput.start_on || undefined,
|
||||
start_on: validatedInput.start_on || undefined,
|
||||
completed: validatedInput.completed === 'true' ? true : undefined,
|
||||
}
|
||||
let response
|
||||
try {
|
||||
response = await asanaClient.updateTask(validatedInput.taskId, task)
|
||||
logger.forBot().info(`Successful - Update Task - ${response.permalink_url}`)
|
||||
} catch (error) {
|
||||
logger.forBot().debug(`'Update Task' exception ${JSON.stringify(error)}`)
|
||||
response = {}
|
||||
}
|
||||
return { permalink_url: response.permalink_url || '' }
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import * as asana from 'asana'
|
||||
|
||||
export class AsanaApi {
|
||||
private _client: asana.Client
|
||||
|
||||
public constructor(apiToken: string) {
|
||||
this._client = asana.Client.create({
|
||||
defaultHeaders: {
|
||||
'Asana-Enable': 'new_goal_memberships,new_user_task_lists',
|
||||
},
|
||||
}).useAccessToken(apiToken)
|
||||
}
|
||||
|
||||
public async createTask(
|
||||
task: asana.resources.Tasks.CreateParams & { workspace: string }
|
||||
): Promise<asana.resources.Tasks.Type> {
|
||||
return await this._client.tasks.create(task)
|
||||
}
|
||||
|
||||
public async updateTask(
|
||||
taskId: string,
|
||||
task: asana.resources.Tasks.UpdateParams
|
||||
): Promise<asana.resources.Tasks.Type> {
|
||||
return await this._client.tasks.update(taskId, task)
|
||||
}
|
||||
|
||||
public async addCommentToTask(taskId: string, comment: string): Promise<asana.resources.Stories.Type> {
|
||||
return await this._client.tasks.addComment(taskId, { text: comment })
|
||||
}
|
||||
|
||||
public async findUser(userEmail: string): Promise<asana.resources.Users.Type> {
|
||||
return await this._client.users.findById(userEmail)
|
||||
}
|
||||
|
||||
public async findAllUsers(workspace: string): Promise<asana.resources.Users.Type[]> {
|
||||
const users = await this._client.users.findAll({ workspace })
|
||||
return users.data as asana.resources.Users.Type[]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export const INTEGRATION_NAME = 'asana'
|
||||
@@ -0,0 +1,65 @@
|
||||
import * as sdk from '@botpress/sdk'
|
||||
import {
|
||||
createTaskInputSchema,
|
||||
createTaskOutputSchema,
|
||||
updateTaskInputSchema,
|
||||
updateTaskOutputSchema,
|
||||
findUserInputSchema,
|
||||
findUserOutputSchema,
|
||||
addCommentToTaskInputSchema,
|
||||
addCommentToTaskOutputSchema,
|
||||
} from '../misc/custom-schemas'
|
||||
|
||||
type SdkActions = NonNullable<sdk.IntegrationDefinitionProps['actions']>
|
||||
type SdkAction = SdkActions[string]
|
||||
|
||||
const createTask = {
|
||||
title: 'Create Task',
|
||||
description: 'Create Task',
|
||||
input: {
|
||||
schema: createTaskInputSchema,
|
||||
},
|
||||
output: {
|
||||
schema: createTaskOutputSchema,
|
||||
},
|
||||
} satisfies SdkAction
|
||||
|
||||
const updateTask = {
|
||||
title: 'Update Task',
|
||||
description: 'Update Task by taskId',
|
||||
input: {
|
||||
schema: updateTaskInputSchema,
|
||||
},
|
||||
output: {
|
||||
schema: updateTaskOutputSchema,
|
||||
},
|
||||
} satisfies SdkAction
|
||||
|
||||
const findUser = {
|
||||
title: 'Find User',
|
||||
description: 'Find User by userId',
|
||||
input: {
|
||||
schema: findUserInputSchema,
|
||||
},
|
||||
output: {
|
||||
schema: findUserOutputSchema,
|
||||
},
|
||||
} satisfies SdkAction
|
||||
|
||||
const addCommentToTask = {
|
||||
title: 'Add Comment to Task',
|
||||
description: 'Add Comment to Task, by task ID',
|
||||
input: {
|
||||
schema: addCommentToTaskInputSchema,
|
||||
},
|
||||
output: {
|
||||
schema: addCommentToTaskOutputSchema,
|
||||
},
|
||||
} satisfies SdkAction
|
||||
|
||||
export const actions = {
|
||||
createTask,
|
||||
updateTask,
|
||||
findUser,
|
||||
addCommentToTask,
|
||||
} satisfies SdkActions
|
||||
@@ -0,0 +1,13 @@
|
||||
import { IntegrationDefinitionProps, messages } from '@botpress/sdk'
|
||||
|
||||
export const channels = {
|
||||
channel: {
|
||||
title: 'Channel',
|
||||
description: 'The channel for Asana',
|
||||
messages: {
|
||||
...messages.defaults,
|
||||
markdown: messages.markdown,
|
||||
bloc: messages.markdownBloc,
|
||||
},
|
||||
},
|
||||
} satisfies IntegrationDefinitionProps['channels']
|
||||
@@ -0,0 +1,32 @@
|
||||
import { z, IntegrationDefinitionProps } from '@botpress/sdk'
|
||||
|
||||
export { actions } from './actions'
|
||||
export { channels } from './channels'
|
||||
|
||||
export const configuration = {
|
||||
schema: z.object({
|
||||
apiToken: z.string().min(1).title('API Token').describe('API Token'),
|
||||
workspaceGid: z.string().min(1).title('Workspace Global ID').describe('Workspace Global ID'),
|
||||
}),
|
||||
} satisfies IntegrationDefinitionProps['configuration']
|
||||
|
||||
export const states = {
|
||||
// voidStateOne: {
|
||||
// type: 'integration',
|
||||
// schema: z.object({
|
||||
// dataField: z.string(),
|
||||
// }),
|
||||
// },
|
||||
// voidStateTwo: {
|
||||
// type: 'conversation',
|
||||
// schema: z.object({
|
||||
// otherDataField: z.string(),
|
||||
// }),
|
||||
// },
|
||||
} satisfies IntegrationDefinitionProps['states']
|
||||
|
||||
export const user = {
|
||||
tags: {
|
||||
// id: {},
|
||||
},
|
||||
} satisfies IntegrationDefinitionProps['user']
|
||||
@@ -0,0 +1,14 @@
|
||||
import { reporting } from '@botpress/sdk-addons'
|
||||
import actions from './actions'
|
||||
import { register, unregister, channels, handler } from './setup'
|
||||
import * as bp from '.botpress'
|
||||
|
||||
const integration = new bp.Integration({
|
||||
register,
|
||||
unregister,
|
||||
actions,
|
||||
channels,
|
||||
handler,
|
||||
})
|
||||
|
||||
export default reporting.wrapIntegration(integration)
|
||||
@@ -0,0 +1,95 @@
|
||||
import { z } from '@botpress/sdk'
|
||||
import { photoSchema, workspaceSchema } from './sub-schemas'
|
||||
|
||||
export const createTaskInputSchema = z.object({
|
||||
name: z.string().describe('The name of the task (e.g. "My Test Task")').title('Name'),
|
||||
notes: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('The description of the task (Optional) (e.g. "This is my other task created using the Asana API")')
|
||||
.title('Notes'),
|
||||
assignee: z
|
||||
.string()
|
||||
.optional()
|
||||
.default('me')
|
||||
.describe(
|
||||
'The ID of the user who will be assigned to the task or "me" to assign to the current user (Optional) (e.g. "1215207682932839") (Default: "me")'
|
||||
)
|
||||
.title('Assignee'),
|
||||
projects: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
'The project IDs should be strings separated by commas (Optional) (e.g. "1205199808673678, 1215207282932839").'
|
||||
)
|
||||
.title('Projects'),
|
||||
parent: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('The ID of the parent task (Optional) (e.g. "1205206556256028")')
|
||||
.title('Parent'),
|
||||
start_on: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('The start date of the task in YYYY-MM-DD format (Optional) (e.g. "2023-08-13")')
|
||||
.title('Start On'),
|
||||
due_on: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('The due date of the task without a specific time in YYYY-MM-DD format (Optional) (e.g. "2023-08-15")')
|
||||
.title('Due On'),
|
||||
})
|
||||
|
||||
export const taskOutputSchema = z.object({
|
||||
permalink_url: z.string().describe('The permalink url').title('Permalink Url'),
|
||||
})
|
||||
export const createTaskOutputSchema = taskOutputSchema
|
||||
|
||||
export const updateTaskInputSchema = createTaskInputSchema
|
||||
.omit({
|
||||
projects: true,
|
||||
parent: true,
|
||||
})
|
||||
.extend({
|
||||
taskId: z.string().describe('Task ID to update').title('Task ID'),
|
||||
name: z.string().optional().describe('The name of the task (Optional) (e.g. "My Test Task")').title('Name'),
|
||||
assignee: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
'The ID of the user who will be assigned to the task or "me" to assign to the current user (Optional) (e.g. "1215207682932839")'
|
||||
)
|
||||
.title('Assignee'),
|
||||
completed: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
'If the task is completed, enter "true" (without quotes), otherwise it will keep its previous status. (Optional)'
|
||||
)
|
||||
.title('Completed'),
|
||||
})
|
||||
export const updateTaskOutputSchema = taskOutputSchema
|
||||
|
||||
export const findUserInputSchema = z.object({
|
||||
userEmail: z.string().describe('User Email (e.g. "mrsomebody@example.com")').title('User Email'),
|
||||
})
|
||||
|
||||
export const findUserOutputSchema = z
|
||||
.object({
|
||||
gid: z.string().describe('The GID of the User').title('GID'),
|
||||
name: z.string().describe('The name of the user').title('Name'),
|
||||
email: z.string().describe('The email of the user').title('Email'),
|
||||
photo: photoSchema.describe('The photo of the user').title('Photo'),
|
||||
resource_type: z.string().describe('The resource type of the user').title('Resource Type'),
|
||||
workspaces: z.array(workspaceSchema).describe('List of the workspaces').title('Workspaces'),
|
||||
})
|
||||
.partial()
|
||||
|
||||
export const addCommentToTaskInputSchema = z.object({
|
||||
taskId: z.string().describe('Task ID to comment').title('Task ID'),
|
||||
comment: z.string().describe('Content of the comment to be added').title('Comment'),
|
||||
})
|
||||
|
||||
export const addCommentToTaskOutputSchema = z.object({
|
||||
text: z.string().describe('The text of the comment').title('Text'),
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
import { z } from '@botpress/sdk'
|
||||
|
||||
const workspaceSchema = z.object({
|
||||
gid: z.string().describe('The GID of the workspace').title('GID'),
|
||||
name: z.string().describe('The name of the workspace').title('Name'),
|
||||
resource_type: z.string().describe('The resource type of the workspace').title('Resource Type'),
|
||||
})
|
||||
|
||||
const photoSchema = z
|
||||
.object({
|
||||
image_21x21: z.string().describe('An Image 21 by 21').title('Image 21x21'),
|
||||
image_27x27: z.string().describe('An Image 27 by 27').title('Image 27x27'),
|
||||
image_36x36: z.string().describe('An Image 36 by 36').title('Image 36x36'),
|
||||
image_60x60: z.string().describe('An Image 60 by 60').title('Image 60x60'),
|
||||
image_128x128: z.string().describe('An Image 128 by 128').title('Image 128x128'),
|
||||
})
|
||||
.nullable()
|
||||
|
||||
export { workspaceSchema, photoSchema }
|
||||
@@ -0,0 +1,9 @@
|
||||
import * as bp from '.botpress'
|
||||
|
||||
export type Configuration = bp.configuration.Configuration
|
||||
export type IntegrationProps = bp.IntegrationProps
|
||||
export type IntegrationCtx = bp.Context
|
||||
export type RegisterFunction = IntegrationProps['register']
|
||||
export type UnregisterFunction = IntegrationProps['unregister']
|
||||
export type Channels = IntegrationProps['channels']
|
||||
export type Handler = IntegrationProps['handler']
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { Channels } from '../misc/types'
|
||||
|
||||
class NotImplementedError extends Error {
|
||||
public constructor() {
|
||||
super('Not implemented')
|
||||
}
|
||||
}
|
||||
|
||||
export const channels: Channels = {
|
||||
channel: {
|
||||
messages: {
|
||||
text: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
image: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
markdown: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
audio: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
video: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
file: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
location: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
carousel: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
card: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
choice: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
dropdown: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
bloc: async () => {
|
||||
throw new NotImplementedError()
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { Handler } from '../misc/types'
|
||||
|
||||
class NotImplementedError extends Error {
|
||||
public constructor() {
|
||||
super('Not implemented')
|
||||
}
|
||||
}
|
||||
|
||||
export const handler: Handler = async () => {
|
||||
throw new NotImplementedError()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export { register } from './register'
|
||||
export { unregister } from './unregister'
|
||||
export { channels } from './channels'
|
||||
export { handler } from './handler'
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { RegisterFunction } from '../misc/types'
|
||||
|
||||
export const register: RegisterFunction = async () => {
|
||||
/**
|
||||
* This is called when an integration configuration is saved.
|
||||
* You should use this handler to instanciate ressources in the external service and ensure that the configuration is valid.
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { UnregisterFunction } from '../misc/types'
|
||||
|
||||
export const unregister: UnregisterFunction = async () => {
|
||||
/**
|
||||
* This is called when a bot removes the integration.
|
||||
* You should use this handler to instanciate ressources in the external service and ensure that the configuration is valid.
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { AsanaApi } from '../client'
|
||||
import type { Configuration } from '../misc/types'
|
||||
|
||||
export const getClient = (config: Configuration) => new AsanaApi(config.apiToken)
|
||||
Reference in New Issue
Block a user