From 2e3fb2001b748f934e144ab94e11fe452ab94c54 Mon Sep 17 00:00:00 2001 From: Makito Date: Tue, 21 May 2024 01:29:58 +0800 Subject: [PATCH 1/7] feat(plugins): redesigned RLA form --- packages/core/forms/src/forms/RLAForm.vue | 341 ++++++++++++++++++ packages/core/forms/src/forms/index.ts | 1 + .../fields/advanced/FieldSelectionGroup.vue | 124 +++++-- packages/core/forms/src/index.ts | 8 +- packages/core/forms/src/locales/en.json | 31 ++ .../entities-plugins/docs/plugin-form.md | 6 + .../sandbox/pages/PluginFormPage.vue | 2 + .../src/components/PluginEntityForm.vue | 3 +- .../src/composables/useSchemas.ts | 4 +- .../entities-plugins/src/types/plugin-form.ts | 2 + 10 files changed, 487 insertions(+), 35 deletions(-) create mode 100644 packages/core/forms/src/forms/RLAForm.vue diff --git a/packages/core/forms/src/forms/RLAForm.vue b/packages/core/forms/src/forms/RLAForm.vue new file mode 100644 index 0000000000..1241aef2a1 --- /dev/null +++ b/packages/core/forms/src/forms/RLAForm.vue @@ -0,0 +1,341 @@ + + + + + diff --git a/packages/core/forms/src/forms/index.ts b/packages/core/forms/src/forms/index.ts index d39f6e9dbf..c5612e8404 100644 --- a/packages/core/forms/src/forms/index.ts +++ b/packages/core/forms/src/forms/index.ts @@ -2,3 +2,4 @@ export { default as OIDCForm } from './OIDCForm.vue' export { default as PostFunction } from './PostFunction.vue' export { default as ExitTransformer } from './ExitTransformer.vue' export { default as ACMEForm } from './ACMEForm.vue' +export { default as RLAForm } from './RLAForm.vue' diff --git a/packages/core/forms/src/generator/fields/advanced/FieldSelectionGroup.vue b/packages/core/forms/src/generator/fields/advanced/FieldSelectionGroup.vue index 926b83e0bb..3e83bee124 100644 --- a/packages/core/forms/src/generator/fields/advanced/FieldSelectionGroup.vue +++ b/packages/core/forms/src/generator/fields/advanced/FieldSelectionGroup.vue @@ -1,42 +1,91 @@ @@ -103,21 +152,25 @@ export default { diff --git a/packages/core/forms/src/index.ts b/packages/core/forms/src/index.ts index 6a498718cf..8178bd8aa6 100644 --- a/packages/core/forms/src/index.ts +++ b/packages/core/forms/src/index.ts @@ -12,14 +12,18 @@ export default { export { customFields } from './generator/fields/advanced/exports' export { VueFormGenerator, sharedForms } -export const getSharedFormName = (modelName: string, enabledAcmeCustomTemplate = false): string => { +export interface GetSharedFormNameOptions { + useRLARedesignedForm?: boolean +} + +export const getSharedFormName = (modelName: string, options?: GetSharedFormNameOptions): string => { const mapping:Record = { - ...(enabledAcmeCustomTemplate && { acme: 'ACMEForm' }), 'openid-connect': 'OIDCForm', 'post-function': 'PostFunction', // Pre and Post function plugins are using same component 'pre-function': 'PostFunction', 'exit-transformer': 'ExitTransformer', + ...(options?.useRLARedesignedForm && { 'rate-limiting-advanced': 'RLAForm' }), } return mapping[modelName] diff --git a/packages/core/forms/src/locales/en.json b/packages/core/forms/src/locales/en.json index 837d7d1a02..4d783c053c 100644 --- a/packages/core/forms/src/locales/en.json +++ b/packages/core/forms/src/locales/en.json @@ -61,5 +61,36 @@ "config-functions": { "newElementButtonLabel": "+ Add" } + }, + "rla": { + "view_advanced_fields": "View Advanced Fields", + "request_limits": { + "title": "Request Limits", + "subtitle": "Maximum number of requests sent through your gateway at specific time intervals", + "label": "Limit {index}", + "help": "Set one or more requests-per-window limit and window sizes to apply a limit to (defined in seconds). There must be a matching number of window limits and sizes specified.", + "request_number": "Request number", + "time_interval": "Time interval", + "interval_determiner": "Every", + "seconds": "seconds" + }, + "error_message": { + "label": "Error Message", + "code_placeholder": "Code Message", + "message_placeholder": "Error Message", + "help": "Set a custom error code and message to be returned when the rate limit is exceeded." + }, + "identifiers": { + "label": "Identifiers", + "options": { + "ip": "IP", + "credential": "Credential", + "consumer": "Consumer", + "service": "Service", + "header": "Header", + "path": "Path", + "consumer-group": "Consumer Group" + } + } } } diff --git a/packages/entities/entities-plugins/docs/plugin-form.md b/packages/entities/entities-plugins/docs/plugin-form.md index 736649a453..97e44ce37e 100644 --- a/packages/entities/entities-plugins/docs/plugin-form.md +++ b/packages/entities/entities-plugins/docs/plugin-form.md @@ -97,6 +97,12 @@ A form component for Plugins. - default: `false` - Whether to enable grouping for required and advanced (optional) fields. + - `useRLARedesignedForm`: + - type: `boolean` + - required: `false` + - default: `false` + - Whether to use the redesigned form for the RLA plugin. + The base konnect or kongManger config. #### `pluginType` diff --git a/packages/entities/entities-plugins/sandbox/pages/PluginFormPage.vue b/packages/entities/entities-plugins/sandbox/pages/PluginFormPage.vue index d12a8b1ade..b4bd447704 100644 --- a/packages/entities/entities-plugins/sandbox/pages/PluginFormPage.vue +++ b/packages/entities/entities-plugins/sandbox/pages/PluginFormPage.vue @@ -51,6 +51,7 @@ const konnectConfig = ref({ backRoute: { name: 'select-plugin' }, cancelRoute: { name: 'home' }, groupFields: true, + useRLARedesignedForm: true, }) const kongManagerConfig = ref({ @@ -63,6 +64,7 @@ const kongManagerConfig = ref({ backRoute: { name: 'select-plugin' }, cancelRoute: { name: 'home' }, groupFields: true, + useRLARedesignedForm: true, }) const onUpdate = (payload: Record) => { diff --git a/packages/entities/entities-plugins/src/components/PluginEntityForm.vue b/packages/entities/entities-plugins/src/components/PluginEntityForm.vue index 59ff14d256..47c790be3c 100644 --- a/packages/entities/entities-plugins/src/components/PluginEntityForm.vue +++ b/packages/entities/entities-plugins/src/components/PluginEntityForm.vue @@ -136,6 +136,7 @@ const { axiosInstance } = useAxios(props.config?.axiosRequestConfig) const { parseSchema } = composables.useSchemas(props.entityMap.focusedEntity?.id || undefined, { groupFields: props.config.groupFields, + useRLARedesignedForm: props.config.useRLARedesignedForm, }) const { convertToDotNotation, unFlattenObject, isObjectEmpty, unsetNullForeignKey } = composables.usePluginHelpers() @@ -604,7 +605,7 @@ watch(() => props.schema, (newSchema, oldSchema) => { formSchema.value = { fields: formSchema.value?.fields?.map((r: Record) => { return { ...r, disabled: r.disabled || false } }) } Object.assign(originalModel, JSON.parse(JSON.stringify(form.model))) - sharedFormName.value = getSharedFormName(form.model.name) + sharedFormName.value = getSharedFormName(form.model.name, { useRLARedesignedForm: props.config.useRLARedesignedForm }) initFormModel() }, { immediate: true, deep: true }) diff --git a/packages/entities/entities-plugins/src/composables/useSchemas.ts b/packages/entities/entities-plugins/src/composables/useSchemas.ts index f8387836a7..3e10a4fbbb 100644 --- a/packages/entities/entities-plugins/src/composables/useSchemas.ts +++ b/packages/entities/entities-plugins/src/composables/useSchemas.ts @@ -48,6 +48,7 @@ export interface Schema { export interface UseSchemasOptions { app?: 'konnect' | 'kongManager' groupFields?: boolean + useRLARedesignedForm?: boolean } /** Sorts non-config fields and place them at the top */ @@ -242,7 +243,8 @@ export const useSchemas = (entityId?: string, options?: UseSchemasOptions) => { // No field grouping for: // - Plugins with custom layouts // - Plugins explicitly marked to use legacy form - if (!getSharedFormName(pluginName) && options?.groupFields && !metadata?.useLegacyForm) { + // - Redesigned RLA form + if (!getSharedFormName(pluginName, { useRLARedesignedForm: options?.useRLARedesignedForm }) && options?.groupFields && !metadata?.useLegacyForm) { const pinnedFields = [] const defaultVisibleFields = [] const advancedFields = [] diff --git a/packages/entities/entities-plugins/src/types/plugin-form.ts b/packages/entities/entities-plugins/src/types/plugin-form.ts index 2f4861207e..0043e41554 100644 --- a/packages/entities/entities-plugins/src/types/plugin-form.ts +++ b/packages/entities/entities-plugins/src/types/plugin-form.ts @@ -38,6 +38,8 @@ export interface BasePluginFormConfig { disableConsumerGroupScope?: boolean /** Whether to enable grouping for required and advanced (optional) fields. Default: false */ groupFields?: boolean + /** Whether to use the redesigned form for the RLA plugin. Default: false */ + useRLARedesignedForm?: boolean } export interface KongManagerPluginSelectConfig extends BasePluginSelectConfig, KongManagerBaseFormConfig {} From 844281e810248f9c3b6d70035fae437a770df49a Mon Sep 17 00:00:00 2001 From: Makito Date: Wed, 22 May 2024 12:19:46 +0800 Subject: [PATCH 2/7] fix(forms): address feedback on RLA form --- packages/core/forms/src/forms/RLAForm.vue | 93 ++++++++++++++++++++++- packages/core/forms/src/locales/en.json | 6 +- 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/packages/core/forms/src/forms/RLAForm.vue b/packages/core/forms/src/forms/RLAForm.vue index 1241aef2a1..98a41588d8 100644 --- a/packages/core/forms/src/forms/RLAForm.vue +++ b/packages/core/forms/src/forms/RLAForm.vue @@ -79,6 +79,18 @@ + +
+
Examples:
+
    +
  1. + {{ example.description }}
    Request number: {{ example.config.limit }}, Time interval: {{ example.config.window_size }}, Window type: {{ example.config.window_type }} +
  2. +
+
{ const addRequestLimit = (index: number) => { const limits = cloneDeep(props.formModel?.['config-limit'] ?? []) const windowSizes = cloneDeep(props.formModel?.['config-window_size'] ?? []) + if (limits.length === 0) { + limits.push(undefined) + } + if (windowSizes.length === 0) { + windowSizes.push(undefined) + } limits.splice(index + 1, 0, undefined) windowSizes.splice(index + 1, 0, undefined) props.onModelUpdated(limits, 'config-limit') @@ -262,6 +332,12 @@ const removeRequestLimit = (index: number) => { if (requestLimits.value.length > 1) { const limits = cloneDeep(props.formModel?.['config-limit'] ?? []) const windowSizes = cloneDeep(props.formModel?.['config-window_size'] ?? []) + if (limits.length === 0) { + limits.push(undefined) + } + if (windowSizes.length === 0) { + windowSizes.push(undefined) + } limits.splice(index, 1) windowSizes.splice(index, 1) props.onModelUpdated(limits, 'config-limit') @@ -292,7 +368,7 @@ const removeRequestLimit = (index: number) => { &-items { display: flex; flex-direction: column; - gap: $kui-space-40; + gap: $kui-space-50; } &-row, @@ -301,7 +377,7 @@ const removeRequestLimit = (index: number) => { align-items: center; display: flex; flex-direction: row; - gap: $kui-space-40; + gap: $kui-space-50; justify-content: space-between; } @@ -316,6 +392,17 @@ const removeRequestLimit = (index: number) => { :deep(.form-group) { margin-bottom: 0 !important; } + + &-examples { + color: $kui-color-text-neutral; + margin-bottom: $kui-space-50; + margin-top: $kui-space-50; + + ol { + margin-bottom: 0; + margin-top: 0; + } + } } .rla-form-identifiers { @@ -328,7 +415,7 @@ const removeRequestLimit = (index: number) => { align-items: center; display: flex; flex-direction: row; - gap: $kui-space-40; + gap: $kui-space-50; justify-content: space-between; .input-error-code { diff --git a/packages/core/forms/src/locales/en.json b/packages/core/forms/src/locales/en.json index 4d783c053c..b222e6ebc1 100644 --- a/packages/core/forms/src/locales/en.json +++ b/packages/core/forms/src/locales/en.json @@ -66,13 +66,13 @@ "view_advanced_fields": "View Advanced Fields", "request_limits": { "title": "Request Limits", - "subtitle": "Maximum number of requests sent through your gateway at specific time intervals", + "subtitle": "Advanced control on request rate with customizable limits and window sizes.", "label": "Limit {index}", - "help": "Set one or more requests-per-window limit and window sizes to apply a limit to (defined in seconds). There must be a matching number of window limits and sizes specified.", + "help": "Set one or more requests-per-window limits and window sizes to apply a limit to (defined in seconds). There must be a matching number of window limits and sizes specified.", "request_number": "Request number", "time_interval": "Time interval", "interval_determiner": "Every", - "seconds": "seconds" + "seconds": "Seconds" }, "error_message": { "label": "Error Message", From 365f36272b0483058acf67cf02e5c54f7fbe5bb1 Mon Sep 17 00:00:00 2001 From: Makito Date: Wed, 22 May 2024 13:56:11 +0800 Subject: [PATCH 3/7] fix(forms): address feedback --- packages/core/forms/src/forms/RLAForm.vue | 12 +----------- .../fields/advanced/FieldSelectionGroup.vue | 2 +- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/core/forms/src/forms/RLAForm.vue b/packages/core/forms/src/forms/RLAForm.vue index 98a41588d8..69bcac5709 100644 --- a/packages/core/forms/src/forms/RLAForm.vue +++ b/packages/core/forms/src/forms/RLAForm.vue @@ -19,7 +19,6 @@ :tooltip-attributes="{ maxWidth: '300', placement: 'top', - tooltipId: 'rla-form-request-limits-tooltip' }" > {{ t('rla.request_limits.title') }} @@ -259,16 +258,7 @@ const advancedSchema = computed(() => { return { fields: props.formSchema?.fields - ?.filter((field: any) => typeof field.model === 'string' && !omittedFields.has(field.model)) - ?.map((field: any) => { - if (field.model === 'config-redis-cluster_addresses') { - return { - ...field, - hint: 'e.g. localhost:6379', - } - } - return field - }), + ?.filter((field: any) => typeof field.model === 'string' && !omittedFields.has(field.model)), } }) diff --git a/packages/core/forms/src/generator/fields/advanced/FieldSelectionGroup.vue b/packages/core/forms/src/generator/fields/advanced/FieldSelectionGroup.vue index 3e83bee124..024c6663b0 100644 --- a/packages/core/forms/src/generator/fields/advanced/FieldSelectionGroup.vue +++ b/packages/core/forms/src/generator/fields/advanced/FieldSelectionGroup.vue @@ -196,7 +196,7 @@ export default { align-items: center; display: flex; flex-direction: row; - gap: $kui-space-80 + gap: $kui-space-80; } } } From 438299eea0c3274418101732227b227017c8edf6 Mon Sep 17 00:00:00 2001 From: Makito Date: Wed, 22 May 2024 14:14:21 +0800 Subject: [PATCH 4/7] fix(forms): remove redundant props and add hints for redis params --- packages/core/forms/src/forms/RLAForm.vue | 10 +++++++--- packages/core/forms/src/locales/en.json | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/core/forms/src/forms/RLAForm.vue b/packages/core/forms/src/forms/RLAForm.vue index 69bcac5709..09f0ebc78c 100644 --- a/packages/core/forms/src/forms/RLAForm.vue +++ b/packages/core/forms/src/forms/RLAForm.vue @@ -101,7 +101,6 @@ tooltipAttributes: { maxWidth: '300', placement: 'top', - tooltipId: 'rla-form-request-limits-tooltip', }, }" required @@ -121,7 +120,6 @@ :tooltip-attributes="{ maxWidth: '300', placement: 'top', - tooltipId: 'rla-form-error-tooltip' }" > {{ t('rla.error_message.label') }} @@ -258,7 +256,13 @@ const advancedSchema = computed(() => { return { fields: props.formSchema?.fields - ?.filter((field: any) => typeof field.model === 'string' && !omittedFields.has(field.model)), + ?.filter((field: any) => typeof field.model === 'string' && !omittedFields.has(field.model)) + ?.map((field: any) => { + if (field.model === 'config-redis-cluster_addresses' || field.model === 'config-redis-sentinel_addresses') { + field.hint = t('rla.redis_address_example') + } + return field + }), } }) diff --git a/packages/core/forms/src/locales/en.json b/packages/core/forms/src/locales/en.json index b222e6ebc1..9822e94bd1 100644 --- a/packages/core/forms/src/locales/en.json +++ b/packages/core/forms/src/locales/en.json @@ -91,6 +91,7 @@ "path": "Path", "consumer-group": "Consumer Group" } - } + }, + "redis_address_example": "e.g. localhost:6379" } } From 2064f267622a6dff96dd87e27a8ea222f304f809 Mon Sep 17 00:00:00 2001 From: Makito Date: Wed, 22 May 2024 14:25:49 +0800 Subject: [PATCH 5/7] fix(entities-plugins): config for horizontal radios in plugin scoping --- packages/entities/entities-plugins/docs/plugin-form.md | 6 ++++++ .../entities/entities-plugins/src/components/PluginForm.vue | 1 + packages/entities/entities-plugins/src/types/plugin-form.ts | 2 ++ 3 files changed, 9 insertions(+) diff --git a/packages/entities/entities-plugins/docs/plugin-form.md b/packages/entities/entities-plugins/docs/plugin-form.md index 97e44ce37e..1c4f2d5480 100644 --- a/packages/entities/entities-plugins/docs/plugin-form.md +++ b/packages/entities/entities-plugins/docs/plugin-form.md @@ -103,6 +103,12 @@ A form component for Plugins. - default: `false` - Whether to use the redesigned form for the RLA plugin. + - `useHorizontalRadiosForPluginScoping`: + - type: `boolean` + - required: `false` + - default: `false` + - Whether to use the horizontal radios in the plugin scoping section. + The base konnect or kongManger config. #### `pluginType` diff --git a/packages/entities/entities-plugins/src/components/PluginForm.vue b/packages/entities/entities-plugins/src/components/PluginForm.vue index 66f1ab9748..fdc1c7f58e 100644 --- a/packages/entities/entities-plugins/src/components/PluginForm.vue +++ b/packages/entities/entities-plugins/src/components/PluginForm.vue @@ -422,6 +422,7 @@ const defaultFormSchema: DefaultPluginsSchemaRecord = reactive({ }, ], pinned: true, + horizontalRadios: props.config.useHorizontalRadiosForPluginScoping, // KM-136-RLA-Redesign: While enabled, this new horizontal radio design will be applied for ALL plugins }, protocols: { id: 'protocols', diff --git a/packages/entities/entities-plugins/src/types/plugin-form.ts b/packages/entities/entities-plugins/src/types/plugin-form.ts index 0043e41554..0dc92a7dee 100644 --- a/packages/entities/entities-plugins/src/types/plugin-form.ts +++ b/packages/entities/entities-plugins/src/types/plugin-form.ts @@ -40,6 +40,8 @@ export interface BasePluginFormConfig { groupFields?: boolean /** Whether to use the redesigned form for the RLA plugin. Default: false */ useRLARedesignedForm?: boolean + /** Whether to use the horizontal radios in the plugin scoping section. Default: false */ + useHorizontalRadiosForPluginScoping?: boolean } export interface KongManagerPluginSelectConfig extends BasePluginSelectConfig, KongManagerBaseFormConfig {} From dd83a6a52d63df8df532c75033c1250d1e94ad25 Mon Sep 17 00:00:00 2001 From: Makito Date: Fri, 24 May 2024 02:23:15 +0800 Subject: [PATCH 6/7] feat(forms): implement use cases for RLA --- packages/core/forms/package.json | 1 + packages/core/forms/src/forms/RLAForm.vue | 279 ++++++++++++++++------ packages/core/forms/src/locales/en.json | 13 +- pnpm-lock.yaml | 12 +- 4 files changed, 228 insertions(+), 77 deletions(-) diff --git a/packages/core/forms/package.json b/packages/core/forms/package.json index 4fc668505b..2278ff6296 100644 --- a/packages/core/forms/package.json +++ b/packages/core/forms/package.json @@ -60,6 +60,7 @@ "vue": "^3.4.26" }, "devDependencies": { + "@formatjs/intl": "^2.10.2", "@kong-ui-public/i18n": "workspace:^", "@kong/design-tokens": "1.12.12", "@kong/kongponents": "9.0.0-alpha.159", diff --git a/packages/core/forms/src/forms/RLAForm.vue b/packages/core/forms/src/forms/RLAForm.vue index 09f0ebc78c..81c10f75d2 100644 --- a/packages/core/forms/src/forms/RLAForm.vue +++ b/packages/core/forms/src/forms/RLAForm.vue @@ -7,15 +7,12 @@ @model-updated="(value: any, model: string) => onModelUpdated(value, model)" /> - + -
-
+
+
- {{ t('rla.request_limits.label', { index: index + 1 }) }} + {{ t('rla.window_type.label') }} + + -
- + - -
{{ t('rla.request_limits.interval_determiner') }}
- -
{{ t('rla.request_limits.seconds') }}
-
- -
- - - + {{ t(`rla.window_type.options.${wt}` as Parameters[0]) }} + +
+
- +
+ + {{ t('rla.request_limits.label_index', { index: index + 1 }) }} + +
+ - - + +
{{ t('rla.request_limits.interval_determiner') }}
+ +
{{ t('rla.request_limits.seconds') }}
+
+ +
+ + + + + + + +
-
-
-
Examples:
-
    -
  1. - {{ example.description }}
    Request number: {{ example.config.limit }}, Time interval: {{ example.config.window_size }}, Window type: {{ example.config.window_type }} -
  2. -
+
+
{{ t('rla.try_a_use_case') }}:
+
+ + + {{ t('rla.use_case_index', { index: i + 1 }) }} + + + + +
+
@@ -103,7 +148,6 @@ placement: 'top', }, }" - required @selected="(item: any) => props.onModelUpdated(item.value, 'config-identifier')" >