From d83ca259666d0136972615fe42edec9abac13447 Mon Sep 17 00:00:00 2001 From: Clemens Bergmann Date: Thu, 16 Jan 2025 00:17:53 +0100 Subject: [PATCH 1/6] allow import of group and instance issues --- .../gitlab/gitlab-api/gitlab-api.service.ts | 43 +++++++++++++------ .../issue/providers/gitlab/gitlab.const.ts | 15 +++++++ .../issue/providers/gitlab/gitlab.model.ts | 1 + src/app/t.const.ts | 4 ++ src/assets/i18n/en.json | 6 ++- 5 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts b/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts index 8786d8c0242..8e530808319 100644 --- a/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts +++ b/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts @@ -80,7 +80,7 @@ export class GitlabApiService { // // const PARAMS_COUNT = 59; // Can't send more than 59 issue id For some reason it returns 502 bad gateway // return this._sendIssuePaginatedRequest$( // { - // url: `${this._apiLink(cfg)}/issues?${queryParams}${this.getScopeParam( + // url: `${this._autoScopedApiLink(cfg)}/issues?${queryParams}${this.getScopeParam( // cfg, // )}${this.getCustomFilterParam(cfg)}`, // }, @@ -119,7 +119,7 @@ export class GitlabApiService { } return this._sendIssuePaginatedRequest$( { - url: `${this._apiLink(cfg)}/issues?search=${searchText}${this.getScopeParam( + url: `${this._autoScopedApiLink(cfg)}/issues?search=${searchText}${this.getScopeParam( cfg, )}&order_by=updated_at${this.getCustomFilterParam(cfg)}`, }, @@ -143,7 +143,7 @@ export class GitlabApiService { getProjectIssues$(cfg: GitlabCfg): Observable { return this._sendIssuePaginatedRequest$( { - url: `${this._apiLink( + url: `${this._autoScopedApiLink( cfg, )}/issues?state=opened&order_by=updated_at&${this.getScopeParam( cfg, @@ -166,11 +166,9 @@ export class GitlabApiService { total_time_spent: null | number; }*/ - const { projectIssueId } = getPartsFromGitlabIssueId(issueId); - return this._sendRawRequest$( { - url: `${this._apiLink(cfg)}/issues/${projectIssueId}/add_spent_time`, + url: `${this._issueApiLink(cfg, issueId)}/add_spent_time`, method: 'POST', data: { duration: duration, @@ -190,10 +188,9 @@ export class GitlabApiService { time_estimate: null | number; total_time_spent: null | number; }> { - const { projectIssueId } = getPartsFromGitlabIssueId(issueId); return this._sendRawRequest$( { - url: `${this._apiLink(cfg)}/issues/${projectIssueId}/time_stats`, + url: `${this._issueApiLink(cfg, issueId)}/time_stats`, }, cfg, ).pipe(map((res) => (res as any).body)); @@ -326,11 +323,33 @@ export class GitlabApiService { private _issueApiLink(cfg: GitlabCfg, issueId: string): string { console.log(issueId); - const { projectIssueId } = getPartsFromGitlabIssueId(issueId); - return `${this._apiLink(cfg)}/issues/${projectIssueId}`; + const { project, projectIssueId } = getPartsFromGitlabIssueId(issueId); + return `${this._baseApiLink(cfg)}/${this._projectUrl(project)}/issues/${projectIssueId}`; + } + + private _autoScopedApiLink(cfg: GitlabCfg): string { + let apiURL: string = this._baseApiLink(cfg); + + if (cfg.search_scope == 'group') { + const groupURL = assertTruthy(cfg.project) + .toString() + .split(/\//gi) + .slice(0, -1) + .join('%2F'); + apiURL += 'groups/' + groupURL; + } else if (cfg.search_scope == 'project' || cfg.search_scope === null) { + apiURL += this._projectUrl(cfg.project); + } + + return apiURL; + } + + private _projectUrl(project: string): string { + const projectURL = assertTruthy(project).toString().replace(/\//gi, '%2F'); + return 'projects/' + projectURL; } - private _apiLink(cfg: GitlabCfg): string { + private _baseApiLink(cfg: GitlabCfg): string { let apiURL: string = ''; if (cfg.gitlabBaseUrl) { @@ -342,8 +361,6 @@ export class GitlabApiService { apiURL = GITLAB_API_BASE_URL + '/'; } - const projectURL = assertTruthy(cfg.project).toString().replace(/\//gi, '%2F'); - apiURL += 'projects/' + projectURL; return apiURL; } } diff --git a/src/app/features/issue/providers/gitlab/gitlab.const.ts b/src/app/features/issue/providers/gitlab/gitlab.const.ts index 2c1c7869ad5..0f13723452b 100644 --- a/src/app/features/issue/providers/gitlab/gitlab.const.ts +++ b/src/app/features/issue/providers/gitlab/gitlab.const.ts @@ -21,6 +21,7 @@ export const DEFAULT_GITLAB_CFG: GitlabCfg = { scope: 'all', filter: null, isEnableTimeTracking: false, + search_scope: 'project', }; // NOTE: we need a high limit because git has low usage limits :( @@ -143,6 +144,20 @@ export const GITLAB_CONFIG_FORM: LimitedFormlyFieldConfig[] description: T.F.GITLAB.FORM.SUBMIT_TIMELOGS_DESCRIPTION, }, }, + { + key: 'search_scope', + type: 'select', + defaultValue: 'project', + templateOptions: { + required: true, + label: T.F.GITLAB.FORM.SEARCH_SCOPE, + options: [ + { value: 'project', label: T.F.GITLAB.FORM.SEARCH_SCOPE_PROJECT }, + { value: 'group', label: T.F.GITLAB.FORM.SEARCH_SCOPE_GROUP }, + { value: 'instance', label: T.F.GITLAB.FORM.SEARCH_SCOPE_INSTANCE }, + ], + }, + }, ], }, ]; diff --git a/src/app/features/issue/providers/gitlab/gitlab.model.ts b/src/app/features/issue/providers/gitlab/gitlab.model.ts index 6313786065f..56ad3fa196e 100644 --- a/src/app/features/issue/providers/gitlab/gitlab.model.ts +++ b/src/app/features/issue/providers/gitlab/gitlab.model.ts @@ -8,4 +8,5 @@ export interface GitlabCfg extends BaseIssueProviderCfg { scope: string | null; filter: string | null; isEnableTimeTracking: boolean; + search_scope: string | null; } diff --git a/src/app/t.const.ts b/src/app/t.const.ts index 572b55518af..b89440573b5 100644 --- a/src/app/t.const.ts +++ b/src/app/t.const.ts @@ -284,6 +284,10 @@ const T = { SUBMIT_TIMELOGS: 'F.GITLAB.FORM.SUBMIT_TIMELOGS', SUBMIT_TIMELOGS_DESCRIPTION: 'F.GITLAB.FORM.SUBMIT_TIMELOGS_DESCRIPTION', TOKEN: 'F.GITLAB.FORM.TOKEN', + SEARCH_SCOPE: 'F.GITLAB.FORM.SEARCH_SCOPE', + SEARCH_SCOPE_PROJECT: 'F.GITLAB.FORM.SEARCH_SCOPE_PROJECT', + SEARCH_SCOPE_GROUP: 'F.GITLAB.FORM.SEARCH_SCOPE_GROUP', + SEARCH_SCOPE_INSTANCE: 'F.GITLAB.FORM.SEARCH_SCOPE_INSTANCE', }, FORM_SECTION: { HELP: 'F.GITLAB.FORM_SECTION.HELP', diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 2a5a41b037e..9ccaab69951 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -281,7 +281,11 @@ "SOURCE_PROJECT": "Project", "SUBMIT_TIMELOGS": "Submit timelogs to Gitlab", "SUBMIT_TIMELOGS_DESCRIPTION": "Show Time Tracking Dialog after clicking on finish day", - "TOKEN": "Access Token" + "TOKEN": "Access Token", + "SEARCH_SCOPE": "Search Scope", + "SEARCH_SCOPE_INSTANCE": "Instance", + "SEARCH_SCOPE_GROUP": "Group", + "SEARCH_SCOPE_PROJECT": "Project" }, "FORM_SECTION": { "HELP": "

Here you can configure SuperProductivity to list open GitLab (either its the online version or a self-hosted instance) issues for a specific project in the task creation panel in the daily planning view. They will be listed as suggestions and will provide a link to the issue as well as more information about it.

In addition you can automatically import all open issues.

", From 39d943a82183821f9ce35bf95114cb889c97787e Mon Sep 17 00:00:00 2001 From: Clemens Bergmann Date: Thu, 16 Jan 2025 10:12:10 +0100 Subject: [PATCH 2/6] fix issue link --- .../gitlab/gitlab-api/gitlab-api.service.ts | 23 ++++++++++++------- .../gitlab-common-interfaces.service.ts | 10 +------- .../issue/providers/gitlab/gitlab.const.ts | 2 -- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts b/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts index 8e530808319..1c6ff5b5281 100644 --- a/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts +++ b/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts @@ -12,7 +12,7 @@ import { SnackService } from 'src/app/core/snack/snack.service'; import { GitlabCfg } from '../gitlab.model'; import { GitlabOriginalComment, GitlabOriginalIssue } from './gitlab-api-responses'; -import { GITLAB_API_BASE_URL } from '../gitlab.const'; +import { GITLAB_BASE_URL } from '../gitlab.const'; import { T } from 'src/app/t.const'; import { catchError, @@ -58,6 +58,11 @@ export class GitlabApiService { ); } + getIssuelink$(id: string, cfg: GitlabCfg): string { + const { project, projectIssueId } = getPartsFromGitlabIssueId(id); + return `${this._baseLink(cfg)}${project}/issues/${projectIssueId}`; + } + private getScopeParam(cfg: GitlabCfg): string { if (cfg.scope) { return `&scope=${cfg.scope}`; @@ -348,19 +353,21 @@ export class GitlabApiService { const projectURL = assertTruthy(project).toString().replace(/\//gi, '%2F'); return 'projects/' + projectURL; } - - private _baseApiLink(cfg: GitlabCfg): string { - let apiURL: string = ''; + + private _baseLink(cfg: GitlabCfg): string { + let baseURL: string = GITLAB_BASE_URL; if (cfg.gitlabBaseUrl) { const fixedUrl = cfg.gitlabBaseUrl.match(/.*\/$/) ? cfg.gitlabBaseUrl : `${cfg.gitlabBaseUrl}/`; - apiURL = fixedUrl + 'api/v4/'; - } else { - apiURL = GITLAB_API_BASE_URL + '/'; + baseURL = fixedUrl; } - return apiURL; + return baseURL; + } + + private _baseApiLink(cfg: GitlabCfg): string { + return this._baseLink(cfg) + 'api/v4/'; } } diff --git a/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts b/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts index 8574956232a..e1eeea8ed93 100644 --- a/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts +++ b/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts @@ -39,15 +39,7 @@ export class GitlabCommonInterfacesService implements IssueServiceInterface { issueLink$(issueId: string, issueProviderId: string): Observable { return this._getCfgOnce$(issueProviderId).pipe( map((cfg) => { - const project: string = cfg.project; - if (cfg.gitlabBaseUrl) { - const fixedUrl = cfg.gitlabBaseUrl.match(/.*\/$/) - ? cfg.gitlabBaseUrl - : `${cfg.gitlabBaseUrl}/`; - return `${fixedUrl}${project}/issues/${issueId}`; - } else { - return `${GITLAB_BASE_URL}${project}/issues/${issueId}`; - } + return this._gitlabApiService.getIssuelink$(issueId, cfg) }), ); } diff --git a/src/app/features/issue/providers/gitlab/gitlab.const.ts b/src/app/features/issue/providers/gitlab/gitlab.const.ts index 0f13723452b..425a010b357 100644 --- a/src/app/features/issue/providers/gitlab/gitlab.const.ts +++ b/src/app/features/issue/providers/gitlab/gitlab.const.ts @@ -31,8 +31,6 @@ export const GITLAB_INITIAL_POLL_DELAY = GITHUB_INITIAL_POLL_DELAY + 8000; // export const GITLAB_POLL_INTERVAL = 15 * 1000; export const GITLAB_BASE_URL = 'https://gitlab.com/'; -export const GITLAB_API_BASE_URL = `${GITLAB_BASE_URL}api/v4`; - export const GITLAB_PROJECT_REGEX = /(^[1-9][0-9]*$)|((\/|%2F|\w-?|\.-?)+$)/i; export const GITLAB_CONFIG_FORM: LimitedFormlyFieldConfig[] = [ From 68a390812a52432e40f6177f63223615ecbea6f8 Mon Sep 17 00:00:00 2001 From: Clemens Bergmann Date: Thu, 16 Jan 2025 10:17:33 +0100 Subject: [PATCH 3/6] remove unused import --- .../issue/providers/gitlab/gitlab-common-interfaces.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts b/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts index e1eeea8ed93..c7b63791115 100644 --- a/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts +++ b/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts @@ -9,7 +9,6 @@ import { GitlabCfg } from './gitlab.model'; import { GitlabIssue } from './gitlab-issue/gitlab-issue.model'; import { truncate } from '../../../../util/truncate'; import { - GITLAB_BASE_URL, GITLAB_INITIAL_POLL_DELAY, GITLAB_POLL_INTERVAL, } from './gitlab.const'; From 759e031b873dadddd0397084c464c76eb206efdd Mon Sep 17 00:00:00 2001 From: Clemens Bergmann Date: Thu, 16 Jan 2025 10:19:22 +0100 Subject: [PATCH 4/6] remove blanks --- .../issue/providers/gitlab/gitlab-api/gitlab-api.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts b/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts index 1c6ff5b5281..4cbd41f5789 100644 --- a/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts +++ b/src/app/features/issue/providers/gitlab/gitlab-api/gitlab-api.service.ts @@ -353,7 +353,7 @@ export class GitlabApiService { const projectURL = assertTruthy(project).toString().replace(/\//gi, '%2F'); return 'projects/' + projectURL; } - + private _baseLink(cfg: GitlabCfg): string { let baseURL: string = GITLAB_BASE_URL; From 7215f1e1685b1009c24973b38dbe7b6ef27bdcf4 Mon Sep 17 00:00:00 2001 From: Clemens Bergmann Date: Thu, 16 Jan 2025 10:21:56 +0100 Subject: [PATCH 5/6] add missing ; --- .../issue/providers/gitlab/gitlab-common-interfaces.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts b/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts index c7b63791115..e9360301cc9 100644 --- a/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts +++ b/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts @@ -38,7 +38,7 @@ export class GitlabCommonInterfacesService implements IssueServiceInterface { issueLink$(issueId: string, issueProviderId: string): Observable { return this._getCfgOnce$(issueProviderId).pipe( map((cfg) => { - return this._gitlabApiService.getIssuelink$(issueId, cfg) + return this._gitlabApiService.getIssuelink$(issueId, cfg); }), ); } From f2180d3c89c43c8152d7ce0f1bf29fc096c3ed0a Mon Sep 17 00:00:00 2001 From: Clemens Bergmann Date: Sun, 19 Jan 2025 05:44:57 +0100 Subject: [PATCH 6/6] fix lint --- .../providers/gitlab/gitlab-common-interfaces.service.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts b/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts index e9360301cc9..70169adf994 100644 --- a/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts +++ b/src/app/features/issue/providers/gitlab/gitlab-common-interfaces.service.ts @@ -8,10 +8,7 @@ import { IssueData, IssueProviderGitlab, SearchResultItem } from '../../issue.mo import { GitlabCfg } from './gitlab.model'; import { GitlabIssue } from './gitlab-issue/gitlab-issue.model'; import { truncate } from '../../../../util/truncate'; -import { - GITLAB_INITIAL_POLL_DELAY, - GITLAB_POLL_INTERVAL, -} from './gitlab.const'; +import { GITLAB_INITIAL_POLL_DELAY, GITLAB_POLL_INTERVAL } from './gitlab.const'; import { isGitlabEnabled } from './is-gitlab-enabled'; import { IssueProviderService } from '../../issue-provider.service';