Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/entities/entities-routes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@kong/kongponents": "9.32.6",
"@types/lodash.isequal": "^4.5.8",
"axios": "^1.7.7",
"js-yaml": "^4.1.0",
"vite-plugin-monaco-editor": "^1.1.0",
"vue": "^3.5.13",
"vue-router": "^4.4.5"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@
<h2>Konnect Actions Outside</h2>
<div id="kong-ui-app-page-header-action-button" />

<h2>Declarative Config</h2>
<RouteList
:key="declarativeKey"
cache-identifier="declarative"
:config="declarativeKonnectConfig"
/>

<h2>Konnect API</h2>
<RouteList
v-if="permissions"
Expand Down Expand Up @@ -68,15 +75,66 @@
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import type { AxiosError } from 'axios'
import { RouteList } from '../../src'
import type { KonnectRouteListConfig, KongManagerRouteListConfig, EntityRow, CopyEventPayload } from '../../src'
import type { PermissionsActions } from '@entities-shared-sandbox/components/SandboxPermissionsControl.vue'
import SandboxPermissionsControl from '@entities-shared-sandbox/components/SandboxPermissionsControl.vue'
import type { AxiosError } from 'axios'
import { onMounted, reactive, ref, watch } from 'vue'
import type { CopyEventPayload, EntityRow, KongManagerRouteListConfig, KonnectRouteListConfig } from '../../src'
import { RouteList } from '../../src'

const controlPlaneId = import.meta.env.VITE_KONNECT_CONTROL_PLANE_ID || ''

const declarativeKonnectConfig = reactive<KonnectRouteListConfig>({
app: 'konnect',
apiBaseUrl: '/us/kong-api',
controlPlaneId,
createRoute: { name: 'create-route' },
getViewRoute: () => ({ }),
getEditRoute: () => ({ }),
declarative: {
config: {
routes: [],
services: [],
listeners: [],
},
filterSchema: {
name: {
type: 'text',
},
matchPath: {
type: 'text',
},
},
getViewRoute: (name: string) => {
console.log('getViewRoute called with name:', name)
return {}
},
getEditRoute: (name: string) => {
console.log('getEditRoute called with name:', name)
return {}
},
},
})

onMounted(() => {
setTimeout(() => {
declarativeKonnectConfig.declarative.config = {
routes: [
{
name: 'example-route-1',
match: {
path: '/example-1',
},
policies: [],
},
],
services: [],
listeners: [],
}
declarativeKey.value++
}, 1000)
})

