From b712729e17ace5786cf8ced481c66bdec56f5ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20R=C3=BCsch?= Date: Mon, 27 Nov 2023 21:27:48 +0100 Subject: [PATCH 01/10] Refactor user types --- .../jira-cloud-provider/cloud-types.ts | 12 ++++++ .../jira-server-provider/server-types.ts | 12 ++++++ .../CreateIssue/Fields/AssigneeSelect.tsx | 10 +++-- .../CreateIssue/Fields/ProjectSelect.tsx | 9 ++--- .../CreateIssue/Fields/ReporterSelect.tsx | 8 +++- types/index.ts | 38 ++++++++----------- 6 files changed, 56 insertions(+), 33 deletions(-) create mode 100644 electron/providers/jira-cloud-provider/cloud-types.ts create mode 100644 electron/providers/jira-server-provider/server-types.ts diff --git a/electron/providers/jira-cloud-provider/cloud-types.ts b/electron/providers/jira-cloud-provider/cloud-types.ts new file mode 100644 index 00000000..d3ff1c7b --- /dev/null +++ b/electron/providers/jira-cloud-provider/cloud-types.ts @@ -0,0 +1,12 @@ +export interface JiraCloudUser { + accountId: string + name: string + displayName: string + emailAddress: string + avatarUrls: { + "16x16": string + "24x24": string + "32x32": string + "48x48": string + } +} diff --git a/electron/providers/jira-server-provider/server-types.ts b/electron/providers/jira-server-provider/server-types.ts new file mode 100644 index 00000000..f1cc43ef --- /dev/null +++ b/electron/providers/jira-server-provider/server-types.ts @@ -0,0 +1,12 @@ +export interface JiraServerUser { + key: string + name: string + displayName: string + emailAddress: string + avatarUrls: { + "16x16": string + "24x24": string + "32x32": string + "48x48": string + } +} diff --git a/src/components/CreateIssue/Fields/AssigneeSelect.tsx b/src/components/CreateIssue/Fields/AssigneeSelect.tsx index f3a8e58a..2a12b6f7 100644 --- a/src/components/CreateIssue/Fields/AssigneeSelect.tsx +++ b/src/components/CreateIssue/Fields/AssigneeSelect.tsx @@ -19,10 +19,10 @@ export function AssigneeSelect({ nothingFound="Please select a project first" itemComponent={SelectItem} data={ - !isLoading && assignableUsers && assignableUsers instanceof Array + !isLoading && assignableUsers ? assignableUsers.map((assignableUser) => ({ image: assignableUser.avatarUrls["24x24"], - value: assignableUser.accountId, + value: assignableUser.id, label: assignableUser.displayName, })) : [] @@ -30,7 +30,11 @@ export function AssigneeSelect({ clearable searchable withinPortal - {...form.getInputProps("assignee.id")} + {...form.getInputProps("assignee")} + value={ form.getInputProps("assignee").value?.id } + onChange={(value) => { + form.getInputProps("assignee").onChange(assignableUsers?.find((user) => user.id === value)) + }} /> ) } diff --git a/src/components/CreateIssue/Fields/ProjectSelect.tsx b/src/components/CreateIssue/Fields/ProjectSelect.tsx index 8f3c2051..b9e7328a 100644 --- a/src/components/CreateIssue/Fields/ProjectSelect.tsx +++ b/src/components/CreateIssue/Fields/ProjectSelect.tsx @@ -28,11 +28,10 @@ export function ProjectSelect({ form.getInputProps("projectId").onChange(value) form.setFieldValue("type", "") form.setFieldValue("status", "") - form.setFieldValue("assignee.id", null) - form.setFieldValue( - "reporter", - currentUser?.accountId || (null as unknown as string) - ) + form.setFieldValue("assignee", undefined) + if (currentUser) { + form.setFieldValue("reporter", currentUser) + } }} /> ) diff --git a/src/components/CreateIssue/Fields/ReporterSelect.tsx b/src/components/CreateIssue/Fields/ReporterSelect.tsx index 9006fbfc..443f70f0 100644 --- a/src/components/CreateIssue/Fields/ReporterSelect.tsx +++ b/src/components/CreateIssue/Fields/ReporterSelect.tsx @@ -19,10 +19,10 @@ export function ReporterSelect({ nothingFound="Please select a project first" itemComponent={SelectItem} data={ - !isLoading && assignableUsers && assignableUsers instanceof Array + !isLoading && assignableUsers ? assignableUsers.map((assignableUser) => ({ image: assignableUser.avatarUrls["24x24"], - value: assignableUser.accountId, + value: assignableUser.id, label: assignableUser.displayName, })) : [] @@ -31,6 +31,10 @@ export function ReporterSelect({ searchable withinPortal {...form.getInputProps("reporter")} + value={ form.getInputProps("reporter").value?.id } + onChange={(value) => { + form.getInputProps("reporter").onChange(assignableUsers?.find((user) => user.id === value)) + }} /> ) } diff --git a/types/index.ts b/types/index.ts index 6717e35b..306764bb 100644 --- a/types/index.ts +++ b/types/index.ts @@ -14,6 +14,19 @@ export interface Project { type: string } +export interface User { + id: string + name: string + displayName: string + emailAddress: string + avatarUrls: { + "16x16": string + "24x24": string + "32x32": string + "48x48": string + } +} + export interface Priority { statusColor: string description: string @@ -41,18 +54,9 @@ export interface Issue { storyPointsEstimate: number epic: string labels: string[] - assignee: { - id: string - displayName: string - avatarUrls: { - "48x48": string - "24x24": string - "16x16": string - "32x32": string - } - } + assignee?: User rank: string - reporter: string + reporter: User sprint?: Sprint projectId: string subtasks: { @@ -103,18 +107,6 @@ export interface IssueType { subtask: boolean } -export interface User { - accountId: string - emailAddress: string - avatarUrls: { - "48x48": string - "24x24": string - "16x16": string - "32x32": string - } - displayName: string -} - export interface SprintCreate { name: string startDate: Date From f9db363413348af73694833c0fabad394b888e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20R=C3=BCsch?= Date: Mon, 27 Nov 2023 21:28:13 +0100 Subject: [PATCH 02/10] Add create issue for server --- .../JiraServerProvider.ts | 102 ++++++++++++++++-- 1 file changed, 96 insertions(+), 6 deletions(-) diff --git a/electron/providers/jira-server-provider/JiraServerProvider.ts b/electron/providers/jira-server-provider/JiraServerProvider.ts index 8390acc5..5efac219 100644 --- a/electron/providers/jira-server-provider/JiraServerProvider.ts +++ b/electron/providers/jira-server-provider/JiraServerProvider.ts @@ -13,6 +13,7 @@ import { } from "../../../types" import {JiraIssue, JiraIssueType, JiraProject, JiraSprint,} from "../../../types/jira" import {IProvider} from "../base-provider" +import {JiraServerUser} from "./server-types"; export class JiraServerProvider implements IProvider { private loginOptions = { @@ -72,7 +73,7 @@ export class JiraServerProvider implements IProvider { return instance } - private getRestApiClient(version: number) { + private getRestApiClient(version: string|number) { return this.constructRestBasedClient('api', version.toString()); } @@ -377,8 +378,15 @@ export class JiraServerProvider implements IProvider { return new Promise((resolve, reject) => { this.getRestApiClient(2) .get(`/user/assignable/search?project=${projectIdOrKey}`) - .then(async (response) => { - resolve(response.data as User[]) + .then(async (response: AxiosResponse) => { + const users: User[] = response.data.map((user) => ({ + id: user.key, + name: user.name, + displayName: user.displayName, + avatarUrls: user.avatarUrls, + emailAddress: user.emailAddress, + } as User)) + resolve(users) }) .catch((error) => { if (error.response) { @@ -399,14 +407,96 @@ export class JiraServerProvider implements IProvider { return new Promise((resolve, reject) => { this.getRestApiClient(2) .get('/myself') - .then(async (response) => resolve(response.data as User)) + .then(async (response: AxiosResponse) => resolve({ + id: response.data.key, + name: response.data.name, + displayName: response.data.displayName, + avatarUrls: response.data.avatarUrls, + emailAddress: response.data.emailAddress, + } as User)) .catch((error) => reject(new Error(`Error in the current user: ${error}`))) }) } /* eslint-disable @typescript-eslint/no-unused-vars */ - createIssue(issue: Issue): Promise { - throw new Error("Method not implemented for Jira Server") + createIssue({ + summary, + type, + projectId, + reporter, + assignee, + sprint, + storyPointsEstimate, + description, + status, + epic, + startDate, + dueDate, + labels, + priority, + }: Issue): Promise { + const offsetStartDate = this.offsetDate(startDate) + const offsetDueDate = this.offsetDate(dueDate) + + return new Promise((resolve, reject) => { + this.getRestApiClient(2) + .post( + `/issue`, + { + fields: { + summary, + parent: { key: epic }, + issuetype: { id: type }, + project: { + id: projectId, + }, + reporter: { + name: reporter.name, + }, + ...(priority.id && { priority }), + ...(assignee && { + assignee: { + name: assignee.name, + }, + }), + description, + labels, + ...(offsetStartDate && { + [this.customFields.get("Start date")!]: offsetStartDate, + }), + ...(offsetDueDate && { + [this.customFields.get("Due date")!]: offsetDueDate, + }), + ...(sprint && + sprint.id && { + [this.customFields.get("Sprint")!]: +sprint.id, + }), + ...(storyPointsEstimate && { + [this.customFields.get("Story point estimate")!]: + storyPointsEstimate, + }), + // ...(files && { + // [this.customFields.get("Attachment")!]: files, + // }), + }, + } + ) + .then(async (response) => { + const createdIssue = response.data + resolve(JSON.stringify(createdIssue.key)) + await this.setTransition(createdIssue.id, status) + }) + .catch((error) => { + let specificError = error + if (error.response) { + if (error.response.status === 404) { + specificError = new Error("The user does not have the necessary permissions") + } + } + + reject(new Error(`Error creating issue: ${specificError}`)) + }) + }) } getEpicsByProject(projectIdOrKey: string): Promise { From 106a19ad44b8b0a6dd485e12113e2ae1aa33c33a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20R=C3=BCsch?= Date: Mon, 27 Nov 2023 21:28:21 +0100 Subject: [PATCH 03/10] Fix create issue for cloud --- .../jira-cloud-provider/JiraCloudProvider.ts | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/electron/providers/jira-cloud-provider/JiraCloudProvider.ts b/electron/providers/jira-cloud-provider/JiraCloudProvider.ts index f7ef14a1..6fae6c6f 100644 --- a/electron/providers/jira-cloud-provider/JiraCloudProvider.ts +++ b/electron/providers/jira-cloud-provider/JiraCloudProvider.ts @@ -20,6 +20,7 @@ import { } from "../../../types/jira" import { IProvider } from "../base-provider" import { getAccessToken, refreshTokens } from "./getAccessToken" +import {JiraCloudUser} from "./cloud-types"; export class JiraCloudProvider implements IProvider { public accessToken: string | undefined @@ -282,7 +283,15 @@ export class JiraCloudProvider implements IProvider { this.getRestApiClient(3) .get(`/user/assignable/search?project=${projectIdOrKey}`) .then(async (response) => { - resolve(response.data as User[]) + const users = response.data.map((cloudUser: JiraCloudUser) => ({ + id: cloudUser.accountId, + name: cloudUser.name, + avatarUrls: cloudUser.avatarUrls, + displayName: cloudUser.displayName, + emailAddress: cloudUser.emailAddress, + } as User)) + + resolve(users as User[]) }) .catch((error) => { let specificError = error @@ -301,8 +310,14 @@ export class JiraCloudProvider implements IProvider { return new Promise((resolve, reject) => { this.getRestApiClient(3) .get('/myself') - .then(async (response) => { - resolve(response.data as User) + .then(async (response: AxiosResponse) => { + resolve({ + id: response.data.accountId, + name: response.data.name, + avatarUrls: response.data.avatarUrls, + displayName: response.data.displayName, + emailAddress: response.data.emailAddress, + } as User) }) .catch((error) => reject(new Error(`Error in fetching the current user: ${error}`)) @@ -649,10 +664,14 @@ export class JiraCloudProvider implements IProvider { id: projectId, }, reporter: { - id: reporter, + id: reporter.id, }, ...(priority.id && { priority }), - assignee, + ...(assignee && { + assignee: { + id: assignee.id, + } + }), description: { type: "doc", version: 1, From f940580714da9d4f87219b3fc281241d9ce87bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20R=C3=BCsch?= Date: Mon, 27 Nov 2023 21:49:36 +0100 Subject: [PATCH 04/10] Add jira server info to provider class --- .../JiraServerProvider.ts | 19 ++++++++++++++++++- .../jira-server-provider/server-types.ts | 12 ++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/electron/providers/jira-server-provider/JiraServerProvider.ts b/electron/providers/jira-server-provider/JiraServerProvider.ts index 5efac219..cd972c62 100644 --- a/electron/providers/jira-server-provider/JiraServerProvider.ts +++ b/electron/providers/jira-server-provider/JiraServerProvider.ts @@ -13,7 +13,7 @@ import { } from "../../../types" import {JiraIssue, JiraIssueType, JiraProject, JiraSprint,} from "../../../types/jira" import {IProvider} from "../base-provider" -import {JiraServerUser} from "./server-types"; +import {JiraServerInfo, JiraServerUser} from "./server-types"; export class JiraServerProvider implements IProvider { private loginOptions = { @@ -22,6 +22,8 @@ export class JiraServerProvider implements IProvider { password: "", } + private serverInfo?: JiraServerInfo = undefined + private customFields = new Map() private reversedCustomFields = new Map() @@ -98,10 +100,25 @@ export class JiraServerProvider implements IProvider { this.loginOptions.username = basicLoginOptions.username this.loginOptions.password = basicLoginOptions.password + await this.getServerInfo() await this.mapCustomFields() return this.isLoggedIn() } + async getServerInfo(): Promise { + return new Promise((resolve, reject) => { + this.getRestApiClient(2) + .get('/serverInfo') + .then((response: AxiosResponse) => { + this.serverInfo = response.data + resolve() + }) + .catch((error) => { + reject(new Error(`Error in checking server info: ${error}`)) + }) + }) + } + async isLoggedIn(): Promise { return new Promise((resolve, reject) => { this.getAuthRestApiClient(1) diff --git a/electron/providers/jira-server-provider/server-types.ts b/electron/providers/jira-server-provider/server-types.ts index f1cc43ef..46e03599 100644 --- a/electron/providers/jira-server-provider/server-types.ts +++ b/electron/providers/jira-server-provider/server-types.ts @@ -10,3 +10,15 @@ export interface JiraServerUser { "48x48": string } } + +export interface JiraServerInfo { + baseUrl: string + version: string + versionNumbers: [number, number, number] + buildNumber: number + buildDate: string + serverTime: string + scmInfo: string + buildPartnerName: string + serverTitle: string +} From 52aebb78c6caff777e8c28ac7be7767e76fd167e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20R=C3=BCsch?= Date: Mon, 27 Nov 2023 22:28:33 +0100 Subject: [PATCH 05/10] Add implementation for executing api calls in a versioned manner --- .../JiraServerProvider.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/electron/providers/jira-server-provider/JiraServerProvider.ts b/electron/providers/jira-server-provider/JiraServerProvider.ts index cd972c62..4e91d7ec 100644 --- a/electron/providers/jira-server-provider/JiraServerProvider.ts +++ b/electron/providers/jira-server-provider/JiraServerProvider.ts @@ -28,6 +28,49 @@ export class JiraServerProvider implements IProvider { private reversedCustomFields = new Map() + private executeVersioned(functionsByVersionMatcher: { [versionMatcher: string]: (...args: never[]) => R }, ...args: never[]) { + if (!this.serverInfo) { + throw new Error('Server info not set!') + } + + const matches = (matcher: string): boolean => { + let match = true + matcher.split('.').forEach((matcherPart, index) => { + match = match && ( + matcherPart === '*' + || matcherPart === this.serverInfo!.versionNumbers[index].toString() + ) + }) + + return match + } + + const isAMoreSpecificThanB = (matcherA: string, matcherB: string): boolean => { + const matcherBParts = matcherB.split('.') + let isMoreSpecific = false; + matcherA.split('.').forEach((matcherAPart, index) => { + if (matcherBParts[index] === '*' && matcherAPart !== '*') { + isMoreSpecific = true; + } + }) + + return isMoreSpecific; + } + + let selectedMatcher: string | undefined + Object.keys(functionsByVersionMatcher).forEach((matcher) => { + if (matches(matcher) && (selectedMatcher === undefined || isAMoreSpecificThanB(matcher, selectedMatcher))) { + selectedMatcher = matcher + } + }) + + if (!selectedMatcher) { + throw new Error(`No version matcher found for version: ${this.serverInfo.version}`) + } + + return functionsByVersionMatcher[selectedMatcher](...args) + } + private getAuthHeader() { return `Basic ${Buffer.from( `${this.loginOptions.username}:${this.loginOptions.password}` From 5494f24057b0afed489255bff453eb40e38df49a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20R=C3=BCsch?= Date: Mon, 27 Nov 2023 22:28:44 +0100 Subject: [PATCH 06/10] Add test implementation for getIssueTypesWithFieldsMap --- .../JiraServerProvider.ts | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/electron/providers/jira-server-provider/JiraServerProvider.ts b/electron/providers/jira-server-provider/JiraServerProvider.ts index 4e91d7ec..1fe68d51 100644 --- a/electron/providers/jira-server-provider/JiraServerProvider.ts +++ b/electron/providers/jira-server-provider/JiraServerProvider.ts @@ -624,6 +624,40 @@ export class JiraServerProvider implements IProvider { } getIssueTypesWithFieldsMap(): Promise<{ [key: string]: string[] }> { + return this.executeVersioned({ + '7.*': this.getIssueTypesWithFieldsMap_7.bind(this), + '*': this.getIssueTypesWithFieldsMap_8and9.bind(this) + }) + } + + getIssueTypesWithFieldsMap_7(): Promise<{ [key: string]: string[] }> { + return new Promise((resolve) => { + this.getRestApiClient(2) + .get('/issue/createmeta?expand=projects.issuetypes.fields') + .then(async (response) => { + const issueTypeToFieldsMap: { [key: string]: string[] } = {} + response.data.projects.forEach( + (project: { + id: string + issuetypes: { + fields: {} + id: string + }[] + }) => { + project.issuetypes.forEach((issueType) => { + const fieldKeys = Object.keys(issueType.fields) + issueTypeToFieldsMap[issueType.id] = fieldKeys.map( + (fieldKey) => this.reversedCustomFields.get(fieldKey)! + ) + }) + } + ) + resolve(issueTypeToFieldsMap) + }) + }) + } + + getIssueTypesWithFieldsMap_8and9(): Promise<{ [key: string]: string[] }> { return new Promise((resolve) => { // IMPROVE: This is barely scalable this.getProjects() @@ -634,7 +668,7 @@ export class JiraServerProvider implements IProvider { this.getRestApiClient(2) .get(`/issue/createmeta/${project.id}/issuetypes`) .then(async (response) => { - await Promise.all(response.data.values.map((issueType: { id: string }) => + await Promise.all(response.data.values.map((issueType: { id: string }) => // IMPROVE: This call currently only supports 50 issue types this.getRestApiClient(2) .get(`/issue/createmeta/${project.id}/issuetypes/${issueType.id}`) From 60f7867b070f3f61dd181a60878a15bda18538f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20R=C3=BCsch?= Date: Mon, 27 Nov 2023 22:36:40 +0100 Subject: [PATCH 07/10] Fix issues with changed types --- .../jira-server-provider/JiraServerProvider.ts | 11 ++++------- src/components/BacklogView/Issue/IssueCard.tsx | 6 +++--- src/components/CreateIssue/CreateIssueModal.tsx | 2 +- .../Components/AssigneeMenu/AssigneeMenu.tsx | 4 ++-- src/components/DetailView/Components/ReporterMenu.tsx | 4 ++-- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/electron/providers/jira-server-provider/JiraServerProvider.ts b/electron/providers/jira-server-provider/JiraServerProvider.ts index 5efac219..94184477 100644 --- a/electron/providers/jira-server-provider/JiraServerProvider.ts +++ b/electron/providers/jira-server-provider/JiraServerProvider.ts @@ -749,15 +749,12 @@ export class JiraServerProvider implements IProvider { }, }), ...(reporter && { - reporter: { - id: reporter, - }, + reporter, }), ...(priority && priority.id && { priority }), - ...(assignee && - assignee.id && { - assignee, - }), + ...(assignee && { + assignee, + }), ...(description && { description }), diff --git a/src/components/BacklogView/Issue/IssueCard.tsx b/src/components/BacklogView/Issue/IssueCard.tsx index 55410167..067397b4 100644 --- a/src/components/BacklogView/Issue/IssueCard.tsx +++ b/src/components/BacklogView/Issue/IssueCard.tsx @@ -151,14 +151,14 @@ export function IssueCard({ > - {assignee.avatarUrls !== undefined ? ( + {assignee?.avatarUrls !== undefined ? ( } onClick={() => - editIssue.mutate({ assignee: { id: user.accountId } } as Issue) + editIssue.mutate({ assignee: user } as Issue) } - key={user.accountId} + key={user.id} > {user.displayName} diff --git a/src/components/DetailView/Components/ReporterMenu.tsx b/src/components/DetailView/Components/ReporterMenu.tsx index 953e0228..962b068f 100644 --- a/src/components/DetailView/Components/ReporterMenu.tsx +++ b/src/components/DetailView/Components/ReporterMenu.tsx @@ -91,8 +91,8 @@ export function ReporterMenu({ issueKey }: { issueKey: string }) { assignableUsers.map((user) => ( } - onClick={() => mutation.mutate({ reporter: user.accountId } as Issue)} - key={user.accountId} + onClick={() => mutation.mutate({ reporter: user } as Issue)} + key={user.id} > {user.displayName} From 874a2aa74485fc78ebacb9d041e97d39202c7c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20R=C3=BCsch?= Date: Mon, 27 Nov 2023 22:40:49 +0100 Subject: [PATCH 08/10] Add error when jira server version is unsupported --- .../providers/jira-server-provider/JiraServerProvider.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/electron/providers/jira-server-provider/JiraServerProvider.ts b/electron/providers/jira-server-provider/JiraServerProvider.ts index 70281e43..04e5d941 100644 --- a/electron/providers/jira-server-provider/JiraServerProvider.ts +++ b/electron/providers/jira-server-provider/JiraServerProvider.ts @@ -154,6 +154,12 @@ export class JiraServerProvider implements IProvider { .get('/serverInfo') .then((response: AxiosResponse) => { this.serverInfo = response.data + if (this.serverInfo.versionNumbers[0] < 7) { + reject(new Error( + `Your Jira server version is unsupported. Minimum major version: 7. Your version: ${this.serverInfo.versionNumbers[0]}`, + )) + } + resolve() }) .catch((error) => { From 51dc1b382082ec9272860d6a05323f39ff83669c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20R=C3=BCsch?= Date: Mon, 27 Nov 2023 23:23:41 +0100 Subject: [PATCH 09/10] Fix server provider for creating epics --- .../JiraServerProvider.ts | 116 ++++++++++-------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/electron/providers/jira-server-provider/JiraServerProvider.ts b/electron/providers/jira-server-provider/JiraServerProvider.ts index 94184477..6c5cb7c9 100644 --- a/electron/providers/jira-server-provider/JiraServerProvider.ts +++ b/electron/providers/jira-server-provider/JiraServerProvider.ts @@ -439,62 +439,70 @@ export class JiraServerProvider implements IProvider { const offsetDueDate = this.offsetDate(dueDate) return new Promise((resolve, reject) => { - this.getRestApiClient(2) - .post( - `/issue`, - { - fields: { - summary, - parent: { key: epic }, - issuetype: { id: type }, - project: { - id: projectId, - }, - reporter: { - name: reporter.name, - }, - ...(priority.id && { priority }), - ...(assignee && { - assignee: { - name: assignee.name, + this.getIssueTypesByProject(projectId) + .then((issueTypes) => { + const relevantIssueType = issueTypes.find((issueType) => issueType.id === type) + + this.getRestApiClient(2) + .post( + `/issue`, + { + fields: { + summary, + parent: { key: epic }, + issuetype: { id: type }, + project: { + id: projectId, + }, + reporter: { + name: reporter.name, + }, + ...(priority.id && { priority }), + ...(assignee && { + assignee: { + name: assignee.name, + }, + }), + description, + labels, + ...(offsetStartDate && { + [this.customFields.get("Start date")!]: offsetStartDate, + }), + ...(offsetDueDate && { + [this.customFields.get("Due date")!]: offsetDueDate, + }), + ...(sprint && + sprint.id && { + [this.customFields.get("Sprint")!]: +sprint.id, + }), + ...(storyPointsEstimate && { + [this.customFields.get("Story point estimate")!]: + storyPointsEstimate, + }), + ...(relevantIssueType && relevantIssueType.name === 'Epic' && { + [this.customFields.get("Epic Name")!]: summary + }), + // ...(files && { + // [this.customFields.get("Attachment")!]: files, + // }), }, - }), - description, - labels, - ...(offsetStartDate && { - [this.customFields.get("Start date")!]: offsetStartDate, - }), - ...(offsetDueDate && { - [this.customFields.get("Due date")!]: offsetDueDate, - }), - ...(sprint && - sprint.id && { - [this.customFields.get("Sprint")!]: +sprint.id, - }), - ...(storyPointsEstimate && { - [this.customFields.get("Story point estimate")!]: - storyPointsEstimate, - }), - // ...(files && { - // [this.customFields.get("Attachment")!]: files, - // }), - }, - } - ) - .then(async (response) => { - const createdIssue = response.data - resolve(JSON.stringify(createdIssue.key)) - await this.setTransition(createdIssue.id, status) - }) - .catch((error) => { - let specificError = error - if (error.response) { - if (error.response.status === 404) { - specificError = new Error("The user does not have the necessary permissions") - } - } + } + ) + .then(async (response) => { + const createdIssue = response.data + resolve(JSON.stringify(createdIssue.key)) + await this.setTransition(createdIssue.id, status) + }) + .catch((error) => { + let specificError = error + if (error.response) { + if (error.response.status === 404) { + specificError = new Error("The user does not have the necessary permissions") + } + } - reject(new Error(`Error creating issue: ${specificError}`)) + reject(new Error(`Error creating issue: ${specificError}`)) + }) }) }) } From 6663b0f6c03829198c62fc4f726b2085c6089db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20R=C3=BCsch?= Date: Wed, 6 Dec 2023 11:24:01 +0100 Subject: [PATCH 10/10] Revert accidental changes --- .../jira-server-provider/JiraServerProvider.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/electron/providers/jira-server-provider/JiraServerProvider.ts b/electron/providers/jira-server-provider/JiraServerProvider.ts index 3c950f7d..402c4668 100644 --- a/electron/providers/jira-server-provider/JiraServerProvider.ts +++ b/electron/providers/jira-server-provider/JiraServerProvider.ts @@ -620,7 +620,16 @@ export class JiraServerProvider implements IProvider { } getLabels(): Promise { - throw new Error("Method not implemented for Jira Server") + return new Promise((resolve, reject) => { + this.getRestApiClient(2) + .get('/jql/autocompletedata/suggestions?fieldName=labels') + .then((response: AxiosResponse<{ results: { value: string }[] }>) => { + resolve(response.data.results.map((result) => result.value)) + }) + .catch((error) => + reject(new Error(`Error in fetching labels: ${JSON.stringify(error)}`)) + ) + }) } getPriorities(): Promise {