feat: [AH-881]: Implement webhook listing page (#3262)

* feat: [AH-881]: show ALL when no triggers
* feat: [AH-881]: new API changes integration
* feat: [AH-881]: update status column mapping
* feat: [AH-881]: remove mock data
* feat: [AH-881]: Implement webhook listing page
This commit is contained in:
Shivanand Sonnad 2025-01-17 05:46:36 +00:00 committed by Harness
parent 19e099a2c1
commit 6643c377ca
18 changed files with 684 additions and 6 deletions

View File

@ -51,7 +51,7 @@
"@codemirror/view": "^6.9.6",
"@harnessio/design-system": "^2.1.1",
"@harnessio/icons": "^2.1.9",
"@harnessio/react-har-service-client": "^0.4.0",
"@harnessio/react-har-service-client": "^0.6.0",
"@harnessio/react-ssca-manager-client": "^0.65.0",
"@harnessio/uicore": "^4.1.2",
"@tanstack/react-query": "4.20.4",

View File

@ -27,6 +27,7 @@ import repositoryList from '@ar/pages/repository-list/strings/strings.en.yaml'
import upstreamProxyDetails from '@ar/pages/upstream-proxy-details/strings/strings.en.yaml'
import versionDetails from '@ar/pages/version-details/strings/strings.en.yaml'
import versionList from '@ar/pages/version-list/strings/strings.en.yaml'
import webhookList from '@ar/pages/webhook-list/strings/strings.en.yaml'
export default function languageLoader() {
return {
@ -37,6 +38,7 @@ export default function languageLoader() {
repositoryList,
upstreamProxyDetails,
versionDetails,
versionList
versionList,
webhookList
}
}

View File

@ -26,6 +26,7 @@ import type { RepositoryConfigType, RepositoryPackageType } from '@ar/common/typ
import RepositoryConfigurationFormWidget from '@ar/frameworks/RepositoryStep/RepositoryConfigurationFormWidget'
import { RepositoryDetailsTab } from './constants'
import WebhookListPage from '../webhook-list/WebhookListPage'
import { RepositoryProviderContext } from './context/RepositoryProvider'
import RegistryArtifactListPage from '../artifact-list/RegistryArtifactListPage'
@ -58,6 +59,8 @@ export default function RepositoryDetailsTabPage(props: RepositoryDetailsTabPage
readonly={isReadonly}
/>
)
case RepositoryDetailsTab.WEBHOOKS:
return <WebhookListPage />
default:
return <Text intent="warning">{getString('stepNotFound')}</Text>
}

View File

@ -0,0 +1,20 @@
/*
* 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.
*/
.pageBody {
--page-header-height: 253px;
background-color: var(--primary-bg) !important;
}

View File

@ -0,0 +1,19 @@
/*
* Copyright 2023 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.
*/
/* eslint-disable */
// This is an auto-generated file
export declare const pageBody: string

View File

@ -0,0 +1,151 @@
/*
* 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 React, { useMemo, useRef } from 'react'
import { flushSync } from 'react-dom'
import { useParams } from 'react-router-dom'
import { Expander } from '@blueprintjs/core'
import { useListWebhooksQuery } from '@harnessio/react-har-service-client'
import { Button, ButtonVariation, ExpandingSearchInput, ExpandingSearchInputHandle, Page } from '@harnessio/uicore'
import { DEFAULT_PAGE_INDEX, PreferenceScope } from '@ar/constants'
import { useStrings } from '@ar/frameworks/strings'
import type { RepositoryDetailsTabPathParams } from '@ar/routes/types'
import { useAppStore, useGetSpaceRef, useParentHooks } from '@ar/hooks'
import { PermissionIdentifier, ResourceType } from '@ar/common/permissionTypes'
import WebhookListTable from './components/WebhookListTable/WebhookListTable'
import CreateWebhookButton from './components/CreateWebhookButton/CreateWebhookButton'
import { useListWebhooksQueryParamOptions, type WebhookListPageQueryParams } from './utils'
import css from './WebhookListPage.module.scss'
export default function WebhookListPage() {
const { getString } = useStrings()
const registryRef = useGetSpaceRef()
const searchRef = useRef({} as ExpandingSearchInputHandle)
const { scope } = useAppStore()
const { repositoryIdentifier } = useParams<RepositoryDetailsTabPathParams>()
const { useQueryParams, useUpdateQueryParams, usePermission, usePreferenceStore } = useParentHooks()
const { updateQueryParams } = useUpdateQueryParams<Partial<WebhookListPageQueryParams>>()
const queryParamOptions = useListWebhooksQueryParamOptions()
const queryParams = useQueryParams<WebhookListPageQueryParams>(queryParamOptions)
const { page, size, searchTerm } = queryParams
const { accountId, orgIdentifier, projectIdentifier } = scope
const { preference: sortingPreference, setPreference: setSortingPreference } = usePreferenceStore<string | undefined>(
PreferenceScope.USER,
'ArtifactWebhooksSortingPreference'
)
const sort = useMemo(
() => (sortingPreference ? JSON.parse(sortingPreference) : queryParams.sort),
[queryParams.sort, sortingPreference]
)
const [sortField, sortOrder] = sort || []
const [isEdit] = usePermission(
{
resourceScope: {
accountIdentifier: accountId,
orgIdentifier,
projectIdentifier
},
resource: {
resourceType: ResourceType.ARTIFACT_REGISTRY,
resourceIdentifier: repositoryIdentifier
},
permissions: [PermissionIdentifier.EDIT_ARTIFACT_REGISTRY]
},
[accountId, projectIdentifier, orgIdentifier, repositoryIdentifier]
)
const { isFetching, data, error, refetch } = useListWebhooksQuery({
registry_ref: registryRef,
queryParams: {
page,
size,
search_term: searchTerm,
sort_field: sortField,
sort_order: sortOrder
}
})
const handleClearFilters = (): void => {
flushSync(searchRef.current.clear)
updateQueryParams({
page: undefined,
size: undefined,
searchTerm: undefined
})
}
const response = data?.content.data
const hasFilter = !!searchTerm
return (
<>
<Page.SubHeader>
<CreateWebhookButton />
<Expander />
<ExpandingSearchInput
alwaysExpanded
width={200}
ref={searchRef}
placeholder={getString('search')}
defaultValue={searchTerm ?? ''}
onChange={text => {
updateQueryParams({ searchTerm: text || undefined, page: DEFAULT_PAGE_INDEX })
}}
/>
</Page.SubHeader>
<Page.Body
className={css.pageBody}
loading={isFetching}
error={error?.message || error}
retryOnError={() => refetch()}
noData={{
when: () => !response?.itemCount,
icon: 'code-webhook',
noIconColor: true,
messageTitle: hasFilter ? getString('noResultsFound') : getString('webhookList.table.noWebhooksTitle'),
button: hasFilter ? (
<Button text={getString('clearFilters')} variation={ButtonVariation.LINK} onClick={handleClearFilters} />
) : (
<CreateWebhookButton />
)
}}>
{response && (
<WebhookListTable
data={response}
refetchList={refetch}
gotoPage={pageNumber => updateQueryParams({ page: pageNumber })}
onPageSizeChange={newSize => updateQueryParams({ size: newSize, page: DEFAULT_PAGE_INDEX })}
sortBy={sort}
setSortBy={sortArray => {
setSortingPreference(JSON.stringify(sortArray))
updateQueryParams({ sort: sortArray, page: DEFAULT_PAGE_INDEX })
}}
readonly={!isEdit}
/>
)}
</Page.Body>
</>
)
}

View File

@ -0,0 +1,28 @@
/*
* 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 React from 'react'
import { Button, ButtonVariation } from '@harnessio/uicore'
import { useStrings } from '@ar/frameworks/strings'
export default function CreateWebhookButton() {
const { getString } = useStrings()
return (
<Button variation={ButtonVariation.PRIMARY} icon="plus" iconProps={{ size: 10 }}>
{getString('webhookList.newWebhook')}
</Button>
)
}

View File

@ -0,0 +1,38 @@
/*
* 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.
*/
.table {
--typography-size: 12px;
--typography-weight: 400;
--line-height: 20px;
padding: var(--spacing-large);
[role='cell'],
[role='columnheader'] {
width: auto !important;
padding-right: var(--spacing-small);
}
div[class*='TableV2--cells'],
div[class*='TableV2--header'] {
display: grid !important;
grid-template-columns: minmax(var(--har-table-name-column-min-width), 1fr) 1fr 50px 50px;
}
}
.tableRow {
position: relative;
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2023 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.
*/
/* eslint-disable */
// This is an auto-generated file
export declare const table: string
export declare const tableRow: string

View File

@ -0,0 +1,128 @@
/*
* 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 React from 'react'
import classNames from 'classnames'
import type { Column } from 'react-table'
import { useHistory } from 'react-router-dom'
import { type PaginationProps, TableV2 } from '@harnessio/uicore'
import type { ListWebhooks, Webhook } from '@harnessio/react-har-service-client'
import { useStrings } from '@ar/frameworks/strings'
import { useParentHooks, useRoutes } from '@ar/hooks'
import { RepositoryDetailsTab } from '@ar/pages/repository-details/constants'
import {
WebhookActionsCell,
WebhookListColumnActions,
WebhookNameCell,
WebhookStatusCell,
WebhookTriggerCell
} from './WebhookListTableCells'
import css from './WebhookListTable.module.scss'
interface WebhookListSortBy {
sort: 'name'
}
export interface WebhookListTableProps extends WebhookListColumnActions {
data: ListWebhooks
gotoPage: (pageNumber: number) => void
onPageSizeChange?: PaginationProps['onPageSizeChange']
setSortBy: (sortBy: string[]) => void
sortBy: string[]
minimal?: boolean
}
export default function WebhookListTable(props: WebhookListTableProps): JSX.Element {
const { data, gotoPage, onPageSizeChange, readonly, sortBy, setSortBy } = props
const { useDefaultPaginationProps } = useParentHooks()
const { getString } = useStrings()
const history = useHistory()
const routes = useRoutes()
const [currentSort, currentOrder] = sortBy || []
const { webhooks, itemCount = 0, pageCount = 0, pageIndex, pageSize = 0 } = data
const paginationProps = useDefaultPaginationProps({
itemCount,
pageSize,
pageCount,
pageIndex,
gotoPage,
onPageSizeChange
})
const columns: Column<Webhook>[] = React.useMemo(() => {
const getServerSortProps = (id: string) => {
return {
enableServerSort: true,
isServerSorted: currentSort === id,
isServerSortedDesc: currentOrder === 'DESC',
getSortedColumn: ({ sort }: WebhookListSortBy) => {
setSortBy([sort, currentOrder === 'DESC' ? 'ASC' : 'DESC'])
}
}
}
return [
{
Header: getString('webhookList.table.columns.name'),
accessor: 'name',
Cell: WebhookNameCell,
serverSortProps: getServerSortProps('name'),
readonly: readonly
},
{
Header: getString('webhookList.table.columns.trigger'),
accessor: 'triggers',
Cell: WebhookTriggerCell,
disableSortBy: true
},
{
Header: '',
accessor: 'latestExecutionResult',
Cell: WebhookStatusCell,
disableSortBy: true
},
{
Header: '',
accessor: 'menu',
Cell: WebhookActionsCell,
disableSortBy: true
}
].filter(Boolean) as unknown as Column<Webhook>[]
}, [currentOrder, currentSort, getString, readonly])
return (
<TableV2
className={classNames(css.table)}
columns={columns}
data={webhooks}
pagination={paginationProps}
sortable
getRowClassName={() => css.tableRow}
onRowClick={rowDetails => {
// TODO: navigate to webhook details page
history.push(
routes.toARRepositoryDetailsTab({
repositoryIdentifier: rowDetails.identifier,
tab: RepositoryDetailsTab.WEBHOOKS
})
)
}}
/>
)
}

View File

@ -0,0 +1,123 @@
/*
* 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 React, { useState } from 'react'
import { Icon, IconProps } from '@harnessio/icons'
import { Color, FontVariation } from '@harnessio/design-system'
import { Container, getErrorInfoFromErrorObject, Layout, Text, Toggle, useToaster } from '@harnessio/uicore'
import { updateWebhook, type Trigger, type Webhook } from '@harnessio/react-har-service-client'
import type { Cell, CellValue, ColumnInstance, Renderer, Row, TableInstance } from 'react-table'
import { useGetSpaceRef } from '@ar/hooks'
import { killEvent } from '@ar/common/utils'
import { useStrings } from '@ar/frameworks/strings'
import ActionButton from '@ar/components/ActionButton/ActionButton'
import { DefaultStatusIconMap, WebhookStatusIconMap, WebhookTriggerLabelMap } from '../../constants'
type CellTypeWithActions<D extends Record<string, any>, V = any> = TableInstance<D> & {
column: ColumnInstance<D>
row: Row<D>
cell: Cell<D, V>
value: CellValue<V>
}
type CellType = Renderer<CellTypeWithActions<Webhook>>
export interface WebhookListColumnActions {
refetchList?: () => void
readonly?: boolean
}
export const WebhookNameCell: Renderer<{
row: Row<Webhook>
column: ColumnInstance<Webhook> & WebhookListColumnActions
}> = ({ row, column }) => {
const { name, enabled, identifier } = row.original
const { readonly } = column
const [isEnabled, setIsEnabled] = useState(enabled)
const registryRef = useGetSpaceRef()
const { showError, clear } = useToaster()
const handleUpdateToggle = async (checked: boolean) => {
setIsEnabled(checked)
try {
await updateWebhook({
registry_ref: registryRef,
webhook_identifier: identifier,
body: {
...row.original,
enabled: checked
}
})
} catch (e) {
clear()
showError(getErrorInfoFromErrorObject(e as Error))
setIsEnabled(enabled)
}
}
return (
<Layout.Horizontal spacing="small" flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
<Icon name="code-webhook" size={24} />
<Container onClick={killEvent}>
<Toggle disabled={readonly} checked={isEnabled} onToggle={handleUpdateToggle} />
</Container>
<Text lineClamp={1} font={{ variation: FontVariation.BODY }} color={Color.PRIMARY_7}>
{name}
</Text>
</Layout.Horizontal>
)
}
export const WebhookTriggerCell: CellType = ({ value }) => {
const { getString } = useStrings()
const getDisplayText = () => {
if (!value || !value.length) return getString('all')
return value
.map((each: Trigger) => {
if (WebhookTriggerLabelMap[each]) return getString(WebhookTriggerLabelMap[each])
return each
})
.join(', ')
}
return (
<Text lineClamp={2} font={{ variation: FontVariation.BODY }} color={Color.GREY_500}>
{getDisplayText()}
</Text>
)
}
export const WebhookStatusCell: CellType = ({ row }) => {
const { original } = row
const { latestExecutionResult } = original
const iconProps: IconProps =
latestExecutionResult && WebhookStatusIconMap[latestExecutionResult]
? WebhookStatusIconMap[latestExecutionResult]
: DefaultStatusIconMap
return <Icon {...iconProps} size={18} />
}
export const WebhookActionsCell: CellType = () => {
const [open, setOpen] = useState(false)
return (
<ActionButton isOpen={open} setOpen={setOpen}>
{/* TODO: Add webhook actions here */}
<>Option 1</>
</ActionButton>
)
}