const konnectConfig = ref<KonnectRouteListConfig>({
app: 'konnect',
apiBaseUrl: '/us/kong-api', // `/{geo}/kong-api, with leading slash and no trailing slash
Expand Down Expand Up @@ -116,6 +174,7 @@ const kongManagerConfig = ref<KongManagerRouteListConfig>({

// Remount the tables in the sandbox when the permission props change; not needed outside of a sandbox
const key = ref(1)
const declarativeKey = ref(1)
const permissions = ref<PermissionsActions | null>(null)

const isRouteListControlCollapsed = ref<boolean>(true)
Expand Down
158 changes: 116 additions & 42 deletions packages/entities/entities-routes/src/components/RouteList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<EntityFilter
v-model="filterQuery"
:config="filterConfig"
@update:fuzzy-filters="fuzzyFilters = $event"
/>
</template>
<!-- Create action -->
Expand Down Expand Up @@ -189,9 +190,23 @@
{{ formatUnixTimeStamp(rowValue ?? row.created_at) }}
</template>

<!-- Declarative columns -->
<template #matchPath="{ row }">
<span class="route-list-cell-match-path">
{{ row.match.path }}
</span>
</template>

<template #policies="{ row }">
{{ row.policies.length }}
</template>

<!-- Row actions -->
<template #actions="{ row }">
<KClipboardProvider v-slot="{ copyToClipboard }">
<KClipboardProvider
v-if="!props.config.declarative"
v-slot="{ copyToClipboard }"
>
<KDropdownItem
data-testid="action-entity-copy-id"
@click="copyId(row, copyToClipboard)"
Expand All @@ -207,20 +222,29 @@
{{ t('actions.copy_json') }}
</KDropdownItem>
</KClipboardProvider>
<PermissionsWrapper :auth-function="() => canRetrieve(row)">
<PermissionsWrapper
v-if="!props.config.declarative"
:auth-function="() => canRetrieve(row)"
>
<KDropdownItem
data-testid="action-entity-view"
has-divider
:item="getViewDropdownItem(row.id)"
/>
</PermissionsWrapper>
<PermissionsWrapper :auth-function="() => canEdit(row)">
<PermissionsWrapper
v-if="!props.config.declarative"
:auth-function="() => canEdit(row)"
>
<KDropdownItem
data-testid="action-entity-edit"
:item="getEditDropdownItem(row.id)"
/>
</PermissionsWrapper>
<PermissionsWrapper :auth-function="() => canDelete(row)">
<PermissionsWrapper
v-if="!props.config.declarative"
:auth-function="() => canDelete(row)"
>
<KDropdownItem
danger
data-testid="action-entity-delete"
Expand Down Expand Up @@ -248,47 +272,49 @@
</template>

<script setup lang="ts">
import type { PropType } from 'vue'
import { computed, ref, watch, onBeforeMount } from 'vue'
import type { AxiosError } from 'axios'
import { useRouter } from 'vue-router'

import { BadgeMethodAppearances } from '@kong/kongponents'
import type { BadgeMethodAppearance, HeaderTag } from '@kong/kongponents'
import { AddIcon, ForwardIcon, BookIcon } from '@kong/icons'
import type {
BaseTableHeaders,
DeclarativeRoute,
EmptyStateOptions,
ExactMatchFilterConfig,
FilterFields,
FuzzyMatchFilterConfig,
FuzzyMatchFilters,
TableErrorMessage,
} from '@kong-ui-public/entities-shared'
import {
EntityBaseTable,
EntityDeleteModal,
EntityEmptyState,
EntityFilter,
EntityTypes,
FetcherStatus,
EntityEmptyState,
PermissionsWrapper,
TableTags,
useAxios,
useDeclarativeRoutesFetcher,
useDeleteUrlBuilder,
useFetcher,
useTableState,
useDeleteUrlBuilder,
TableTags,
} from '@kong-ui-public/entities-shared'
import { KUI_COLOR_TEXT_DECORATIVE_AQUA, KUI_ICON_SIZE_50 } from '@kong/design-tokens'
import { AddIcon, BookIcon, ForwardIcon } from '@kong/icons'
import type { BadgeMethodAppearance, HeaderTag } from '@kong/kongponents'
import { BadgeMethodAppearances } from '@kong/kongponents'
import type { AxiosError } from 'axios'
import type { PropType } from 'vue'
import { computed, onBeforeMount, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import composables from '../composables'
import endpoints from '../routes-endpoints'
import type {
CopyEventPayload,
EntityRow,
KongManagerRouteListConfig,
KonnectRouteListConfig,
EntityRow,
CopyEventPayload,
} from '../types'
import type {
BaseTableHeaders,
EmptyStateOptions,
ExactMatchFilterConfig,
FilterFields,
FuzzyMatchFilterConfig,
TableErrorMessage,
} from '@kong-ui-public/entities-shared'
import '@kong-ui-public/entities-shared/dist/style.css'

import composables from '../composables'
import endpoints from '../routes-endpoints'
import { KUI_COLOR_TEXT_DECORATIVE_AQUA, KUI_ICON_SIZE_50 } from '@kong/design-tokens'
import '@kong-ui-public/entities-shared/dist/style.css'

const emit = defineEmits<{
(e: 'error', error: AxiosError): void
Expand Down Expand Up @@ -392,18 +418,23 @@ const disableSorting = computed((): boolean => props.config.app !== 'kongManager
const fields: BaseTableHeaders = {
// the Name column is non-hidable
name: { label: t('routes.list.table_headers.name'), searchable: true, sortable: true, hidable: false },
protocols: { label: t('routes.list.table_headers.protocols'), searchable: true },
...!props.hideTraditionalColumns && {
hosts: { label: t('routes.list.table_headers.hosts'), searchable: true },
methods: { label: t('routes.list.table_headers.methods'), searchable: true },
paths: { label: t('routes.list.table_headers.paths'), searchable: true },
},
...props.hasExpressionColumn && {
expression: { label: t('routes.list.table_headers.expression'), tooltip: true },
...props.config.declarative ? {
matchPath: { label: 'Match Path', searchable: true },
policies: { label: 'Policies', searchable: false, sortable: true },
} : {
protocols: { label: t('routes.list.table_headers.protocols'), searchable: true },
...!props.hideTraditionalColumns && {
hosts: { label: t('routes.list.table_headers.hosts'), searchable: true },
methods: { label: t('routes.list.table_headers.methods'), searchable: true },
paths: { label: t('routes.list.table_headers.paths'), searchable: true },
},
...props.hasExpressionColumn && {
expression: { label: t('routes.list.table_headers.expression'), tooltip: true },
},
tags: { label: t('routes.list.table_headers.tags'), sortable: false },
updated_at: { label: t('routes.list.table_headers.updated_at'), sortable: true },
created_at: { label: t('routes.list.table_headers.created_at'), sortable: true },
},
tags: { label: t('routes.list.table_headers.tags'), sortable: false },
updated_at: { label: t('routes.list.table_headers.updated_at'), sortable: true },
created_at: { label: t('routes.list.table_headers.created_at'), sortable: true },
}
const defaultTablePreferences = {
columnVisibility: {
Expand Down Expand Up @@ -433,7 +464,17 @@ const fetcherBaseUrl = computed<string>(() => {

const filterQuery = ref<string>('')
const filterConfig = computed<InstanceType<typeof EntityFilter>['$props']['config']>(() => {
const isExactMatch = (props.config.app === 'konnect' || props.config.isExactMatch)
if (props.config.declarative) {
const { name, matchPath } = fields

return {
isExactMatch: false,
fields: { name, matchPath },
schema: props.config.declarative.filterSchema,
} as FuzzyMatchFilterConfig
}

const isExactMatch = props.config.app === 'konnect' || props.config.isExactMatch

if (isExactMatch) {
return {
Expand All @@ -455,11 +496,34 @@ const filterConfig = computed<InstanceType<typeof EntityFilter>['$props']['confi
} as FuzzyMatchFilterConfig
})

// This is only used by the Declarative PoC
// FIXME: This is not optimal as the filterSchema is passed in from the host app
// This might lead to troublesome type mismatches
const fuzzyFilters = ref<FuzzyMatchFilters<'name' | 'matchPath'>>()

const declarativeFilterFn = (route: DeclarativeRoute): boolean => {
if (!fuzzyFilters.value) {
return true
}

const { name, matchPath } = fuzzyFilters.value
if (name && !route.name.toLowerCase().includes(name.toLowerCase())) {
return false
}
if (matchPath && !route.match.path.toLowerCase().includes(matchPath.toLowerCase())) {
return false
}

return true
}

const {
fetcher,
fetcherState,
fetcherCacheKey,
} = useFetcher(computed(() => ({ ...props.config, cacheIdentifier: props.cacheIdentifier })), fetcherBaseUrl)
} = props.config.declarative
? useDeclarativeRoutesFetcher(() => props.config.declarative?.config, props.cacheIdentifier, declarativeFilterFn)
: useFetcher(computed(() => ({ ...props.config, cacheIdentifier: props.cacheIdentifier })), fetcherBaseUrl)

const getCellAttrs = (params: Record<string, any>): Record<string, any> => {
if (params.headerKey === 'expression') {
Expand Down Expand Up @@ -547,6 +611,12 @@ const rowClick = async (row: EntityRow): Promise<void> => {
return
}

if (props.config.declarative) {
const route = row as unknown as DeclarativeRoute
router.push(props.config.declarative.getViewRoute(route.name))
return
}

router.push(props.config.getViewRoute(row.id as string))
}

Expand Down Expand Up @@ -681,5 +751,9 @@ onBeforeMount(async () => {
.route-list-cell-expression {
font-family: $kui-font-family-code;
}

.route-list-cell-match-path {
font-family: $kui-font-family-code;
}
}
</style>
12 changes: 10 additions & 2 deletions packages/entities/entities-routes/src/types/route-list.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { DeclarativeConfig, FilterSchema, KongManagerBaseTableConfig, KonnectBaseTableConfig } from '@kong-ui-public/entities-shared'
import type { RouteLocationRaw } from 'vue-router'
import type { FilterSchema, KongManagerBaseTableConfig, KonnectBaseTableConfig } from '@kong-ui-public/entities-shared'

export interface BaseRouteListConfig {
/** Current service id if the RouteList in nested in the routes tab on a service detail page */
Expand All @@ -10,10 +10,18 @@ export interface BaseRouteListConfig {
getViewRoute: (id: string) => RouteLocationRaw
/** A function that returns the route for editing a route */
getEditRoute: (id: string) => RouteLocationRaw

declarative?: {
config: DeclarativeConfig
filterSchema?: FilterSchema
getViewRoute: (name: string) => RouteLocationRaw
getEditRoute: (name: string) => RouteLocationRaw
}
}

/** Konnect route list config */
export interface KonnectRouteListConfig extends KonnectBaseTableConfig, BaseRouteListConfig {}
export interface KonnectRouteListConfig extends KonnectBaseTableConfig, BaseRouteListConfig {
}

/** Kong Manager route list config */
export interface KongManagerRouteListConfig extends KongManagerBaseTableConfig, BaseRouteListConfig {
Expand Down
Loading
Loading