Skip to content
Merged
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
5 changes: 3 additions & 2 deletions integrations/jira/integration.definition.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { IntegrationDefinition } from '@botpress/sdk'

import { configuration, states, user, channels, actions } from './src/definitions'
import { configuration, states, secrets, user, channels, actions } from './src/definitions'

export default new IntegrationDefinition({
name: 'jira',
title: 'Jira',
description:
'This integration allows you to work with your Jira workspace, users, projects, and workflow transitions.',
version: '0.3.0',
version: '0.4.0',
readme: 'readme.md',
icon: 'icon.svg',
configuration,
Expand All @@ -16,6 +16,7 @@ export default new IntegrationDefinition({
actions,
events: {},
states,
secrets,
attributes: {
category: 'Project Management',
repo: 'botpress',
Expand Down
4 changes: 4 additions & 0 deletions integrations/jira/linkTemplate.vrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
webhookId = to_string!(.webhookId)
webhookUrl = to_string!(.webhookUrl)

"{{ webhookUrl }}/oauth/wizard/start?state={{ webhookId }}"
11 changes: 7 additions & 4 deletions integrations/jira/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This integration allows you to connect your Botpress chatbot with Jira Software, a popular platform for project management and issue tracking. With this integration, you can search, create, update, and transition issues, list projects, find Jira users, and post issue comments from your chatbot.

To set up the integration, you will need to provide your **host**, **email**, and **API token** credentials. Once the integration is set up, you can use the built-in actions to manage issues and projects, and use the issue comments channel to post comments.
This version supports the Jira OAuth setup wizard. If you are testing the OAuth build, the setup flow should offer **Connect with OAuth** and **Use an API Token**.

For more detailed instructions on how to set up and use the Botpress Jira Software integration, please refer to our documentation.

Expand All @@ -12,7 +12,8 @@ Before enabling the Botpress Jira Software Integration, please ensure that you h

- A Botpress cloud account.
- Access to a Jira Software account.
- API token generated from your Jira Software account.
- For OAuth setup: permission to authorize Jira access from your Atlassian account.
- For manual setup: an API token generated from your Atlassian account.

## Enable Integration

Expand All @@ -21,8 +22,10 @@ To enable the Jira Software integration in Botpress, follow these steps:
- Access your Botpress admin panel.
- Navigate to the “Integrations” section.
- Locate the Jira Software integration and click on "Install Integration".
- Provide the required API token, host, and email configuration details.
- Save the configuration.
- In the setup wizard, select **Connect with OAuth** to authorize Botpress with Atlassian.
- If your Atlassian account has access to multiple Jira sites, select the site this integration should use.
- Alternatively, select **Use an API Token** and provide your Jira host, Atlassian account email, and API token.
- Finish the setup.

## Usage

Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/assign-issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import type { Implementation } from '../misc/types'

import { getClient, getErrorMessage, serializeErrorForLog } from '../utils'

export const assignIssue: Implementation['actions']['assignIssue'] = async ({ ctx, input, logger }) => {
export const assignIssue: Implementation['actions']['assignIssue'] = async ({ client, ctx, input, logger }) => {
const validatedInput = assignIssueInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

try {
await jiraClient.assignIssue(validatedInput.issueKey, validatedInput.accountId)
Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/count-issues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import type { Implementation } from '../misc/types'

import { buildRuntimeError, getClient, serializeErrorForLog } from '../utils'

export const countIssues: Implementation['actions']['countIssues'] = async ({ ctx, input, logger }) => {
export const countIssues: Implementation['actions']['countIssues'] = async ({ client, ctx, input, logger }) => {
const validatedInput = countIssuesInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

try {
const count = await jiraClient.countIssues(validatedInput.jql)
Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/delete-issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import type { Implementation } from '../misc/types'

import { getClient, getErrorMessage, serializeErrorForLog } from '../utils'

export const deleteIssue: Implementation['actions']['deleteIssue'] = async ({ ctx, input, logger }) => {
export const deleteIssue: Implementation['actions']['deleteIssue'] = async ({ client, ctx, input, logger }) => {
const validatedInput = deleteIssueInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

try {
await jiraClient.deleteIssue(validatedInput.issueKey, validatedInput.deleteSubtasks ?? false)
Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/find-all-users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import type { Implementation } from '../misc/types'

import { buildRuntimeError, getClient, serializeErrorForLog } from '../utils'

export const findAllUsers: Implementation['actions']['findAllUsers'] = async ({ ctx, input, logger }) => {
export const findAllUsers: Implementation['actions']['findAllUsers'] = async ({ client, ctx, input, logger }) => {
const validatedInput = findAllUsersInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })
const addParams = {
startAt: validatedInput.startAt,
maxResults: validatedInput.maxResults,
Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/find-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import type { Implementation } from '../misc/types'

import { getClient, getErrorMessage, serializeErrorForLog } from '../utils'

export const findUser: Implementation['actions']['findUser'] = async ({ ctx, input, logger }) => {
export const findUser: Implementation['actions']['findUser'] = async ({ client, ctx, input, logger }) => {
const validatedInput = findUserInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })
try {
const response = await jiraClient.findUser(validatedInput.query)
logger
Expand Down
9 changes: 7 additions & 2 deletions integrations/jira/src/actions/get-issue-transitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ import { getIssueTransitionsInputSchema, getIssueTransitionsOutputSchema } from
import type { Implementation } from '../misc/types'
import { getClient, getErrorMessage, serializeErrorForLog } from '../utils'

export const getIssueTransitions: Implementation['actions']['getIssueTransitions'] = async ({ ctx, input, logger }) => {
export const getIssueTransitions: Implementation['actions']['getIssueTransitions'] = async ({
client,
ctx,
input,
logger,
}) => {
const validatedInput = getIssueTransitionsInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

try {
const response = await jiraClient.getIssueTransitions({
Expand Down
6 changes: 3 additions & 3 deletions integrations/jira/src/actions/get-issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import type { Implementation } from '../misc/types'

import { ISSUE_SEARCH_FIELDS, flattenIssue, getClient, getErrorMessage, serializeErrorForLog } from '../utils'

export const getIssue: Implementation['actions']['getIssue'] = async ({ ctx, input, logger }) => {
export const getIssue: Implementation['actions']['getIssue'] = async ({ client, ctx, input, logger }) => {
const validatedInput = getIssueInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

try {
const response = await jiraClient.getIssue({
issueIdOrKey: validatedInput.issueKey,
fields: ISSUE_SEARCH_FIELDS,
})
logger.forBot().info(`Successful - Get Issue - ${response.key}`)
return getIssueOutputSchema.parse(flattenIssue(response, ctx.configuration.host))
return getIssueOutputSchema.parse(flattenIssue(response, jiraClient.host))
} catch (error) {
logger.forBot().debug(`'Get Issue' exception ${serializeErrorForLog(error)}`)
const message = getErrorMessage(error)
Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/list-issue-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import type { Implementation } from '../misc/types'

import { getClient, getErrorMessage, serializeErrorForLog } from '../utils'

export const listIssueTypes: Implementation['actions']['listIssueTypes'] = async ({ ctx, input, logger }) => {
export const listIssueTypes: Implementation['actions']['listIssueTypes'] = async ({ client, ctx, input, logger }) => {
const validatedInput = listIssueTypesInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

try {
const response = await jiraClient.listIssueTypesForProject(validatedInput.projectKey)
Expand Down
9 changes: 7 additions & 2 deletions integrations/jira/src/actions/list-project-statuses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ import { listProjectStatusesInputSchema, listProjectStatusesOutputSchema } from
import type { Implementation } from '../misc/types'
import { getClient, getErrorMessage, serializeErrorForLog } from '../utils'

export const listProjectStatuses: Implementation['actions']['listProjectStatuses'] = async ({ ctx, input, logger }) => {
export const listProjectStatuses: Implementation['actions']['listProjectStatuses'] = async ({
client,
ctx,
input,
logger,
}) => {
const validatedInput = listProjectStatusesInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

try {
const response = await jiraClient.listProjectStatuses(validatedInput.projectKey)
Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/list-projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { buildRuntimeError, getClient, serializeErrorForLog } from '../utils'
const DEFAULT_MAX_RESULTS = 50
const HARD_MAX_RESULTS = 100

export const listProjects: Implementation['actions']['listProjects'] = async ({ ctx, input, logger }) => {
export const listProjects: Implementation['actions']['listProjects'] = async ({ client, ctx, input, logger }) => {
const validatedInput = listProjectsInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

const startAt = validatedInput.nextToken ? Number(validatedInput.nextToken) : 0
const maxResults = Math.min(validatedInput.maxResults ?? DEFAULT_MAX_RESULTS, HARD_MAX_RESULTS)
Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/new-issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
textToAdfDocument,
} from '../utils'

export const newIssue: Implementation['actions']['newIssue'] = async ({ ctx, input, logger }) => {
export const newIssue: Implementation['actions']['newIssue'] = async ({ client, ctx, input, logger }) => {
const validatedInput = newIssueInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

try {
const issueTypeIds = await resolveIssueTypeIds(jiraClient, [
Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/new-issues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import { buildRuntimeError, getClient, resolveIssueTypeIds, serializeErrorForLog

type IssueInput = Version3Models.IssueUpdateDetails

export const newIssues: Implementation['actions']['newIssues'] = async ({ ctx, input, logger }) => {
export const newIssues: Implementation['actions']['newIssues'] = async ({ client, ctx, input, logger }) => {
const validatedInput = newIssuesInputSchema.parse(input)
if (validatedInput.issues.length === 0) {
throw new RuntimeError('At least one issue must be provided')
}
if (validatedInput.issues.length > 50) {
throw new RuntimeError(`Jira allows up to 50 issues per batch; received ${validatedInput.issues.length}`)
}
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

try {
const issueTypeIds = await resolveIssueTypeIds(jiraClient, validatedInput.issues)
Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/pick-issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import type { Implementation } from '../misc/types'

import { buildRuntimeError, getClient, serializeErrorForLog } from '../utils'

export const pickIssue: Implementation['actions']['pickIssue'] = async ({ ctx, input, logger }) => {
export const pickIssue: Implementation['actions']['pickIssue'] = async ({ client, ctx, input, logger }) => {
const validatedInput = pickIssueInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

try {
const response = await jiraClient.pickIssue(validatedInput.query, validatedInput.currentJql)
Expand Down
6 changes: 3 additions & 3 deletions integrations/jira/src/actions/search-issues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ const DEFAULT_MAX_RESULTS = 50
const HARD_MAX_RESULTS = 100
const DEFAULT_JQL = 'order by created DESC'

export const searchIssues: Implementation['actions']['searchIssues'] = async ({ ctx, input, logger }) => {
export const searchIssues: Implementation['actions']['searchIssues'] = async ({ client, ctx, input, logger }) => {
const validatedInput = searchIssuesInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

const maxResults = Math.min(validatedInput.maxResults ?? DEFAULT_MAX_RESULTS, HARD_MAX_RESULTS)
const jql = validatedInput.jql && validatedInput.jql.trim().length > 0 ? validatedInput.jql : DEFAULT_JQL
Expand All @@ -23,7 +23,7 @@ export const searchIssues: Implementation['actions']['searchIssues'] = async ({
})

const issues = response.issues ?? []
const items = issues.map((issue) => flattenIssue(issue, ctx.configuration.host))
const items = issues.map((issue) => flattenIssue(issue, jiraClient.host))
const nextToken = response.isLast ? undefined : response.nextPageToken

if (response.isLast === false && response.nextPageToken === undefined && items.length > 0) {
Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/transition-issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import type { Implementation } from '../misc/types'

import { getClient, getErrorMessage, serializeErrorForLog, textToAdfDocument } from '../utils'

export const transitionIssue: Implementation['actions']['transitionIssue'] = async ({ ctx, input, logger }) => {
export const transitionIssue: Implementation['actions']['transitionIssue'] = async ({ client, ctx, input, logger }) => {
const validatedInput = transitionIssueInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })

try {
await jiraClient.transitionIssue({
Expand Down
4 changes: 2 additions & 2 deletions integrations/jira/src/actions/update-issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
textToAdfDocument,
} from '../utils'

export const updateIssue: Implementation['actions']['updateIssue'] = async ({ ctx, input, logger }) => {
export const updateIssue: Implementation['actions']['updateIssue'] = async ({ client, ctx, input, logger }) => {
const validatedInput = updateIssueInputSchema.parse(input)
const jiraClient = getClient(ctx.configuration)
const jiraClient = await getClient({ client, ctx, logger })
const fields: NonNullable<Version3Parameters.EditIssue['fields']> = {}

if (validatedInput.summary !== undefined) {
Expand Down
Loading
Loading