From 5a596b3e6932b7330ae0da96575bcd3017131ca6 Mon Sep 17 00:00:00 2001 From: Jonathan Atiene <34762800+bemijonathan@users.noreply.github.com> Date: Sun, 5 May 2024 18:21:57 +0100 Subject: [PATCH 1/3] fixing trello --- src/clients/base-client.ts | 10 ++++++++++ src/clients/index.ts | 1 + src/clients/jira.ts | 15 ++++++--------- src/clients/trello.ts | 13 +++++++++++++ src/index.ts | 2 +- 5 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 src/clients/base-client.ts create mode 100644 src/clients/trello.ts diff --git a/src/clients/base-client.ts b/src/clients/base-client.ts new file mode 100644 index 0000000..12eda0a --- /dev/null +++ b/src/clients/base-client.ts @@ -0,0 +1,10 @@ +import { Issue } from 'jira.js/out/version2/models' + +export type TicketInformation = { + title?: string + branchName: string + body?: string +} +export interface IBaseClient { + getTicket(ticketInformation: TicketInformation): Promise +} diff --git a/src/clients/index.ts b/src/clients/index.ts index 66e9306..fa7a5bd 100644 --- a/src/clients/index.ts +++ b/src/clients/index.ts @@ -1,2 +1,3 @@ export * from './jira.js' export * from './github.js' +export * from './base-client.js' diff --git a/src/clients/jira.ts b/src/clients/jira.ts index 9561328..1e61fe6 100644 --- a/src/clients/jira.ts +++ b/src/clients/jira.ts @@ -1,16 +1,17 @@ import { WebhookPayload } from '@actions/github/lib/interfaces' import { Version2Client } from 'jira.js' -import { Issue } from 'jira.js/out/agile/models' +import { Issue } from 'jira.js/out/version2/models' import * as core from '@actions/core' import { Logger } from '../utils' +import { IBaseClient, TicketInformation } from './base-client' -export class JiraClient { +export class JiraClient implements IBaseClient { client: Version2Client constructor() { this.client = this.initializeJiraClient() } - initializeJiraClient = () => { + private initializeJiraClient = () => { const host = core.getInput('jiraHost') || process.env.JIRA_HOST || '' return new Version2Client({ host, @@ -23,15 +24,11 @@ export class JiraClient { } }) } - getJiraTicket = async ({ + getTicket = async ({ title, branchName, body - }: { - title?: string - branchName: string - body?: string - }): Promise => { + }: TicketInformation): Promise => { const ticketRegex = /([A-Z]+-[0-9]+)/g const allTickets = (`${body} ${branchName} ${title}` || '').match( ticketRegex diff --git a/src/clients/trello.ts b/src/clients/trello.ts new file mode 100644 index 0000000..7eca891 --- /dev/null +++ b/src/clients/trello.ts @@ -0,0 +1,13 @@ +import { IBaseClient, TicketInformation } from './base-client' + +const trelloBaseUrl = 'https://api.trello.com/1'; + +export class JiraClient implements IBaseClient { + constructor() {} + + getTicket = async ( + ticketInformation: TicketInformation + ): => { + // implementation + } +} diff --git a/src/index.ts b/src/index.ts index 5c16dad..e4e63eb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,7 +25,7 @@ export async function run(): Promise { return } - const jiraIssues = await jiraClient.getJiraTicket({ + const jiraIssues = await jiraClient.getTicket({ title: githubContext.payload.pull_request?.title, branchName: githubContext.payload.pull_request?.head.ref, body: `${githubContext.payload.pull_request?.body} ${githubContext.payload.pull_request?.head.ref}}` From cec32f470eec25c57d532f27d67d48498b7b43d0 Mon Sep 17 00:00:00 2001 From: jonathan atiene Date: Tue, 7 May 2024 11:02:15 +0100 Subject: [PATCH 2/3] fixed types for trello integration --- package-lock.json | 195 ++++++++++++++++++++++++++++++--- package.json | 1 + src/clients/base-client.ts | 3 +- src/clients/jira.ts | 15 +-- src/clients/trello.ts | 44 ++++++-- src/index.ts | 17 ++- src/steps/summarize-changes.ts | 2 +- src/types/trello.ts | 97 ++++++++++++++++ 8 files changed, 328 insertions(+), 46 deletions(-) create mode 100644 src/types/trello.ts diff --git a/package-lock.json b/package-lock.json index 2248d27..d64d7cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "ignore": "^5.2.4", "jira.js": "^3.0.2", "langchain": "^0.1.3", + "node-fetch": "^3.3.2", "openai": "^4.24.7" }, "devDependencies": { @@ -113,6 +114,25 @@ "undici-types": "~5.26.4" } }, + "node_modules/@anthropic-ai/sdk/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.22.10", "dev": true, @@ -2119,6 +2139,25 @@ "once": "^1.4.0" } }, + "node_modules/@octokit/request/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/@octokit/types": { "version": "6.41.0", "license": "MIT", @@ -3249,6 +3288,14 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "4.3.4", "dev": true, @@ -4426,6 +4473,28 @@ "bser": "2.1.1" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "dev": true, @@ -4552,6 +4621,17 @@ "node": ">= 14" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "dev": true, @@ -6650,21 +6730,20 @@ } }, "node_modules/node-fetch": { - "version": "2.7.0", - "license": "MIT", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "dependencies": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, "node_modules/node-int64": { @@ -6860,6 +6939,25 @@ "undici-types": "~5.26.4" } }, + "node_modules/openai/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/openapi-types": { "version": "12.1.3", "license": "MIT" @@ -7940,7 +8038,8 @@ }, "node_modules/tr46": { "version": "0.0.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/ts-api-utils": { "version": "1.0.2", @@ -8311,11 +8410,13 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/whatwg-url": { "version": "5.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -8531,6 +8632,14 @@ "requires": { "undici-types": "~5.26.4" } + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } } } }, @@ -9574,6 +9683,16 @@ "is-plain-object": "^5.0.0", "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + } } }, "@octokit/request-error": { @@ -10287,6 +10406,11 @@ "version": "1.0.8", "dev": true }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" + }, "debug": { "version": "4.3.4", "dev": true, @@ -11019,6 +11143,15 @@ "bser": "2.1.1" } }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "file-entry-cache": { "version": "6.0.1", "dev": true, @@ -11099,6 +11232,14 @@ } } }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "requires": { + "fetch-blob": "^3.1.2" + } + }, "fs.realpath": { "version": "1.0.0", "dev": true @@ -12295,9 +12436,13 @@ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" }, "node-fetch": { - "version": "2.7.0", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "requires": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" } }, "node-int64": { @@ -12423,6 +12568,14 @@ "requires": { "undici-types": "~5.26.4" } + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } } } }, @@ -13049,7 +13202,9 @@ } }, "tr46": { - "version": "0.0.3" + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "ts-api-utils": { "version": "1.0.2", @@ -13262,10 +13417,14 @@ "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==" }, "webidl-conversions": { - "version": "3.0.1" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "whatwg-url": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" diff --git a/package.json b/package.json index 262fe3b..5ca8483 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "ignore": "^5.2.4", "jira.js": "^3.0.2", "langchain": "^0.1.3", + "node-fetch": "^3.3.2", "openai": "^4.24.7" }, "devDependencies": { diff --git a/src/clients/base-client.ts b/src/clients/base-client.ts index 12eda0a..babd374 100644 --- a/src/clients/base-client.ts +++ b/src/clients/base-client.ts @@ -1,4 +1,5 @@ import { Issue } from 'jira.js/out/version2/models' +import { ITrello } from '../types/trello' export type TicketInformation = { title?: string @@ -6,5 +7,5 @@ export type TicketInformation = { body?: string } export interface IBaseClient { - getTicket(ticketInformation: TicketInformation): Promise + getTicket(tickets: string[]): Promise } diff --git a/src/clients/jira.ts b/src/clients/jira.ts index 1e61fe6..cc00354 100644 --- a/src/clients/jira.ts +++ b/src/clients/jira.ts @@ -1,4 +1,3 @@ -import { WebhookPayload } from '@actions/github/lib/interfaces' import { Version2Client } from 'jira.js' import { Issue } from 'jira.js/out/version2/models' import * as core from '@actions/core' @@ -24,19 +23,9 @@ export class JiraClient implements IBaseClient { } }) } - getTicket = async ({ - title, - branchName, - body - }: TicketInformation): Promise => { - const ticketRegex = /([A-Z]+-[0-9]+)/g - const allTickets = (`${body} ${branchName} ${title}` || '').match( - ticketRegex - ) - if (!allTickets?.length) return [] - const ticket = [...new Set(allTickets)] + getTicket = async (tickets: string[]): Promise => { const issues = await Promise.all( - ticket.map(async t => { + tickets.map(async t => { try { const issue = await this.client.issues.getIssue({ issueIdOrKey: t diff --git a/src/clients/trello.ts b/src/clients/trello.ts index 7eca891..160f32b 100644 --- a/src/clients/trello.ts +++ b/src/clients/trello.ts @@ -1,13 +1,41 @@ -import { IBaseClient, TicketInformation } from './base-client' +import { IBaseClient } from './base-client' +import { Logger } from '../utils' +import { ITrello } from '../types/trello' -const trelloBaseUrl = 'https://api.trello.com/1'; +const fetch = require('node-fetch') -export class JiraClient implements IBaseClient { - constructor() {} +const trelloBaseUrl: string = 'https://api.trello.com/1' - getTicket = async ( - ticketInformation: TicketInformation - ): => { - // implementation +export class TrelloClient implements IBaseClient { + config: { + key: string + token: string + } + + constructor() { + this.config = { + key: process.env.TRELLO_PRIVATE_KEY ?? '', + token: process.env.TRELLO_PRIVATE_TOKEN ?? '' + } + } + + getTicket = async (tickets: string[]) => { + const issues = await Promise.all( + tickets.map(async ticket => { + try { + const data = await fetch( + trelloBaseUrl + new URLSearchParams(this.config).toString(), + { + method: 'GET' + } + ) + return data.text() + } catch (error) { + Logger.error(`Error while fetching ${ticket} from `) + } + }) + ) + + return issues.map(e => e) as unknown as ITrello[] } } diff --git a/src/index.ts b/src/index.ts index e4e63eb..e3fa705 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,11 +25,18 @@ export async function run(): Promise { return } - const jiraIssues = await jiraClient.getTicket({ - title: githubContext.payload.pull_request?.title, - branchName: githubContext.payload.pull_request?.head.ref, - body: `${githubContext.payload.pull_request?.body} ${githubContext.payload.pull_request?.head.ref}}` - }) + const pullRequestTitle = githubContext.payload.pull_request?.title + const pullRequestbranchName = githubContext.payload.pull_request?.head.ref + const pullRequestBody = `${githubContext.payload.pull_request?.body} ${githubContext.payload.pull_request?.head.ref}}` + + const ticketRegex = /([A-Z]+-[0-9]+)/g + const allTickets = ( + `${pullRequestBody} ${pullRequestbranchName} ${pullRequestTitle}` || '' + ).match(ticketRegex) + if (!allTickets?.length) return + const tickets = [...new Set(allTickets)] + + const jiraIssues = await jiraClient.getTicket(tickets) if (!jiraIssues.length) { Logger.warn('Could not get jira ticket, exiting') diff --git a/src/steps/summarize-changes.ts b/src/steps/summarize-changes.ts index a0844f2..99591f3 100644 --- a/src/steps/summarize-changes.ts +++ b/src/steps/summarize-changes.ts @@ -1,6 +1,6 @@ import { Ai } from '../ai.js' import { Logger } from '../utils.js' -import { Issue } from 'jira.js/out/agile/models' +import { Issue } from 'jira.js/out/version2/models' export class SummarizeChanges { static async summarizeGitChanges( diff --git a/src/types/trello.ts b/src/types/trello.ts new file mode 100644 index 0000000..1e2f623 --- /dev/null +++ b/src/types/trello.ts @@ -0,0 +1,97 @@ +export interface ITrello { + id: string + address: string + badges: Badges + checkItemStates: string[] + closed: boolean + coordinates: string + creationMethod: string + dateLastActivity: string + desc: string + descData: DescData + due: string + dueReminder: string + idBoard: string + idChecklists: IdChecklist[] + idLabels: IdLabel[] + idList: string + idMembers: string[] + idMembersVoted: string[] + idShort: number + labels: string[] + limits: Limits + locationName: string + manualCoverAttachment: boolean + name: string + pos: number + shortLink: string + shortUrl: string + subscribed: boolean + url: string + cover: Cover +} + +export interface Badges { + attachmentsByType: AttachmentsByType + location: boolean + votes: number + viewingMemberVoted: boolean + subscribed: boolean + fogbugz: string + checkItems: number + checkItemsChecked: number + comments: number + attachments: number + description: boolean + due: string + start: string + dueComplete: boolean +} + +export interface AttachmentsByType { + trello: Trello +} + +export interface Trello { + board: number + card: number +} + +export interface DescData { + emoji: Emoji +} + +export interface Emoji {} + +export interface IdChecklist { + id: string +} + +export interface IdLabel { + id: string + idBoard: string + name: string + color: string +} + +export interface Limits { + attachments: Attachments +} + +export interface Attachments { + perBoard: PerBoard +} + +export interface PerBoard { + status: string + disableAt: number + warnAt: number +} + +export interface Cover { + color: string + idUploadedBackground: boolean + size: string + brightness: string + isTemplate: boolean +} From ddc6155ce4a46fdb6ed345141dfa8b367ee5e546 Mon Sep 17 00:00:00 2001 From: jonathan atiene Date: Wed, 8 May 2024 22:03:42 +0100 Subject: [PATCH 3/3] added trello api --- src/clients/base-client.ts | 47 +++++++++++++++++++++++++++------- src/clients/jira.ts | 37 ++++++++++++++------------ src/clients/trello.ts | 13 +++++----- src/index.ts | 46 ++++++++++++++++++++++----------- src/steps/summarize-changes.ts | 3 +-- src/types/client.ts | 3 +++ 6 files changed, 100 insertions(+), 49 deletions(-) create mode 100644 src/types/client.ts diff --git a/src/clients/base-client.ts b/src/clients/base-client.ts index babd374..414f1f4 100644 --- a/src/clients/base-client.ts +++ b/src/clients/base-client.ts @@ -1,11 +1,40 @@ -import { Issue } from 'jira.js/out/version2/models' -import { ITrello } from '../types/trello' +import { Logger } from '../utils' +import { TrelloClient } from './trello' +import { JiraClient } from './jira' -export type TicketInformation = { - title?: string - branchName: string - body?: string -} -export interface IBaseClient { - getTicket(tickets: string[]): Promise +export class BaseClient { + static get client(): TrelloClient | JiraClient | null { + const trelloCredentials = { + trelloPublicKey: process.env.TRELLO_PUBLIC_KEY, + trelloPrivateKey: process.env.TRELLO_PRIVATE_KEY + } + + if (this.validateCredentials(trelloCredentials)) { + return new TrelloClient() + } + + const jiraCredentials: Record = { + jiraApiKey: process.env.JIRA_API_KEY, + jiraEmail: process.env.JIRA_EMAIL, + jiraHost: process.env.JIRA_HOST + } + + if (this.validateCredentials(jiraCredentials)) { + return new JiraClient() + } + + return null + } + + static validateCredentials(credential: Record) { + if (Object.values(credential).filter(e => Boolean(e)).length > 0) { + Object.keys(credential).forEach(e => { + if (!credential[e]) { + Logger.error(`${e} configuration is missing`) + return false + } + }) + return true + } + } } diff --git a/src/clients/jira.ts b/src/clients/jira.ts index cc00354..9ee1d1b 100644 --- a/src/clients/jira.ts +++ b/src/clients/jira.ts @@ -1,15 +1,33 @@ import { Version2Client } from 'jira.js' -import { Issue } from 'jira.js/out/version2/models' import * as core from '@actions/core' import { Logger } from '../utils' -import { IBaseClient, TicketInformation } from './base-client' +import { IBaseClient } from '../types/client' export class JiraClient implements IBaseClient { client: Version2Client + constructor() { this.client = this.initializeJiraClient() } + + getTicketDetails = async (tickets: string[]): Promise => { + const issues: string[] = await Promise.all( + tickets.map(async t => { + try { + const issue = await this.client.issues.getIssue({ + issueIdOrKey: t + }) + return issue.fields.description ?? '' + } catch (e) { + Logger.error(`Error while fetching ${t} from JIRA`) + return '' + } + }) + ) + return issues.filter(Boolean) + } + private initializeJiraClient = () => { const host = core.getInput('jiraHost') || process.env.JIRA_HOST || '' return new Version2Client({ @@ -23,19 +41,4 @@ export class JiraClient implements IBaseClient { } }) } - getTicket = async (tickets: string[]): Promise => { - const issues = await Promise.all( - tickets.map(async t => { - try { - const issue = await this.client.issues.getIssue({ - issueIdOrKey: t - }) - return issue.fields.description - } catch (e) { - Logger.error(`Error while fetching ${t} from JIRA`) - } - }) - ) - return issues.filter(e => e) as unknown as Issue[] - } } diff --git a/src/clients/trello.ts b/src/clients/trello.ts index 160f32b..4f69d41 100644 --- a/src/clients/trello.ts +++ b/src/clients/trello.ts @@ -1,10 +1,9 @@ -import { IBaseClient } from './base-client' import { Logger } from '../utils' -import { ITrello } from '../types/trello' +import { IBaseClient } from '../types/client' const fetch = require('node-fetch') -const trelloBaseUrl: string = 'https://api.trello.com/1' +const trelloBaseUrl: string = 'https://api.trello.com/1/cards' export class TrelloClient implements IBaseClient { config: { @@ -19,7 +18,7 @@ export class TrelloClient implements IBaseClient { } } - getTicket = async (tickets: string[]) => { + getTicketDetails = async (tickets: string[]) => { const issues = await Promise.all( tickets.map(async ticket => { try { @@ -29,13 +28,15 @@ export class TrelloClient implements IBaseClient { method: 'GET' } ) - return data.text() + const d = await data.json() + + return d['desc'] } catch (error) { Logger.error(`Error while fetching ${ticket} from `) } }) ) - return issues.map(e => e) as unknown as ITrello[] + return issues.map(e => e) } } diff --git a/src/index.ts b/src/index.ts index e3fa705..1a5ecf8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,42 +1,58 @@ import * as core from '@actions/core' import * as github from '@actions/github' -import { SummarizeChanges, getChanges, CommentHandler } from './steps' +import { CommentHandler, getChanges, SummarizeChanges } from './steps' import dotenv from 'dotenv' -dotenv.config() - import { Logger, Templates } from './utils.js' import { Ai } from './ai' -import { GithubClient, JiraClient } from './clients' +import { BaseClient, GithubClient, JiraClient } from './clients' import { mockdata } from './mockdata' +dotenv.config() + // instantiate clients const jiraClient = new JiraClient() const githubClient = new GithubClient() const commentsHandler = new CommentHandler(githubClient) const ai = new Ai() +const getTicketsFromPullRequestDetails = ( + githubContext: typeof github.context.payload +) => { + const pullRequestTitle = githubContext.payload.pull_request?.title + const pullRequestbranchName = githubContext.payload.pull_request?.head.ref + const pullRequestBody = `${githubContext.payload.pull_request?.body} ${githubContext.payload.pull_request?.head.ref}}` + + const ticketRegex = /([A-Z]+-[0-9]+)/g + const allTickets = ( + `${pullRequestBody} ${pullRequestbranchName} ${pullRequestTitle}` || '' + ).match(ticketRegex) + return [...new Set(allTickets)] +} + +const getClientInstance = () => {} + export async function run(): Promise { try { const githubContext = process.env.NODE_ENV === 'local' ? mockdata : github.context const pullRequestNumber = githubContext.payload.pull_request?.number - if (!pullRequestNumber) { + if (!pullRequestNumber || githubContext.payload) { Logger.warn('Could not get pull request number from context, exiting') return } - const pullRequestTitle = githubContext.payload.pull_request?.title - const pullRequestbranchName = githubContext.payload.pull_request?.head.ref - const pullRequestBody = `${githubContext.payload.pull_request?.body} ${githubContext.payload.pull_request?.head.ref}}` + const tickets = getTicketsFromPullRequestDetails(githubContext.payload) + + if (!tickets.length) + return Logger.warn( + 'Could not get pull request number from context, exiting' + ) + + const client = BaseClient.client - const ticketRegex = /([A-Z]+-[0-9]+)/g - const allTickets = ( - `${pullRequestBody} ${pullRequestbranchName} ${pullRequestTitle}` || '' - ).match(ticketRegex) - if (!allTickets?.length) return - const tickets = [...new Set(allTickets)] + if (!client) return Logger.error('No client credential is set up.') - const jiraIssues = await jiraClient.getTicket(tickets) + const jiraIssues = await client.getTicketDetails(tickets) if (!jiraIssues.length) { Logger.warn('Could not get jira ticket, exiting') diff --git a/src/steps/summarize-changes.ts b/src/steps/summarize-changes.ts index 99591f3..80dadd5 100644 --- a/src/steps/summarize-changes.ts +++ b/src/steps/summarize-changes.ts @@ -1,6 +1,5 @@ import { Ai } from '../ai.js' import { Logger } from '../utils.js' -import { Issue } from 'jira.js/out/version2/models' export class SummarizeChanges { static async summarizeGitChanges( @@ -12,7 +11,7 @@ export class SummarizeChanges { } static async summarizeJiraTickets( - issues: Issue[], + issues: string[], ai: Ai ): Promise { const issueMapLongDesc = issues.join('\n') diff --git a/src/types/client.ts b/src/types/client.ts new file mode 100644 index 0000000..0fa6786 --- /dev/null +++ b/src/types/client.ts @@ -0,0 +1,3 @@ +export interface IBaseClient { + getTicketDetails(tickets: string[]): Promise +}