View File

@ -0,0 +1,35 @@
/*
* 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 { Color } from '@harnessio/design-system'
import type { IconProps } from '@harnessio/icons'
import type { Trigger, WebhookExecResult } from '@harnessio/react-har-service-client'
import type { StringKeys } from '@ar/frameworks/strings'
export const WebhookTriggerLabelMap: Record<Trigger, StringKeys> = {
ARTIFACT_CREATION: 'webhookList.triggers.artifactCreation',
ARTIFACT_DELETION: 'webhookList.triggers.artifactDeletion',
ARTIFACT_MODIFICATION: 'webhookList.triggers.artifactModification'
}
export const WebhookStatusIconMap: Record<WebhookExecResult, IconProps> = {
SUCCESS: { name: 'dot', color: Color.SUCCESS },
RETRIABLE_ERROR: { name: 'dot', color: Color.WARNING },
FATAL_ERROR: { name: 'dot', color: Color.ERROR }
}
export const DefaultStatusIconMap: IconProps = { name: 'dot', color: Color.GREY_500 }

View File

@ -0,0 +1,50 @@
/*
* 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 type { ListWebhooks } from '@harnessio/react-har-service-client'
export const MOCK_WEBHOK_LIST_TABLE: ListWebhooks = {
itemCount: 10,
pageCount: 1,
pageIndex: 0,
pageSize: 10,
webhooks: [
{
name: 'Webhook 1',
identifier: 'webhook-1',
triggers: ['ARTIFACT_CREATION'],
url: 'https://webhook-1.com',
insecure: false,
enabled: true
},
{
name: 'Webhook 2',
identifier: 'webhook-2',
triggers: ['ARTIFACT_CREATION', 'ARTIFACT_DELETION'],
url: 'https://webhook-2.com',
insecure: false,
enabled: true
},
{
name: 'Webhook 3',
identifier: 'webhook-3',
triggers: ['ARTIFACT_CREATION', 'ARTIFACT_DELETION', 'ARTIFACT_MODIFICATION'],
url: 'https://webhook-3.com',
insecure: false,
enabled: true
}
]
}

View File

@ -0,0 +1,10 @@
newWebhook: New Webhook
triggers:
artifactCreation: 'Artifact Creation'
artifactDeletion: 'Artifact Deletion'
artifactModification: 'Artifact Modification'
table:
columns:
name: Webhook
trigger: Event
noWebhooksTitle: There are no webhooks available

View File

@ -0,0 +1,42 @@
/*
* 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 { useMemo } from 'react'
import type { ListWebhooksQueryQueryParams } from '@harnessio/react-har-service-client'
import { useParentHooks } from '@ar/hooks'
import { DEFAULT_PAGE_INDEX, DEFAULT_PAGE_SIZE } from '@ar/constants'
import type { UseQueryParamsOptions } from '@ar/__mocks__/hooks'
export type WebhookListPageQueryParams = ListWebhooksQueryQueryParams & {
searchTerm?: string
sort: string[]
}
export const useListWebhooksQueryParamOptions = (): UseQueryParamsOptions<WebhookListPageQueryParams> => {
const { useQueryParamsOptions } = useParentHooks()
const _options = useQueryParamsOptions(
{
page: DEFAULT_PAGE_INDEX,
size: DEFAULT_PAGE_SIZE,
sort: []
},
{ ignoreEmptyString: false }
)
const options = useMemo(() => ({ ..._options, strictNullHandling: true }), [_options])
return options
}

View File

@ -26,6 +26,7 @@ na: N/A
cancel: Cancel
comingSoon: Coming Soon.
failedToLoadData: Failed to load data. Please try again!
all: All
noResultsFound: No results found
optionalField: '{{name}} (optional)'
description: '{{ $.versionDetails.overview.generalInformation.description }}'

View File

@ -228,6 +228,13 @@ export interface StringsMap {
'versionList.table.columns.size': string
'versionList.table.columns.version': string
'versionList.table.noVersionsTitle': string
'webhookList.newWebhook': string
'webhookList.table.columns.name': string
'webhookList.table.columns.trigger': string
'webhookList.table.noWebhooksTitle': string
'webhookList.triggers.artifactCreation': string
'webhookList.triggers.artifactDeletion': string
'webhookList.triggers.artifactModification': string
'actions.delete': string
'actions.edit': string
'actions.quarantine': string
@ -235,6 +242,7 @@ export interface StringsMap {
'actions.scan': string
'actions.setupClient': string
add: string
all: string
'badges.artifactRegistry': string
'badges.upstreamProxy': string
'breadcrumbs.artifacts': string

View File

@ -1945,10 +1945,10 @@
yargs "^17.6.2"
zod "^3.19.1"
"@harnessio/react-har-service-client@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@harnessio/react-har-service-client/-/react-har-service-client-0.4.0.tgz#ad0deb70502f8ff51c7199d72dd67aa7a13640b4"
integrity sha512-ndVz0Ig0FHHwgDy1oZVQ7I2epFgM0dnYLLAUHOLMzaqvcV6pN8RCuawtygcFi9K2DKMfFQieWTuI9UjVoB8M+w==
"@harnessio/react-har-service-client@^0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@harnessio/react-har-service-client/-/react-har-service-client-0.6.0.tgz#25253fd935fe6a28a5d26d98148ba0ba51d545ef"
integrity sha512-jCCCInwmVoCWrambIop6dK8SvJXHpsq+8MdZNPbguMPjJgqR0d6i6Ms+4lpfBswJ/5TVVEvo/uFJhlAUVtf2fQ==
dependencies:
"@harnessio/oats-cli" "^3.0.0"