diff options
author | Philippe Perrin <philippe.perrin@sonarsource.com> | 2023-06-07 17:14:14 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-06-14 09:51:06 +0000 |
commit | 546ea739e4a652bb1076aa60b7a9ad4f01c5b0f2 (patch) | |
tree | ad5c52404a090adac2d4cbec30a6097a13144115 | |
parent | 815cf53518e591b48b8af46f9840e4cc54c4bb65 (diff) | |
download | sonarqube-546ea739e4a652bb1076aa60b7a9ad4f01c5b0f2.tar.gz sonarqube-546ea739e4a652bb1076aa60b7a9ad4f01c5b0f2.zip |
SONAR-19454 New code definition is made part of Gitlab project onboarding
8 files changed, 109 insertions, 162 deletions
diff --git a/server/sonar-web/src/main/js/api/alm-integrations.ts b/server/sonar-web/src/main/js/api/alm-integrations.ts index f33562053c0..bd225e1e2dc 100644 --- a/server/sonar-web/src/main/js/api/alm-integrations.ts +++ b/server/sonar-web/src/main/js/api/alm-integrations.ts @@ -250,13 +250,16 @@ export function getGitlabProjects(data: { .catch(throwGlobalError); } +export function setupGitlabProjectCreation(data: { almSetting: string; gitlabProjectId: string }) { + return (newCodeDefinitionType?: string, newCodeDefinitionValue?: string) => + importGitlabProject({ ...data, newCodeDefinitionType, newCodeDefinitionValue }); +} + export function importGitlabProject(data: { almSetting: string; gitlabProjectId: string; + newCodeDefinitionType?: string; + newCodeDefinitionValue?: string; }): Promise<{ project: ProjectBase }> { - const { almSetting, gitlabProjectId } = data; - return postJSON('/api/alm_integrations/import_gitlab_project', { - almSetting, - gitlabProjectId, - }).catch(throwGlobalError); + return postJSON('/api/alm_integrations/import_gitlab_project', data).catch(throwGlobalError); } diff --git a/server/sonar-web/src/main/js/api/mocks/AlmIntegrationsServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/AlmIntegrationsServiceMock.ts index 59c1c9b3011..e34143b5c02 100644 --- a/server/sonar-web/src/main/js/api/mocks/AlmIntegrationsServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/AlmIntegrationsServiceMock.ts @@ -60,6 +60,7 @@ import { setupAzureProjectCreation, setupBitbucketCloudProjectCreation, setupBitbucketServerProjectCreation, + setupGitlabProjectCreation, } from '../alm-integrations'; export default class AlmIntegrationsServiceMock { @@ -187,6 +188,7 @@ export default class AlmIntegrationsServiceMock { .mockImplementation(this.checkPersonalAccessTokenIsValid); jest.mocked(setAlmPersonalAccessToken).mockImplementation(this.setAlmPersonalAccessToken); jest.mocked(getGitlabProjects).mockImplementation(this.getGitlabProjects); + jest.mocked(setupGitlabProjectCreation).mockReturnValue(() => this.importProject()); jest.mocked(importGitlabProject).mockImplementation(this.importProject); jest.mocked(setupBitbucketCloudProjectCreation).mockReturnValue(() => this.importProject()); jest.mocked(importBitbucketCloudRepository).mockImplementation(this.importProject); diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx index fe3e7e1dda0..0a8d1add2b6 100644 --- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx @@ -255,9 +255,9 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp canAdmin={!!canAdmin} loadingBindings={loading} location={location} - onProjectCreate={this.handleProjectCreate} router={router} almInstances={gitlabSettings} + onProjectSetupDone={this.handleProjectSetupDone} /> ); } diff --git a/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreate.tsx index cfa49099e20..6478fc3709f 100644 --- a/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreate.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreate.tsx @@ -18,24 +18,24 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { getGitlabProjects, importGitlabProject } from '../../../../api/alm-integrations'; +import { getGitlabProjects, setupGitlabProjectCreation } from '../../../../api/alm-integrations'; import { Location, Router } from '../../../../components/hoc/withRouter'; import { GitlabProject } from '../../../../types/alm-integration'; import { AlmSettingsInstance } from '../../../../types/alm-settings'; import { Paging } from '../../../../types/types'; +import { CreateProjectApiCallback } from '../types'; import GitlabProjectCreateRenderer from './GitlabProjectCreateRenderer'; interface Props { canAdmin: boolean; loadingBindings: boolean; - onProjectCreate: (projectKey: string) => void; almInstances: AlmSettingsInstance[]; location: Location; router: Router; + onProjectSetupDone: (createProject: CreateProjectApiCallback) => void; } interface State { - importingGitlabProjectId?: string; loading: boolean; loadingMore: boolean; projects?: GitlabProject[]; @@ -134,34 +134,13 @@ export default class GitlabProjectCreate extends React.PureComponent<Props, Stat } }; - doImport = async (gitlabProjectId: string) => { + handleImport = (gitlabProjectId: string) => { const { selectedAlmInstance } = this.state; - if (!selectedAlmInstance) { - return Promise.resolve(undefined); - } - - try { - return await importGitlabProject({ - almSetting: selectedAlmInstance.key, - gitlabProjectId, - }); - } catch (_) { - return this.handleError(); - } - }; - - handleImport = async (gitlabProjectId: string) => { - this.setState({ importingGitlabProjectId: gitlabProjectId }); - - const result = await this.doImport(gitlabProjectId); - - if (this.mounted) { - this.setState({ importingGitlabProjectId: undefined }); - - if (result) { - this.props.onProjectCreate(result.project.key); - } + if (selectedAlmInstance) { + this.props.onProjectSetupDone( + setupGitlabProjectCreation({ almSetting: selectedAlmInstance.key, gitlabProjectId }) + ); } }; @@ -221,7 +200,6 @@ export default class GitlabProjectCreate extends React.PureComponent<Props, Stat render() { const { loadingBindings, location, almInstances, canAdmin } = this.props; const { - importingGitlabProjectId, loading, loadingMore, projects, @@ -238,7 +216,6 @@ export default class GitlabProjectCreate extends React.PureComponent<Props, Stat canAdmin={canAdmin} almInstances={almInstances} selectedAlmInstance={selectedAlmInstance} - importingGitlabProjectId={importingGitlabProjectId} loading={loading || loadingBindings} loadingMore={loadingMore} onImport={this.handleImport} diff --git a/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreateRenderer.tsx b/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreateRenderer.tsx index 366f5610d70..695ce5dad36 100644 --- a/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreateRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreateRenderer.tsx @@ -31,7 +31,6 @@ import GitlabProjectSelectionForm from './GitlabProjectSelectionForm'; export interface GitlabProjectCreateRendererProps { canAdmin?: boolean; - importingGitlabProjectId?: string; loading: boolean; loadingMore: boolean; onImport: (gitlabProjectId: string) => void; @@ -52,7 +51,6 @@ export interface GitlabProjectCreateRendererProps { export default function GitlabProjectCreateRenderer(props: GitlabProjectCreateRendererProps) { const { canAdmin, - importingGitlabProjectId, loading, loadingMore, projects, @@ -104,7 +102,6 @@ export default function GitlabProjectCreateRenderer(props: GitlabProjectCreateRe /> ) : ( <GitlabProjectSelectionForm - importingGitlabProjectId={importingGitlabProjectId} loadingMore={loadingMore} onImport={props.onImport} onLoadMore={props.onLoadMore} diff --git a/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectSelectionForm.tsx b/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectSelectionForm.tsx index 56f3c8ebb39..87926e314bc 100644 --- a/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectSelectionForm.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectSelectionForm.tsx @@ -27,17 +27,14 @@ import { Button } from '../../../../components/controls/buttons'; import CheckIcon from '../../../../components/icons/CheckIcon'; import QualifierIcon from '../../../../components/icons/QualifierIcon'; import { Alert } from '../../../../components/ui/Alert'; -import DeferredSpinner from '../../../../components/ui/DeferredSpinner'; import { translate } from '../../../../helpers/l10n'; import { getProjectUrl, queryToSearch } from '../../../../helpers/urls'; import { GitlabProject } from '../../../../types/alm-integration'; import { ComponentQualifier } from '../../../../types/component'; import { Paging } from '../../../../types/types'; -import InstanceNewCodeDefinitionComplianceWarning from '../components/InstanceNewCodeDefinitionComplianceWarning'; import { CreateProjectModes } from '../types'; export interface GitlabProjectSelectionFormProps { - importingGitlabProjectId?: string; loadingMore: boolean; onImport: (gitlabProjectId: string) => void; onLoadMore: () => void; @@ -49,14 +46,7 @@ export interface GitlabProjectSelectionFormProps { } export default function GitlabProjectSelectionForm(props: GitlabProjectSelectionFormProps) { - const { - importingGitlabProjectId, - loadingMore, - projects = [], - projectsPaging, - searching, - searchQuery, - } = props; + const { loadingMore, projects = [], projectsPaging, searching, searchQuery } = props; if (projects.length === 0 && searchQuery.length === 0 && !searching) { return ( @@ -82,92 +72,82 @@ export default function GitlabProjectSelectionForm(props: GitlabProjectSelection } return ( - <> - <InstanceNewCodeDefinitionComplianceWarning /> - <div className="boxed-group big-padded create-project-import"> - <SearchBox - className="spacer" - loading={searching} - minLength={3} - onChange={props.onSearch} - placeholder={translate('onboarding.create_project.search_prompt')} - /> + <div className="boxed-group big-padded create-project-import"> + <SearchBox + className="spacer" + loading={searching} + minLength={3} + onChange={props.onSearch} + placeholder={translate('onboarding.create_project.search_prompt')} + /> - <hr /> + <hr /> - {projects.length === 0 ? ( - <div className="padded">{translate('no_results')}</div> - ) : ( - <table className="data zebra zebra-hover"> - <tbody> - {projects.map((project) => ( - <tr key={project.id}> + {projects.length === 0 ? ( + <div className="padded">{translate('no_results')}</div> + ) : ( + <table className="data zebra zebra-hover"> + <tbody> + {projects.map((project) => ( + <tr key={project.id}> + <td> + <Tooltip overlay={project.slug}> + <strong className="project-name display-inline-block text-ellipsis"> + {project.sqProjectKey ? ( + <Link to={getProjectUrl(project.sqProjectKey)}> + <QualifierIcon + className="spacer-right" + qualifier={ComponentQualifier.Project} + /> + {project.sqProjectName} + </Link> + ) : ( + project.name + )} + </strong> + </Tooltip> + <br /> + <Tooltip overlay={project.pathSlug}> + <span className="text-muted project-path display-inline-block text-ellipsis"> + {project.pathName} + </span> + </Tooltip> + </td> + <td> + <Link + className="display-inline-flex-center big-spacer-right" + to={project.url} + target="_blank" + > + {translate('onboarding.create_project.gitlab.link')} + </Link> + </td> + {project.sqProjectKey ? ( <td> - <Tooltip overlay={project.slug}> - <strong className="project-name display-inline-block text-ellipsis"> - {project.sqProjectKey ? ( - <Link to={getProjectUrl(project.sqProjectKey)}> - <QualifierIcon - className="spacer-right" - qualifier={ComponentQualifier.Project} - /> - {project.sqProjectName} - </Link> - ) : ( - project.name - )} - </strong> - </Tooltip> - <br /> - <Tooltip overlay={project.pathSlug}> - <span className="text-muted project-path display-inline-block text-ellipsis"> - {project.pathName} - </span> - </Tooltip> + <span className="display-flex-center display-flex-justify-end already-set-up"> + <CheckIcon className="little-spacer-right" size={12} /> + {translate('onboarding.create_project.repository_imported')} + </span> </td> - <td> - <Link - className="display-inline-flex-center big-spacer-right" - to={project.url} - target="_blank" - > - {translate('onboarding.create_project.gitlab.link')} - </Link> + ) : ( + <td className="text-right"> + <Button onClick={() => props.onImport(project.id)}> + {translate('onboarding.create_project.set_up')} + </Button> </td> - {project.sqProjectKey ? ( - <td> - <span className="display-flex-center display-flex-justify-end already-set-up"> - <CheckIcon className="little-spacer-right" size={12} /> - {translate('onboarding.create_project.repository_imported')} - </span> - </td> - ) : ( - <td className="text-right"> - <Button - disabled={!!importingGitlabProjectId} - onClick={() => props.onImport(project.id)} - > - {translate('onboarding.create_project.set_up')} - <DeferredSpinner - className="spacer-left" - loading={importingGitlabProjectId === project.id} - /> - </Button> - </td> - )} - </tr> - ))} - </tbody> - </table> - )} - <ListFooter - count={projects.length} - loadMore={props.onLoadMore} - loading={loadingMore} - pageSize={projectsPaging.pageSize} - total={projectsPaging.total} - /> - </div> - </> + )} + </tr> + ))} + </tbody> + </table> + )} + <ListFooter + count={projects.length} + loadMore={props.onLoadMore} + loading={loadingMore} + pageSize={projectsPaging.pageSize} + total={projectsPaging.total} + /> + </div> ); } diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx index aa090fb0c25..c3e0080516f 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx @@ -26,9 +26,7 @@ import { getGitlabProjects } from '../../../../api/alm-integrations'; import AlmIntegrationsServiceMock from '../../../../api/mocks/AlmIntegrationsServiceMock'; import AlmSettingsServiceMock from '../../../../api/mocks/AlmSettingsServiceMock'; import NewCodePeriodsServiceMock from '../../../../api/mocks/NewCodePeriodsServiceMock'; -import { mockNewCodePeriod } from '../../../../helpers/mocks/new-code-period'; import { renderApp } from '../../../../helpers/testReactTestingUtils'; -import { NewCodePeriodSettingType } from '../../../../types/types'; import CreateProjectPage, { CreateProjectPageProps } from '../CreateProjectPage'; jest.mock('../../../../api/alm-integrations'); @@ -108,16 +106,24 @@ it('should show import project feature when PAT is already set', async () => { '/dashboard?id=key' ); - projectItem = screen.getByRole('row', { - name: 'Gitlab project 2 Company / Best Projects opens_in_new_window onboarding.create_project.gitlab.link onboarding.create_project.set_up', - }); - const importProjectButton = within(projectItem).getByRole('button', { + projectItem = screen.getByRole('row', { name: /Gitlab project 2/ }); + const setupButton = within(projectItem).getByRole('button', { name: 'onboarding.create_project.set_up', }); - await act(async () => { - await user.click(importProjectButton); - }); + await user.click(setupButton); + + expect( + screen.getByRole('heading', { name: 'onboarding.create_project.new_code_definition.title' }) + ).toBeInTheDocument(); + + await user.click(screen.getByRole('radio', { name: 'new_code_definition.global_setting' })); + await user.click( + screen.getByRole('button', { + name: 'onboarding.create_project.new_code_definition.create_project', + }) + ); + expect(await screen.findByText('/dashboard?id=key')).toBeInTheDocument(); }); @@ -182,24 +188,6 @@ it('should show no result message when there are no projects', async () => { expect(screen.getByText('onboarding.create_project.gitlab.no_projects')).toBeInTheDocument(); }); -it('should display a warning if the instance default new code definition is not CaYC compliant', async () => { - const user = userEvent.setup(); - newCodePeriodHandler.setNewCodePeriod( - mockNewCodePeriod({ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '91' }) - ); - renderCreateProject(); - await act(async () => { - await user.click(ui.gitlabCreateProjectButton.get()); - await selectEvent.select(ui.instanceSelector.get(), [/conf-final-2/]); - }); - - expect(screen.getByText('Gitlab project 1')).toBeInTheDocument(); - expect(screen.getByText('Gitlab project 2')).toBeInTheDocument(); - expect( - screen.getByText('onboarding.create_project.new_code_option.warning.title') - ).toBeInTheDocument(); -}); - function renderCreateProject(props: Partial<CreateProjectPageProps> = {}) { renderApp('project/create', <CreateProjectPage {...props} />); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolver.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolver.java index 968ffc732c1..e5efa29a274 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolver.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolver.java @@ -43,7 +43,7 @@ public class NewCodeDefinitionResolver { private static final String BEGIN_ITEM_LIST = "<li>"; private static final String END_ITEM_LIST = "</li>"; - public static final String NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION = "Type<br/>" + + public static final String NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION = "Project New Code Definition Type<br/>" + "New code definitions of the following types are allowed:" + BEGIN_LIST + BEGIN_ITEM_LIST + PREVIOUS_VERSION.name() + END_ITEM_LIST + @@ -51,7 +51,7 @@ public class NewCodeDefinitionResolver { BEGIN_ITEM_LIST + REFERENCE_BRANCH.name() + " - will default to the main branch." + END_ITEM_LIST + END_LIST; - public static final String NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION = "Value<br/>" + + public static final String NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION = "Project New Code Definition Value<br/>" + "For each new code definition type, a different value is expected:" + BEGIN_LIST + BEGIN_ITEM_LIST + "no value, when the new code definition type is " + PREVIOUS_VERSION.name() + " and " + REFERENCE_BRANCH.name() + END_ITEM_LIST + |