Skip to content

Commit

Permalink
fix(*): add paginated sprints request to valid get last 4, and refetc…
Browse files Browse the repository at this point in the history
…h worklogs if its necessary
  • Loading branch information
Fedorrychkov committed Jan 12, 2025
1 parent a943d3f commit 03f6dc5
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 13 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nestjs-tg-jira-bot",
"version": "0.1.1",
"version": "0.1.2",
"description": "",
"author": "",
"private": true,
Expand Down
6 changes: 4 additions & 2 deletions src/guards/chat.telegraf.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ ${botWelcomeCommandsText}`,
}
}

request.chatContext = {
const chatContext: ChatTelegrafContextType = {
type: chat?.type,
isChatWithTopics: chat?.is_forum || replyMessage?.is_topic_message,
threadMessageId: replyMessage?.message_thread_id,
Expand All @@ -111,7 +111,9 @@ ${botWelcomeCommandsText}`,
from: from,
chat: update?.message?.chat,
isEditableAvailable,
} as ChatTelegrafContextType
}

request.chatContext = chatContext

return true
}
Expand Down
1 change: 1 addition & 0 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './getImageData'
export * from './id'
export * from './jsonSafe'
export * from './time'
19 changes: 19 additions & 0 deletions src/helpers/jsonSafe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export function jsonParse<T>(value?: string | null): T | null | undefined {
if (!value) {
return undefined
}

try {
return JSON.parse(value)
} catch {
return undefined
}
}

export function jsonStringify<T>(value: T): string {
if (!value) {
return null
}

return JSON.stringify(value)
}
24 changes: 21 additions & 3 deletions src/modules/jira/jira.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Inject, Injectable } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { Axios } from 'axios'
import { exec } from 'child_process'
import { AgileClient } from 'jira.js'
import NodeJsVersion3Client, { Version3Client } from 'jira-rest-sdk'

