diff --git a/web/jest.config.js b/web/jest.config.js index 21678a698..8c7036647 100644 --- a/web/jest.config.js +++ b/web/jest.config.js @@ -90,7 +90,7 @@ module.exports = { // } // }, transformIgnorePatterns: [ - 'node_modules/(?!(date-fns|lodash-es|@harnessio/uicore|@harnessio/design-system|@harnessio/react-har-service-client|@harnessio/react-ssca-manager-client)/)' + 'node_modules/(?!(date-fns|lodash-es|@harnessio/uicore|@harnessio/design-system|@harnessio/react-har-service-client|@harnessio/react-ssca-manager-client|@harnessio/react-ng-manager-client)/)' ], testPathIgnorePatterns: ['/dist', '/src/static'] } diff --git a/web/package.json b/web/package.json index a717fa82c..d22e5a86b 100644 --- a/web/package.json +++ b/web/package.json @@ -52,6 +52,7 @@ "@harnessio/design-system": "^2.1.1", "@harnessio/icons": "^2.1.9", "@harnessio/react-har-service-client": "^0.15.0", + "@harnessio/react-ng-manager-client": "^1.40.0", "@harnessio/react-ssca-manager-client": "^0.65.0", "@harnessio/uicore": "^4.1.2", "@tanstack/react-query": "4.20.4", diff --git a/web/src/ar/app/useOpenApiClient.ts b/web/src/ar/app/useOpenApiClient.ts index d30d3ff3e..3eddc27f8 100644 --- a/web/src/ar/app/useOpenApiClient.ts +++ b/web/src/ar/app/useOpenApiClient.ts @@ -17,6 +17,7 @@ import { useRef } from 'react' import { HARServiceAPIClient } from '@harnessio/react-har-service-client' import { SSCAManagerAPIClient } from '@harnessio/react-ssca-manager-client' +import { NGManagerServiceAPIClient } from '@harnessio/react-ng-manager-client' import type { CustomUtils } from '@ar/MFEAppTypes' @@ -62,4 +63,14 @@ export default function useOpenApiClient({ on401, customUtils }: useOpenApiClien } }) ) + + useRef( + new NGManagerServiceAPIClient({ + responseInterceptor, + requestInterceptor, + urlInterceptor: (url: string) => { + return window.getApiBaseUrl(url) + } + }) + ) } diff --git a/web/src/ar/constants.ts b/web/src/ar/constants.ts index cd12eda97..c15571d38 100644 --- a/web/src/ar/constants.ts +++ b/web/src/ar/constants.ts @@ -34,3 +34,6 @@ export enum PreferenceScope { USER = 'USER', MACHINE = 'MACHINE' // or workstation. This will act as default PreferenceScope } + +export const DEFAULT_ORG = 'default' +export const DEFAULT_PROJECT = 'default_project' diff --git a/web/src/ar/pages/repository-details/DockerRepository/__tests__/EditDockerRegistry.test.tsx b/web/src/ar/pages/repository-details/DockerRepository/__tests__/EditDockerRegistry.test.tsx index 8cf2a3a0e..5c1c5842f 100644 --- a/web/src/ar/pages/repository-details/DockerRepository/__tests__/EditDockerRegistry.test.tsx +++ b/web/src/ar/pages/repository-details/DockerRepository/__tests__/EditDockerRegistry.test.tsx @@ -51,6 +51,15 @@ jest.mock('react-router-dom', () => ({ }) })) +jest.mock('@harnessio/react-ng-manager-client', () => ({ + useGetOrgScopedProjectQuery: jest.fn().mockImplementation(() => ({ + isFetching: false, + refetch: jest.fn(), + error: false, + data: { content: { data: {} } } + })) +})) + jest.mock('@harnessio/react-har-service-client', () => ({ useGetRegistryQuery: jest.fn().mockImplementation(() => ({ isFetching: false, diff --git a/web/src/ar/pages/repository-details/DockerRepository/__tests__/EditDockerUpstreamRegistry.test.tsx b/web/src/ar/pages/repository-details/DockerRepository/__tests__/EditDockerUpstreamRegistry.test.tsx index fd6401b43..e7797ecb9 100644 --- a/web/src/ar/pages/repository-details/DockerRepository/__tests__/EditDockerUpstreamRegistry.test.tsx +++ b/web/src/ar/pages/repository-details/DockerRepository/__tests__/EditDockerUpstreamRegistry.test.tsx @@ -60,6 +60,15 @@ jest.mock('react-router-dom', () => ({ }) })) +jest.mock('@harnessio/react-ng-manager-client', () => ({ + useGetOrgScopedProjectQuery: jest.fn().mockImplementation(() => ({ + isFetching: false, + refetch: jest.fn(), + error: false, + data: { content: { data: {} } } + })) +})) + jest.mock('@harnessio/react-har-service-client', () => ({ useGetRegistryQuery: jest.fn().mockImplementation(() => ({ isFetching: false, diff --git a/web/src/ar/pages/repository-details/components/FormContent/FormContent.module.scss b/web/src/ar/pages/repository-details/components/FormContent/FormContent.module.scss index 89417133d..460b38818 100644 --- a/web/src/ar/pages/repository-details/components/FormContent/FormContent.module.scss +++ b/web/src/ar/pages/repository-details/components/FormContent/FormContent.module.scss @@ -48,3 +48,9 @@ column-gap: var(--spacing-medium); row-gap: var(--spacing-medium); } + +.helperText { + font-size: var(--font-size-small); + font-weight: 400; + color: var(--gray-900); +} diff --git a/web/src/ar/pages/repository-details/components/FormContent/FormContent.module.scss.d.ts b/web/src/ar/pages/repository-details/components/FormContent/FormContent.module.scss.d.ts index 4536e8a82..7f9156383 100644 --- a/web/src/ar/pages/repository-details/components/FormContent/FormContent.module.scss.d.ts +++ b/web/src/ar/pages/repository-details/components/FormContent/FormContent.module.scss.d.ts @@ -18,6 +18,7 @@ // This is an auto-generated file export declare const cardContainer: string export declare const cardHeading: string +export declare const helperText: string export declare const includeExcludeWrapper: string export declare const marginTopLarge: string export declare const scannersContainer: string diff --git a/web/src/ar/pages/repository-details/components/FormContent/SelectContainerScannersFormSection.tsx b/web/src/ar/pages/repository-details/components/FormContent/SelectContainerScannersFormSection.tsx index 85a3e6359..ab3647f7e 100644 --- a/web/src/ar/pages/repository-details/components/FormContent/SelectContainerScannersFormSection.tsx +++ b/web/src/ar/pages/repository-details/components/FormContent/SelectContainerScannersFormSection.tsx @@ -41,7 +41,6 @@ export default function SelectContainerScannersFormSection( }, [packageType]) if (!availableScannerOptions.length) return <> - return ( () - const hasRequiredLicense = - SSCA_LICENSE_STATE === LICENSE_STATE_VALUES.ACTIVE && STO_LICENSE_STATE === LICENSE_STATE_VALUES.ACTIVE + const { hasRequiredLicense, hasRequiredProjectConfig, orgIdentifier } = useCheckRequiredConfigForScan() const handleUpdateFormikState = (event: React.FormEvent) => { const isChecked = event.currentTarget.checked @@ -55,7 +54,7 @@ export default function SelectScannerFormSection(props: SelectScannerFormSection } const getCheckboxState = (value: ScannerConfigSpec['value']) => { - return value === Scanners.AQUA_TRIVY + return value === Scanners.AQUA_TRIVY && hasRequiredProjectConfig // return values.scanners?.some(each => each.name === value) || false } @@ -81,6 +80,17 @@ export default function SelectScannerFormSection(props: SelectScannerFormSection ))} + {!hasRequiredProjectConfig && ( + + + + + )} ) } diff --git a/web/src/ar/pages/repository-details/components/FormContent/__tests__/RepositoryConfigurationFormContent.test.tsx b/web/src/ar/pages/repository-details/components/FormContent/__tests__/RepositoryConfigurationFormContent.test.tsx index ffe2a3f9c..bc1af1cc6 100644 --- a/web/src/ar/pages/repository-details/components/FormContent/__tests__/RepositoryConfigurationFormContent.test.tsx +++ b/web/src/ar/pages/repository-details/components/FormContent/__tests__/RepositoryConfigurationFormContent.test.tsx @@ -41,6 +41,15 @@ jest.mock('@harnessio/react-har-service-client', () => ({ })) })) +jest.mock('@harnessio/react-ng-manager-client', () => ({ + useGetOrgScopedProjectQuery: jest.fn().mockImplementation(() => ({ + isFetching: false, + refetch: jest.fn(), + error: false, + data: { content: { data: {} } } + })) +})) + describe('Verify repository configuration form content', () => { beforeEach(() => { jest.clearAllMocks() diff --git a/web/src/ar/pages/repository-details/components/FormContent/__tests__/SelectContainerScannersFormSection.test.tsx b/web/src/ar/pages/repository-details/components/FormContent/__tests__/SelectContainerScannersFormSection.test.tsx index 6b25b4b65..aaec35b35 100644 --- a/web/src/ar/pages/repository-details/components/FormContent/__tests__/SelectContainerScannersFormSection.test.tsx +++ b/web/src/ar/pages/repository-details/components/FormContent/__tests__/SelectContainerScannersFormSection.test.tsx @@ -28,6 +28,15 @@ import SelectContainerScannersFormSection from '../SelectContainerScannersFormSe import '../../../RepositoryFactory' +jest.mock('@harnessio/react-ng-manager-client', () => ({ + useGetOrgScopedProjectQuery: jest.fn().mockImplementation(() => ({ + isFetching: false, + refetch: jest.fn(), + error: false, + data: { content: { data: {} } } + })) +})) + describe('verify SelectContainerScannersFormSection', () => { beforeEach(() => { jest.clearAllMocks() diff --git a/web/src/ar/pages/repository-details/hooks/useCheckRequiredConfigForScan/useCheckRequiredConfigForScan.tsx b/web/src/ar/pages/repository-details/hooks/useCheckRequiredConfigForScan/useCheckRequiredConfigForScan.tsx new file mode 100644 index 000000000..597031d6e --- /dev/null +++ b/web/src/ar/pages/repository-details/hooks/useCheckRequiredConfigForScan/useCheckRequiredConfigForScan.tsx @@ -0,0 +1,54 @@ +/* + * Copyright 2024 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useGetOrgScopedProjectQuery } from '@harnessio/react-ng-manager-client' + +import { useAppStore, useLicenseStore } from '@ar/hooks' +import { LICENSE_STATE_VALUES } from '@ar/common/LicenseTypes' +import { DEFAULT_ORG, DEFAULT_PROJECT } from '@ar/constants' + +export default function useCheckRequiredConfigForScan(): { + hasRequiredLicense: boolean + hasRequiredProjectConfig: boolean + hasRequiredConfig: boolean + orgIdentifier: string +} { + const { scope } = useAppStore() + const { orgIdentifier, projectIdentifier } = scope + const { SSCA_LICENSE_STATE, STO_LICENSE_STATE } = useLicenseStore() + const hasRequiredLicense = + SSCA_LICENSE_STATE === LICENSE_STATE_VALUES.ACTIVE && STO_LICENSE_STATE === LICENSE_STATE_VALUES.ACTIVE + + const { isFetching, error } = useGetOrgScopedProjectQuery( + { + org: orgIdentifier ?? DEFAULT_ORG, + project: projectIdentifier ?? DEFAULT_PROJECT + }, + { + enabled: !projectIdentifier + } + ) + const hasRequiredProjectConfig = !isFetching && !error + + const hasRequiredConfig = hasRequiredLicense && hasRequiredProjectConfig + + return { + hasRequiredLicense, + hasRequiredProjectConfig, + hasRequiredConfig, + orgIdentifier: orgIdentifier ?? DEFAULT_ORG + } +} diff --git a/web/src/ar/pages/repository-details/strings/strings.en.yaml b/web/src/ar/pages/repository-details/strings/strings.en.yaml index 6ffcc268c..6f2006877 100644 --- a/web/src/ar/pages/repository-details/strings/strings.en.yaml +++ b/web/src/ar/pages/repository-details/strings/strings.en.yaml @@ -35,6 +35,7 @@ repositoryForm: containerScannerSelect: cardTitle: Built-in Container Scanners cardSubTitle: Container scanners detect vulnerabilities in your container images. Select scanner(s) below to add them in your pipeline. + scannerNoteForRequiredConfiguration: To enable scanning for account level or org level registries, please create a project with id set to default_project in your {{orgName}} org. tabs: configuration: Configuration packages: '{{ $.artifactList.pageHeading }}' diff --git a/web/src/ar/pages/version-details/DockerVersion/__tests__/DockerVersionHeader.test.tsx b/web/src/ar/pages/version-details/DockerVersion/__tests__/DockerVersionHeader.test.tsx index 85872c60b..01f00208b 100644 --- a/web/src/ar/pages/version-details/DockerVersion/__tests__/DockerVersionHeader.test.tsx +++ b/web/src/ar/pages/version-details/DockerVersion/__tests__/DockerVersionHeader.test.tsx @@ -282,25 +282,25 @@ describe('Verify DockerVersionHeader component render', () => { const overviewTab = container.querySelector('div[data-tab-id=overview]') await userEvent.click(overviewTab!) expect(mockHistoryPush).toHaveBeenLastCalledWith( - '/registries/undefined/artifacts/undefined/versions/undefined/overview?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' + '/registries/artifacts/versions/overview?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' ) const artifactDetailsTab = container.querySelector('div[data-tab-id=artifact_details]') await userEvent.click(artifactDetailsTab!) expect(mockHistoryPush).toHaveBeenLastCalledWith( - '/registries/undefined/artifacts/undefined/versions/undefined/artifact_details?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' + '/registries/artifacts/versions/artifact_details?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' ) const sscaTab = container.querySelector('div[data-tab-id=supply_chain]') await userEvent.click(sscaTab!) expect(mockHistoryPush).toHaveBeenLastCalledWith( - '/registries/undefined/artifacts/undefined/versions/undefined/artifact-sources/67a5dccf6d75916b0c3ea1b5/artifacts/67a5dccf6d75916b0c3ea1b6/supply_chain?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' + '/registries/undefined/artifacts/undefined/versions/undefined/orgs/default/projects/default_project/artifact-sources/67a5dccf6d75916b0c3ea1b5/artifacts/67a5dccf6d75916b0c3ea1b6/supply_chain?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' ) const stoTab = container.querySelector('div[data-tab-id=security_tests]') await userEvent.click(stoTab!) expect(mockHistoryPush).toHaveBeenLastCalledWith( - '/registries/undefined/artifacts/undefined/versions/undefined/pipelines/HARNESS_ARTIFACT_SCAN_PIPELINE/executions/Tbi7s6nETjmOMKU3Qrnm7A/security_tests?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' + '/registries/undefined/artifacts/undefined/versions/undefined/orgs/default/projects/default_project/pipelines/HARNESS_ARTIFACT_SCAN_PIPELINE/executions/Tbi7s6nETjmOMKU3Qrnm7A/security_tests?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' ) }) @@ -323,25 +323,25 @@ describe('Verify DockerVersionHeader component render', () => { const overviewTab = container.querySelector('div[data-tab-id=overview]') await userEvent.click(overviewTab!) expect(mockHistoryPush).toHaveBeenLastCalledWith( - '/registries/undefined/artifacts/undefined/versions/undefined/overview?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' + '/registries/artifacts/versions/overview?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' ) const artifactDetailsTab = container.querySelector('div[data-tab-id=artifact_details]') await userEvent.click(artifactDetailsTab!) expect(mockHistoryPush).toHaveBeenLastCalledWith( - '/registries/undefined/artifacts/undefined/versions/undefined/artifact_details?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' + '/registries/artifacts/versions/artifact_details?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' ) const sscaTab = container.querySelector('div[data-tab-id=supply_chain]') await userEvent.click(sscaTab!) expect(mockHistoryPush).toHaveBeenLastCalledWith( - '/registries/undefined/artifacts/undefined/versions/undefined/supply_chain?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' + '/registries/undefined/artifacts/undefined/versions/undefined/orgs/default/projects/default_project/supply_chain?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' ) const stoTab = container.querySelector('div[data-tab-id=security_tests]') await userEvent.click(stoTab!) expect(mockHistoryPush).toHaveBeenLastCalledWith( - '/registries/undefined/artifacts/undefined/versions/undefined/security_tests?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' + '/registries/undefined/artifacts/undefined/versions/undefined/orgs/default/projects/default_project/security_tests?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' ) }) }) diff --git a/web/src/ar/pages/version-details/DockerVersion/__tests__/DockerVersionOverviewPage.test.tsx b/web/src/ar/pages/version-details/DockerVersion/__tests__/DockerVersionOverviewPage.test.tsx index 12269c10b..808582499 100644 --- a/web/src/ar/pages/version-details/DockerVersion/__tests__/DockerVersionOverviewPage.test.tsx +++ b/web/src/ar/pages/version-details/DockerVersion/__tests__/DockerVersionOverviewPage.test.tsx @@ -145,7 +145,7 @@ describe('Verify docker version overview page', () => { await userEvent.click(sscaCard) await waitFor(() => { expect(mockHistoryPush).toHaveBeenCalledWith( - '/registries/1/artifacts/1/versions/1/artifact-sources/67a5dccf6d75916b0c3ea1b5/artifacts/67a5dccf6d75916b0c3ea1b6/supply_chain?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' + '/registries/1/artifacts/1/versions/1/orgs/default/projects/default_project/artifact-sources/67a5dccf6d75916b0c3ea1b5/artifacts/67a5dccf6d75916b0c3ea1b6/supply_chain?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' ) }) @@ -159,7 +159,7 @@ describe('Verify docker version overview page', () => { await userEvent.click(securityTestsCard) await waitFor(() => { expect(mockHistoryPush).toHaveBeenCalledWith( - '/registries/1/artifacts/1/versions/1/pipelines/HARNESS_ARTIFACT_SCAN_PIPELINE/executions/Tbi7s6nETjmOMKU3Qrnm7A/security_tests?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' + '/registries/1/artifacts/1/versions/1/orgs/default/projects/default_project/pipelines/HARNESS_ARTIFACT_SCAN_PIPELINE/executions/Tbi7s6nETjmOMKU3Qrnm7A/security_tests?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' ) }) }) diff --git a/web/src/ar/pages/version-details/components/OverviewCards/OverviewCards.tsx b/web/src/ar/pages/version-details/components/OverviewCards/OverviewCards.tsx index e5cca6f78..e92f6d8ab 100644 --- a/web/src/ar/pages/version-details/components/OverviewCards/OverviewCards.tsx +++ b/web/src/ar/pages/version-details/components/OverviewCards/OverviewCards.tsx @@ -23,8 +23,9 @@ import { useGetDockerArtifactIntegrationDetailsQuery } from '@harnessio/react-ha import { encodeRef } from '@ar/hooks/useGetSpaceRef' import { useStrings } from '@ar/frameworks/strings' +import { DEFAULT_ORG, DEFAULT_PROJECT } from '@ar/constants' import type { VersionDetailsPathParams } from '@ar/routes/types' -import { useDecodedParams, useGetSpaceRef, useRoutes } from '@ar/hooks' +import { useAppStore, useDecodedParams, useGetSpaceRef, useRoutes } from '@ar/hooks' import DeploymentsCard from '@ar/pages/version-details/components/DeploymentsCard/DeploymentsCard' import SecurityTestsCard from '@ar/pages/version-details/components/SecurityTestsCard/SecurityTestsCard' import SupplyChainCard from '@ar/pages/version-details/components/SupplyChainCard/SupplyChainCard' @@ -40,6 +41,8 @@ interface RedirectToTabOptions { artifactId?: string executionIdentifier?: string pipelineIdentifier?: string + orgIdentifier?: string + projectIdentifier?: string } interface VersionOverviewCardsProps { @@ -51,6 +54,8 @@ export default function VersionOverviewCards(props: VersionOverviewCardsProps) { const { digest = '', cards = [] } = props const { getString } = useStrings() const routes = useRoutes() + const { scope } = useAppStore() + const { orgIdentifier, projectIdentifier } = scope const pathParams = useDecodedParams() const history = useHistory() const spaceRef = useGetSpaceRef() @@ -112,7 +117,9 @@ export default function VersionOverviewCards(props: VersionOverviewCardsProps) { onClick={() => { handleRedirectToTab(VersionDetailsTab.SUPPLY_CHAIN, { sourceId: responseData.sbomDetails?.artifactSourceId, - artifactId: responseData.sbomDetails?.artifactId + artifactId: responseData.sbomDetails?.artifactId, + orgIdentifier: !orgIdentifier ? DEFAULT_ORG : undefined, + projectIdentifier: !projectIdentifier ? DEFAULT_PROJECT : undefined }) }} orchestrationId={defaultTo(responseData.sbomDetails?.orchestrationId, '')} @@ -129,7 +136,9 @@ export default function VersionOverviewCards(props: VersionOverviewCardsProps) { onClick={() => { handleRedirectToTab(VersionDetailsTab.SECURITY_TESTS, { executionIdentifier: responseData?.stoDetails?.executionId, - pipelineIdentifier: responseData?.stoDetails?.pipelineId + pipelineIdentifier: responseData?.stoDetails?.pipelineId, + orgIdentifier: !orgIdentifier ? DEFAULT_ORG : undefined, + projectIdentifier: !projectIdentifier ? DEFAULT_PROJECT : undefined }) }} totalCount={defaultTo(responseData.stoDetails?.total, 0)} diff --git a/web/src/ar/pages/version-details/components/VersionDetailsTabs/VersionDetailsTabs.tsx b/web/src/ar/pages/version-details/components/VersionDetailsTabs/VersionDetailsTabs.tsx index 4ad0120ee..600122b7f 100644 --- a/web/src/ar/pages/version-details/components/VersionDetailsTabs/VersionDetailsTabs.tsx +++ b/web/src/ar/pages/version-details/components/VersionDetailsTabs/VersionDetailsTabs.tsx @@ -20,7 +20,8 @@ import { Container, Tab, Tabs } from '@harnessio/uicore' import { useStrings } from '@ar/frameworks/strings' import { useQueryParams } from '@ar/__mocks__/hooks' -import { useDecodedParams, useRoutes } from '@ar/hooks' +import { DEFAULT_ORG, DEFAULT_PROJECT } from '@ar/constants' +import { useAppStore, useDecodedParams, useRoutes } from '@ar/hooks' import type { RepositoryPackageType } from '@ar/common/types' import type { VersionDetailsPathParams } from '@ar/routes/types' import versionFactory from '@ar/frameworks/Version/VersionFactory' @@ -30,7 +31,9 @@ import { VersionProviderContext } from '@ar/pages/version-details/context/Versio import { versionDetailsPathParams, versionDetailsTabPathParams, + versionDetailsTabWithOrgAndProjectPathParams, versionDetailsTabWithPipelineDetailsPathParams, + versionDetailsTabWithProjectPathParams, versionDetailsTabWithSSCADetailsPathParams } from '@ar/routes/RouteDestinations' @@ -42,12 +45,14 @@ export default function VersionDetailsTabs(): JSX.Element { const [tab, setTab] = useState('') const routes = useRoutes() + const { scope } = useAppStore() const history = useHistory() const { getString } = useStrings() const routeDefinitions = useRoutes(true) const { data } = useContext(VersionProviderContext) const pathParams = useDecodedParams() const { digest } = useQueryParams() + const { orgIdentifier, projectIdentifier } = scope const tabList = useMemo(() => { const versionType = versionFactory?.getVersionType(data?.packageType) @@ -65,7 +70,9 @@ export default function VersionDetailsTabs(): JSX.Element { ...pathParams, versionTab: nextTab, sourceId: data?.sscaArtifactSourceId, - artifactId: data?.sscaArtifactId + artifactId: data?.sscaArtifactId, + orgIdentifier: !orgIdentifier ? DEFAULT_ORG : undefined, + projectIdentifier: !projectIdentifier ? DEFAULT_PROJECT : undefined }) break case VersionDetailsTab.SECURITY_TESTS: @@ -73,11 +80,18 @@ export default function VersionDetailsTabs(): JSX.Element { ...pathParams, versionTab: nextTab, executionIdentifier: data?.stoExecutionId, - pipelineIdentifier: data?.stoPipelineId + pipelineIdentifier: data?.stoPipelineId, + orgIdentifier: !orgIdentifier ? DEFAULT_ORG : undefined, + projectIdentifier: !projectIdentifier ? DEFAULT_PROJECT : undefined }) break default: - newRoute = routes.toARVersionDetailsTab({ ...pathParams, versionTab: nextTab }) + newRoute = routes.toARVersionDetailsTab({ + versionIdentifier: pathParams.versionIdentifier, + artifactIdentifier: pathParams.artifactIdentifier, + repositoryIdentifier: pathParams.repositoryIdentifier, + versionTab: nextTab + }) break } if (digest) { @@ -106,8 +120,34 @@ export default function VersionDetailsTabs(): JSX.Element { exact path={[ routeDefinitions.toARVersionDetailsTab({ ...versionDetailsTabPathParams }), + // with project and org data + routeDefinitions.toARVersionDetailsTab({ + ...versionDetailsTabWithOrgAndProjectPathParams + }), + // ssca with pipeline data routeDefinitions.toARVersionDetailsTab({ ...versionDetailsTabWithSSCADetailsPathParams }), - routeDefinitions.toARVersionDetailsTab({ ...versionDetailsTabWithPipelineDetailsPathParams }) + // ssca with project and pipeline data + routeDefinitions.toARVersionDetailsTab({ + ...versionDetailsTabWithSSCADetailsPathParams, + ...versionDetailsTabWithProjectPathParams + }), + // ssca with org, project and pipeline data + routeDefinitions.toARVersionDetailsTab({ + ...versionDetailsTabWithSSCADetailsPathParams, + ...versionDetailsTabWithOrgAndProjectPathParams + }), + // sto with pipeline data + routeDefinitions.toARVersionDetailsTab({ ...versionDetailsTabWithPipelineDetailsPathParams }), + // sto with project and pipeline data + routeDefinitions.toARVersionDetailsTab({ + ...versionDetailsTabWithPipelineDetailsPathParams, + ...versionDetailsTabWithProjectPathParams + }), + // sto with org, project and pipeline data + routeDefinitions.toARVersionDetailsTab({ + ...versionDetailsTabWithPipelineDetailsPathParams, + ...versionDetailsTabWithOrgAndProjectPathParams + }) ]}> `/registries/${params?.repositoryIdentifier}/artifacts/${params?.artifactIdentifier}/versions/${params?.versionIdentifier}`, toARVersionDetailsTab: params => { + let route = `/registries/${params?.repositoryIdentifier}/artifacts/${params?.artifactIdentifier}/versions/${params?.versionIdentifier}` + if (params.orgIdentifier) route += `/orgs/${params.orgIdentifier}` + if (params.projectIdentifier) route += `/projects/${params.projectIdentifier}` if (params.sourceId && params.artifactId) { - return `/registries/${params?.repositoryIdentifier}/artifacts/${params?.artifactIdentifier}/versions/${params?.versionIdentifier}/artifact-sources/${params.sourceId}/artifacts/${params.artifactId}/${params.versionTab}` + route += `/artifact-sources/${params.sourceId}/artifacts/${params.artifactId}` } if (params.pipelineIdentifier && params.executionIdentifier) { - return `/registries/${params?.repositoryIdentifier}/artifacts/${params?.artifactIdentifier}/versions/${params?.versionIdentifier}/pipelines/${params.pipelineIdentifier}/executions/${params.executionIdentifier}/${params.versionTab}` + route += `/pipelines/${params.pipelineIdentifier}/executions/${params.executionIdentifier}` } - return `/registries/${params?.repositoryIdentifier}/artifacts/${params?.artifactIdentifier}/versions/${params?.versionIdentifier}/${params.versionTab}` + route += `/${params.versionTab}` + return route }, toARRepositoryWebhookDetails: params => `/registries/${params?.repositoryIdentifier}/webhooks/${params?.webhookIdentifier}`, diff --git a/web/src/ar/routes/RouteDestinations.tsx b/web/src/ar/routes/RouteDestinations.tsx index e8565a5da..4f2439110 100644 --- a/web/src/ar/routes/RouteDestinations.tsx +++ b/web/src/ar/routes/RouteDestinations.tsx @@ -66,6 +66,17 @@ export const versionDetailsTabPathParams: VersionDetailsTabPathParams = { versionTab: ':versionTab' } +export const versionDetailsTabWithProjectPathParams: VersionDetailsTabPathParams = { + ...versionDetailsTabPathParams, + projectIdentifier: ':projectIdentifier' +} + +export const versionDetailsTabWithOrgAndProjectPathParams: VersionDetailsTabPathParams = { + ...versionDetailsTabPathParams, + orgIdentifier: ':orgIdentifier', + projectIdentifier: ':projectIdentifier' +} + export const versionDetailsTabWithPipelineDetailsPathParams: VersionDetailsTabPathParams = { ...versionDetailsTabPathParams, pipelineIdentifier: ':pipelineIdentifier', diff --git a/web/src/ar/routes/types.ts b/web/src/ar/routes/types.ts index 258af856a..04288099c 100644 --- a/web/src/ar/routes/types.ts +++ b/web/src/ar/routes/types.ts @@ -41,6 +41,8 @@ export interface VersionDetailsTabPathParams extends VersionDetailsPathParams { executionIdentifier?: string artifactId?: string sourceId?: string + orgIdentifier?: string + projectIdentifier?: string } export interface RedirectPageQueryParams { diff --git a/web/src/ar/strings/types.ts b/web/src/ar/strings/types.ts index 76f65f2d7..e97b19fa8 100644 --- a/web/src/ar/strings/types.ts +++ b/web/src/ar/strings/types.ts @@ -87,6 +87,7 @@ export interface StringsMap { 'repositoryDetails.repositoryForm.repositoryUpdated': string 'repositoryDetails.repositoryForm.securityScan.containerScannerSelect.cardSubTitle': string 'repositoryDetails.repositoryForm.securityScan.containerScannerSelect.cardTitle': string + 'repositoryDetails.repositoryForm.securityScan.containerScannerSelect.scannerNoteForRequiredConfiguration': string 'repositoryDetails.repositoryForm.securityScan.title': string 'repositoryDetails.repositoryForm.selectRepoType': string 'repositoryDetails.repositoryForm.title': string diff --git a/web/yarn.lock b/web/yarn.lock index 98113d5b1..81da60dda 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -1952,6 +1952,11 @@ dependencies: "@harnessio/oats-cli" "^3.0.0" +"@harnessio/react-ng-manager-client@^1.40.0": + version "1.40.0" + resolved "https://registry.yarnpkg.com/@harnessio/react-ng-manager-client/-/react-ng-manager-client-1.40.0.tgz#e874e9afe1b2358d85364d5e23aabe9f6157bae8" + integrity sha512-PETKrA+4TmFs3e+Ok99zPi0ngvGwVJqX5HKczxTWtaDrfXiu/ktZYX5pDN9GBBibShadT0ONbRe4xN6ILFPIuA== + "@harnessio/react-ssca-manager-client@^0.65.0": version "0.65.0" resolved "https://registry.yarnpkg.com/@harnessio/react-ssca-manager-client/-/react-ssca-manager-client-0.65.0.tgz#8088869e282c5268bf1fefb9715652e0fc1a8940"