-
Notifications
You must be signed in to change notification settings - Fork 506
feat(GitLab): Browse GitLab issues and merge requests (frontend) #7273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
731a8d2
c644394
8075549
db7a085
1b4b870
aee2bda
440a1ac
6477ce8
2a1266b
ed874d6
c4a4307
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { useGetGitLabConfigurationQuery } from 'common/services/useGitlabConfiguration' | ||
|
|
||
| export function useHasGitLabIntegration(projectId: number) { | ||
| const { data } = useGetGitLabConfigurationQuery( | ||
| { project_id: projectId }, | ||
| { skip: !projectId }, | ||
| ) | ||
| return { hasIntegration: !!data?.length } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| import { Res } from 'common/types/responses' | ||
| import { Req } from 'common/types/requests' | ||
| import { service } from 'common/service' | ||
| import Utils from 'common/utils/utils' | ||
|
|
||
| export const gitlabService = service | ||
| .enhanceEndpoints({ addTagTypes: ['GitLab'] }) | ||
| .injectEndpoints({ | ||
| endpoints: (builder) => ({ | ||
| getGitLabIssues: builder.query< | ||
| Res['gitlabIssues'], | ||
| Req['getGitLabIssues'] | ||
| >({ | ||
| providesTags: [{ id: 'LIST', type: 'GitLab' }], | ||
| query: (query: Req['getGitLabIssues']) => ({ | ||
| url: `projects/${query.project_id}/gitlab/issues/?${Utils.toParam({ | ||
| gitlab_project_id: query.gitlab_project_id, | ||
| page: query.page ?? 1, | ||
| page_size: query.page_size ?? 100, | ||
| search_text: query.q || undefined, | ||
| state: 'opened', // Only open items are linkable to feature flags. | ||
| })}`, | ||
| }), | ||
| }), | ||
| getGitLabMergeRequests: builder.query< | ||
| Res['gitlabMergeRequests'], | ||
| Req['getGitLabMergeRequests'] | ||
| >({ | ||
| providesTags: [{ id: 'LIST', type: 'GitLab' }], | ||
| query: (query: Req['getGitLabMergeRequests']) => ({ | ||
| url: `projects/${ | ||
| query.project_id | ||
| }/gitlab/merge-requests/?${Utils.toParam({ | ||
| gitlab_project_id: query.gitlab_project_id, | ||
| page: query.page ?? 1, | ||
| page_size: query.page_size ?? 100, | ||
| search_text: query.q || undefined, | ||
| state: 'opened', // Only open items are linkable to feature flags. | ||
| })}`, | ||
| }), | ||
| }), | ||
| getGitLabProjects: builder.query< | ||
| Res['gitlabProjects'], | ||
| Req['getGitLabProjects'] | ||
| >({ | ||
| providesTags: [{ id: 'LIST', type: 'GitLab' }], | ||
| query: (query: Req['getGitLabProjects']) => ({ | ||
| url: `projects/${query.project_id}/gitlab/projects/?${Utils.toParam({ | ||
| page: query.page ?? 1, | ||
| page_size: query.page_size ?? 100, | ||
| })}`, | ||
| }), | ||
| }), | ||
| // END OF ENDPOINTS | ||
| }), | ||
| }) | ||
|
|
||
| export async function getGitLabProjects( | ||
| store: any, | ||
| data: Req['getGitLabProjects'], | ||
| options?: Parameters< | ||
| typeof gitlabService.endpoints.getGitLabProjects.initiate | ||
| >[1], | ||
| ) { | ||
| return store.dispatch( | ||
| gitlabService.endpoints.getGitLabProjects.initiate(data, options), | ||
| ) | ||
| } | ||
| export async function getGitLabIssues( | ||
| store: any, | ||
| data: Req['getGitLabIssues'], | ||
| options?: Parameters< | ||
| typeof gitlabService.endpoints.getGitLabIssues.initiate | ||
| >[1], | ||
| ) { | ||
| return store.dispatch( | ||
| gitlabService.endpoints.getGitLabIssues.initiate(data, options), | ||
| ) | ||
| } | ||
| export async function getGitLabMergeRequests( | ||
| store: any, | ||
| data: Req['getGitLabMergeRequests'], | ||
| options?: Parameters< | ||
| typeof gitlabService.endpoints.getGitLabMergeRequests.initiate | ||
| >[1], | ||
| ) { | ||
| return store.dispatch( | ||
| gitlabService.endpoints.getGitLabMergeRequests.initiate(data, options), | ||
| ) | ||
| } | ||
| // END OF FUNCTION_EXPORTS | ||
|
|
||
| export const { | ||
| useGetGitLabIssuesQuery, | ||
| useGetGitLabMergeRequestsQuery, | ||
| useGetGitLabProjectsQuery, | ||
| // END OF EXPORTS | ||
| } = gitlabService |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import { Res } from 'common/types/responses' | ||
| import { Req } from 'common/types/requests' | ||
| import { service } from 'common/service' | ||
|
|
||
| export const gitlabConfigurationService = service | ||
| .enhanceEndpoints({ addTagTypes: ['GitLabConfiguration'] }) | ||
| .injectEndpoints({ | ||
| endpoints: (builder) => ({ | ||
| getGitLabConfiguration: builder.query< | ||
| Res['gitlabConfiguration'], | ||
| Req['getGitLabConfiguration'] | ||
| >({ | ||
| providesTags: [{ id: 'LIST', type: 'GitLabConfiguration' }], | ||
| query: (query: Req['getGitLabConfiguration']) => ({ | ||
| url: `projects/${query.project_id}/integrations/gitlab/`, | ||
| }), | ||
| }), | ||
| // END OF ENDPOINTS | ||
| }), | ||
| }) | ||
|
|
||
| export async function getGitLabConfiguration( | ||
| store: any, | ||
| data: Req['getGitLabConfiguration'], | ||
| options?: Parameters< | ||
| typeof gitlabConfigurationService.endpoints.getGitLabConfiguration.initiate | ||
| >[1], | ||
| ) { | ||
| return store.dispatch( | ||
| gitlabConfigurationService.endpoints.getGitLabConfiguration.initiate( | ||
| data, | ||
| options, | ||
| ), | ||
| ) | ||
| } | ||
| // END OF FUNCTION_EXPORTS | ||
|
|
||
| export const { | ||
| useGetGitLabConfigurationQuery, | ||
| // END OF EXPORTS | ||
| } = gitlabConfigurationService | ||
|
|
||
| /* Usage examples: | ||
| const { data, isLoading } = useGetGitLabConfigurationQuery({ project_id: 2 }, {}) //get hook | ||
| gitlabConfigurationService.endpoints.getGitLabConfiguration.select({project_id: 2})(store.getState()) //access data from any function | ||
| */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| import React, { FC, useState } from 'react' | ||
| import Constants from 'common/constants' | ||
| import ErrorMessage from './ErrorMessage' | ||
| import GitLabProjectSelect from './GitLabProjectSelect' | ||
| import GitLabSearchSelect from './GitLabSearchSelect' | ||
| import { useGetGitLabProjectsQuery } from 'common/services/useGitlab' | ||
| import type { | ||
| GitLabIssue, | ||
| GitLabLinkType, | ||
| GitLabMergeRequest, | ||
| } from 'common/types/responses' | ||
|
|
||
| type GitLabLinkSectionProps = { | ||
| projectId: number | ||
| linkedUrls: string[] | ||
| } | ||
|
|
||
| const GitLabLinkSection: FC<GitLabLinkSectionProps> = ({ | ||
| linkedUrls, | ||
| projectId, | ||
| }) => { | ||
| const gitlabTypes = Object.values(Constants.resourceTypes).filter( | ||
| (v) => v.type === 'GITLAB', | ||
| ) | ||
|
|
||
| const [gitlabProjectId, setGitlabProjectId] = useState<number | null>(null) | ||
| const [linkType, setLinkType] = useState<GitLabLinkType>('issue') | ||
| const [selectedItem, setSelectedItem] = useState< | ||
| GitLabIssue | GitLabMergeRequest | null | ||
| >(null) | ||
|
|
||
| const { | ||
| data: projectsData, | ||
| isError: isProjectsError, | ||
| isLoading: isProjectsLoading, | ||
| } = useGetGitLabProjectsQuery({ | ||
| page: 1, | ||
| page_size: 100, | ||
| project_id: projectId, | ||
| }) | ||
| const projects = projectsData?.results ?? [] | ||
|
|
||
| return ( | ||
| <div> | ||
| <label className='cols-sm-2 control-label'> | ||
| Link GitLab issue or merge request | ||
| </label> | ||
| <div className='d-flex gap-2 mb-2'> | ||
| <GitLabProjectSelect | ||
| projects={projects} | ||
| isLoading={isProjectsLoading} | ||
| isDisabled={isProjectsError} | ||
| value={gitlabProjectId} | ||
| onChange={setGitlabProjectId} | ||
| /> | ||
| <div style={{ width: 200 }}> | ||
| <Select | ||
| autoSelect | ||
| className='w-100 react-select' | ||
| size='select-md' | ||
| placeholder='Select type' | ||
| value={gitlabTypes.find((v) => v.resourceType === linkType)} | ||
| onChange={(v: { resourceType: GitLabLinkType }) => { | ||
| setLinkType(v.resourceType) | ||
| setSelectedItem(null) | ||
| }} | ||
| options={gitlabTypes.map((e) => ({ | ||
| label: e.label, | ||
| resourceType: e.resourceType, | ||
| value: e.id, | ||
| }))} | ||
| /> | ||
| </div> | ||
| </div> | ||
| {isProjectsError && ( | ||
| <ErrorMessage error='Failed to load GitLab projects' /> | ||
| )} | ||
|
Comment on lines
+75
to
+77
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feel free to resolve. Wdyt @talissoncosta ? |
||
| {gitlabProjectId != null && ( | ||
| <> | ||
| <GitLabSearchSelect | ||
| projectId={projectId} | ||
| gitlabProjectId={gitlabProjectId} | ||
| linkType={linkType} | ||
| value={selectedItem} | ||
| onChange={(item) => setSelectedItem(item)} | ||
| linkedUrls={linkedUrls} | ||
| /> | ||
| <div className='text-right mt-2'> | ||
| <Button disabled theme='primary'> | ||
| Link | ||
| </Button> | ||
| </div> | ||
| </> | ||
| )} | ||
| </div> | ||
| ) | ||
| } | ||
|
|
||
| export default GitLabLinkSection | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import React, { FC } from 'react' | ||
| import type { GitLabProject } from 'common/types/responses' | ||
|
|
||
| type GitLabProjectSelectProps = { | ||
| projects: GitLabProject[] | ||
| isLoading: boolean | ||
| isDisabled: boolean | ||
| value: number | null | ||
| onChange: (id: number) => void | ||
| } | ||
|
|
||
| const GitLabProjectSelect: FC<GitLabProjectSelectProps> = ({ | ||
| isDisabled, | ||
| isLoading, | ||
| onChange, | ||
| projects, | ||
| value, | ||
| }) => { | ||
| const options = projects.map((p) => ({ | ||
| label: p.path_with_namespace, | ||
| value: p.id, | ||
| })) | ||
|
|
||
| return ( | ||
| <div style={{ minWidth: 250 }}> | ||
| <Select | ||
| className='w-100 react-select' | ||
| size='select-md' | ||
| placeholder={isLoading ? 'Loading...' : 'Select GitLab Project'} | ||
| value={options.find((o) => o.value === value) ?? null} | ||
| onChange={(v: { value: number }) => onChange(v.value)} | ||
| options={options} | ||
| isLoading={isLoading} | ||
| isDisabled={isDisabled} | ||
| /> | ||
| </div> | ||
| ) | ||
| } | ||
|
|
||
| export default GitLabProjectSelect |
Uh oh!
There was an error while loading. Please reload this page.