@Injectable()
export class JiraService {
private jira: NodeJsVersion3Client
private api: Axios
public baseURL: string
private apiToken: string
private email: string
Expand All @@ -20,6 +22,14 @@ export class JiraService {
this.apiToken = this.configService.get<string>('API_KEY')
this.baseURL = this.configService.get<string>('JIRA_HOST')

this.api = new Axios({
baseURL: this.baseURL,
auth: {
username: this.email,
password: this.apiToken,
},
})

this.jira = new Version3Client({
baseURL: this.baseURL,
authentication: {
Expand All @@ -41,7 +51,7 @@ export class JiraService {
})
}

public async createTask(payload: { summary: string; description: string; key: string; issueType: 'Task' | 'Bug' }) {
public async createTask(payload: { summary: string; description: string; key: string; issueType: string }) {
const { summary, description, key, issueType } = payload

const createdIssue = await this.jira.createIssue({
Expand Down Expand Up @@ -80,23 +90,31 @@ export class JiraService {
return boards
}

public async getBoardSprints(boardId: number) {
public async getBoardSprints(boardId: number, startAt = 0) {
const boards = await this.agileClient.board.getAllSprints({
boardId,
startAt: startAt > 0 ? startAt : 0,
})

return boards
}

public async getIssuesBySprint(sprintId: number) {
public async getIssuesBySprint(sprintId: number, startAt = 0) {
const issues = await this.agileClient.sprint.getIssuesForSprint({
sprintId,
maxResults: 500,
startAt: startAt > 0 ? startAt : 0,
})

return issues
}

public async getIssueWorklogs(issueId: string) {
const response = await this.api.get(`/rest/api/2/issue/${issueId}/worklog`)

return response.data
}

public async getSprint(sprintId: number) {
const issues = await this.agileClient.sprint.getSprint({
sprintId,
Expand Down
84 changes: 77 additions & 7 deletions src/scenes/main/main.jira.scene.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'
import * as dayjs from 'dayjs'
import * as isBetween from 'dayjs/plugin/isBetween'
import * as fs from 'fs'
import { Fields } from 'jira.js/out/version2/models'
import { Action, Command, Ctx, Hears, On, Update } from 'nestjs-telegraf'
import { MAIN_CALLBACK_DATA } from 'src/constants'
import { getJiraProjectKeyboards, getJiraProjectSprintsKeyboards } from 'src/constants/keyboard'
Expand All @@ -13,7 +14,7 @@ import {
UserTelegrafGuard,
UseSafeGuards,
} from 'src/guards'
import { getImageFile, time } from 'src/helpers'
import { getImageFile, jsonParse, time } from 'src/helpers'
import { CustomConfigService } from 'src/modules'
import { JiraService } from 'src/modules/jira'
import { ChatTelegrafContextType, JiraConfigType, TgInitUser } from 'src/types'
Expand Down Expand Up @@ -132,7 +133,17 @@ export class MainJiraSceneService {

const boards = await this.jiraService.getProjectBoards(key)
const boardIds = boards.values.map((board) => board.id)
const sprints = await Promise.all(boardIds.map((boardId) => this.jiraService.getBoardSprints(boardId)))
const sprints = await Promise.all(
boardIds.map(async (boardId) => {
const defaultResponse = await this.jiraService.getBoardSprints(boardId)

if (defaultResponse.isLast) {
return defaultResponse
}

return this.jiraService.getBoardSprints(boardId, defaultResponse.total - 4)
}),
)

const filteredSprints = sprints.flatMap((sprint) =>
sprint.values.filter((sprint, index, values) => {
Expand Down Expand Up @@ -167,7 +178,23 @@ export class MainJiraSceneService {
const sprint = await this.jiraService.getSprint(key)
const definedIssues = await this.jiraService.getIssuesBySprint(key)

this.logger.log(sprint)
const currentIssueLenght = definedIssues.issues.length

if (definedIssues.total > currentIssueLenght) {
/**
* Генерим длину массива startAt свойств
*/
const size = Math.floor(definedIssues.total / currentIssueLenght)

const startAtArray = Array.from({ length: size }, (_, index) => definedIssues.total * (index + 1))

for await (const startAt of startAtArray) {
const response = await this.jiraService.getIssuesBySprint(key, startAt)
definedIssues.issues.push(...response.issues)
}

return
}

const srtartDate = sprint.startDate
const endDate = sprint.completeDate || sprint.endDate
Expand All @@ -190,8 +217,32 @@ export class MainJiraSceneService {
return
}

const issues = parsedIssues
.filter((issue) => {
const preparedIssuesWithFullWorklogs = await Promise.all(
parsedIssues.map(async (issue) => {
if (issue.fields.worklog.total > issue.fields.worklog.worklogs.length) {
const response = await this.jiraService.getIssueWorklogs(issue.id)
const parsed = jsonParse<Fields['worklog']>(response)

if (parsed && typeof parsed === 'object' && 'worklogs' in parsed) {
return {
...issue,
fields: {
...issue.fields,
worklog: {
...issue.fields.worklog,
...parsed,
},
},
}
}
}

return issue
}),
)

const issues = preparedIssuesWithFullWorklogs
.filter(async (issue) => {
if (jiraConfig.isSuperAdmin) {
return true
}
Expand All @@ -201,6 +252,7 @@ export class MainJiraSceneService {
}

const worklogs = issue.fields.worklog.worklogs

const parsedWorkLogs = worklogs.filter((log) => {
if (jiraConfig.isSuperAdmin) {
return true
Expand Down Expand Up @@ -451,7 +503,25 @@ export class MainJiraSceneService {
@UserContext() userContext: TgInitUser,
@ChatTelegrafContext() chatContext: ChatTelegrafContextType,
) {
const projectKey = chatContext?.topic?.name?.split('=')?.[1]
const paramsString = chatContext?.topic?.name?.split(':')[1]
const params = paramsString?.split('&')

const paramsObject = params?.reduce((acc: Record<string, string>, param) => {
const [key, value] = param.split('=')
acc[key] = value

return acc
}, {})

const { key: projectKey, type: issueType = 'Bug' } = paramsObject

if (!projectKey) {
return
}

if (!['Bug', 'Task', 'Story'].includes(issueType)) {
return
}

// В качестве тайтла вытаскиваем первый абзац и первые 100 символов
const summary = ctx?.text?.split('\n')?.[0]?.slice(0, 100)
Expand All @@ -461,7 +531,7 @@ export class MainJiraSceneService {
const { createdLink } = await this.jiraService.createTask({
key: projectKey,
summary: `[JiraBot] ${summary}`,
issueType: 'Bug',
issueType,
description: `${description}\nCreated by bot from: https://t.me/c/${chatContext?.chat?.id?.toString()?.replace('-100', '')}/${chatContext?.threadMessageId}/${ctx?.message?.message_id}\nCaller user: @${userContext.username} (${userContext.firstName} ${userContext.lastName})`,
})

Expand Down

0 comments on commit 03f6dc5

Please sign in to comment.