diff --git a/packages/core/forms/fixtures/mockData.ts b/packages/core/forms/fixtures/mockData.ts new file mode 100644 index 0000000000..b90b66a75e --- /dev/null +++ b/packages/core/forms/fixtures/mockData.ts @@ -0,0 +1,888 @@ +export const redisPartials = [ + { + 'created_at': 1739167439, + 'updated_at': 1739167439, + 'name': 'test-redis-ce-config', + 'type': 'redis-ce', + 'config': { + 'host': '127.0.0.1', + 'password': null, + 'ssl_verify': false, + 'database': 0, + 'server_name': 'test-ce-server-name', + 'username': null, + 'timeout': 2000, + 'ssl': false, + 'port': 6379, + }, + 'id': '028c9637-0408-4ab0-8d35-0b474de4bde6', + }, + { + 'created_at': 1739166982, + 'updated_at': 1739166982, + 'name': 'test-redis-ee', + 'type': 'redis-ee', + 'config': { + 'host': '127.0.0.1', + 'password': null, + 'ssl_verify': false, + 'username': 'test-username', + 'keepalive_pool_size': 256, + 'port': 6379, + 'connection_is_proxied': false, + 'cluster_nodes': null, + 'sentinel_role': null, + 'send_timeout': 2000, + 'sentinel_nodes': null, + 'database': 0, + 'ssl': false, + 'keepalive_backlog': 0, + 'server_name': null, + 'read_timeout': 2000, + 'cluster_max_redirections': 5, + 'sentinel_password': null, + 'sentinel_username': null, + 'connect_timeout': 2000, + 'sentinel_master': null, + }, + 'id': '3f2d22a2-fe4c-4211-b1db-2ad44b36eab7', + }, + { + 'created_at': 1739181896, + 'updated_at': 1739181896, + 'name': 'test-redis-ee-2', + 'type': 'redis-ee', + 'config': { + 'host': '127.0.0.1', + 'password': null, + 'ssl_verify': false, + 'username': null, + 'keepalive_pool_size': 256, + 'port': 6379, + 'connection_is_proxied': false, + 'cluster_nodes': null, + 'sentinel_role': null, + 'send_timeout': 2000, + 'sentinel_nodes': null, + 'database': 0, + 'ssl': false, + 'keepalive_backlog': 0, + 'server_name': 'test-redis-ee-server', + 'read_timeout': 2000, + 'cluster_max_redirections': 5, + 'sentinel_password': null, + 'sentinel_username': null, + 'connect_timeout': 2000, + 'sentinel_master': null, + }, + 'id': 'c41669c8-9ff6-4d8c-b667-494864a585ec', + }, +] + +export const redisCEConfig = { + 'created_at': 1739167439, + 'updated_at': 1739167439, + 'name': 'test-redis-ce-config', + 'type': 'redis-ce', + 'config': { + 'host': '127.0.0.1', + 'password': null, + 'ssl_verify': false, + 'database': 0, + 'server_name': 'test-ce-server-name', + 'username': null, + 'timeout': 2000, + 'ssl': false, + 'port': 6379, + }, + 'id': '028c9637-0408-4ab0-8d35-0b474de4bde6', +} + +export const redisEEConfig = { + 'created_at': 1739181896, + 'updated_at': 1739181896, + 'name': 'test-redis-ee', + 'type': 'redis-ee', + 'config': { + 'host': '127.0.0.1', + 'password': null, + 'ssl_verify': false, + 'username': null, + 'keepalive_pool_size': 256, + 'port': 6379, + 'connection_is_proxied': false, + 'cluster_nodes': null, + 'sentinel_role': null, + 'send_timeout': 2000, + 'sentinel_nodes': null, + 'database': 0, + 'ssl': false, + 'keepalive_backlog': 0, + 'server_name': 'test-redis-ee-server', + 'read_timeout': 2000, + 'cluster_max_redirections': 5, + 'sentinel_password': null, + 'sentinel_username': null, + 'connect_timeout': 2000, + 'sentinel_master': null, + }, + 'id': 'c41669c8-9ff6-4d8c-b667-494864a585ec', +} + +export const RLSchema = { + 'groups': [ + { + 'fields': [ + { + 'default': 'rate-limiting', + 'type': 'input', + 'inputType': 'hidden', + 'styleClasses': 'd-none hidden-field', + 'pinned': true, + 'id': 'name', + 'model': 'name', + 'label': '', + }, + { + 'type': 'switch', + 'model': 'enabled', + 'label': 'Enabled', + 'textOn': 'This plugin is Enabled', + 'textOff': 'This plugin is Disabled', + 'styleClasses': 'field-switch hide-label', + 'default': true, + 'pinned': true, + 'id': 'enabled', + }, + { + 'type': 'selectionGroup', + 'disabled': false, + 'inputType': 'hidden', + 'styleClasses': 'hide-label', + 'fields': [ + { + 'label': 'Global', + 'description': 'All services, routes, and consumers', + }, + { + 'label': 'Scoped', + 'description': 'Specific Gateway Services, Routes, Consumers, and/or Consumer Groups', + 'fields': [ + { + 'id': 'service-id', + 'model': 'service-id', + 'label': 'Gateway Service', + 'placeholder': 'Select a Gateway Service', + 'type': 'AutoSuggest', + 'entity': 'services', + 'inputValues': { + 'fields': [ + 'name', + 'id', + ], + }, + 'help': 'The Gateway Service to which this plugin configuration will apply', + 'disabled': false, + }, + { + 'id': 'route-id', + 'model': 'route-id', + 'label': 'Route', + 'placeholder': 'Select a Route', + 'type': 'AutoSuggest', + 'entity': 'routes', + 'inputValues': { + 'fields': [ + 'name', + 'id', + ], + 'primaryField': 'id', + }, + 'help': 'The Route that this Plugin configuration will target', + 'disabled': false, + }, + { + 'model': 'consumer-id', + 'label': 'Consumer', + 'placeholder': 'Select a Consumer', + 'type': 'AutoSuggest', + 'entity': 'consumers', + 'inputValues': { + 'fields': [ + 'username', + 'custom_id', + 'id', + ], + 'primaryField': 'username', + }, + 'help': 'The Consumer that this plugin configuration will target', + 'disabled': false, + }, + { + 'model': 'consumer_group-id', + 'label': 'Consumer Group', + 'placeholder': 'Select a Consumer Group', + 'type': 'AutoSuggest', + 'entity': 'consumer_groups', + 'entityDataKey': 'consumer_group', + 'inputValues': { + 'fields': [ + 'name', + 'id', + ], + 'primaryField': 'name', + }, + 'help': 'The Consumer Group that this plugin configuration will target', + 'disabled': false, + }, + ], + }, + ], + 'pinned': true, + 'id': 'selectionGroup', + 'model': 'selectionGroup', + 'label': '', + }, + ], + }, + { + 'fields': [ + { + 'id': 'protocols', + 'default': [ + 'grpc', + 'grpcs', + 'http', + 'https', + ], + 'help': 'A list of the request protocols that will trigger this plugin. The default value, as well as the possible values allowed on this field, may change depending on the plugin type.', + 'label': 'Protocols', + 'required': true, + 'styleClasses': 'plugin-protocols-select', + 'type': 'multiselect', + 'values': [ + { + 'label': 'grpc', + 'value': 'grpc', + }, + { + 'label': 'grpcs', + 'value': 'grpcs', + }, + { + 'label': 'http', + 'value': 'http', + }, + { + 'label': 'https', + 'value': 'https', + }, + ], + 'placeholder': 'Select valid protocols for the plugin. Default protocols: grpc, grpcs, http, https', + 'model': 'protocols', + }, + { + 'id': 'config-day', + 'model': 'config-day', + 'type': 'input', + 'label': 'Config.Day', + 'help': '
The number of HTTP requests that can be made per day.
\n', + 'inputType': 'number', + 'valueType': 'number', + }, + { + 'id': 'config-fault_tolerant', + 'model': 'config-fault_tolerant', + 'type': 'checkbox', + 'required': true, + 'label': 'Config.Fault Tolerant', + 'help': 'A boolean value that determines if the requests should be proxied even if Kong has troubles connecting a third-party data store. If true
, requests will be proxied anyway, effectively disabling the rate-limiting function until the data store is working again. If false
, then the clients will see 500
errors.
Optionally hide informative response headers.
\n', + 'default': false, + 'valueType': 'boolean', + }, + { + 'id': 'config-hour', + 'model': 'config-hour', + 'type': 'input', + 'label': 'Config.Hour', + 'help': 'The number of HTTP requests that can be made per hour.
\n', + 'inputType': 'number', + 'valueType': 'number', + }, + { + 'id': 'config-minute', + 'model': 'config-minute', + 'type': 'input', + 'label': 'Config.Minute', + 'help': 'The number of HTTP requests that can be made per minute.
\n', + 'inputType': 'number', + 'valueType': 'number', + }, + { + 'id': 'config-month', + 'model': 'config-month', + 'type': 'input', + 'label': 'Config.Month', + 'help': 'The number of HTTP requests that can be made per month.
\n', + 'inputType': 'number', + 'valueType': 'number', + }, + { + 'id': 'config-second', + 'model': 'config-second', + 'type': 'input', + 'label': 'Config.Second', + 'help': 'The number of HTTP requests that can be made per second.
\n', + 'inputType': 'number', + 'valueType': 'number', + }, + { + 'id': 'config-sync_rate', + 'model': 'config-sync_rate', + 'type': 'input', + 'required': true, + 'label': 'Config.Sync Rate', + 'help': 'How often to sync counter data to the central data store. A value of -1 results in synchronous behavior.
\n', + 'selectOptions': { + 'hideNoneSelectedText': true, + }, + 'default': -1, + 'placeholder': 'Default: -1', + 'inputType': 'number', + 'valueType': 'number', + }, + { + 'id': 'config-year', + 'model': 'config-year', + 'type': 'input', + 'label': 'Config.Year', + 'help': 'The number of HTTP requests that can be made per year.
\n', + 'inputType': 'number', + 'valueType': 'number', + }, + ], + 'collapsible': { + 'title': 'Plugin Configuration', + 'description': 'Configuration parameters for this plugin. View advanced parameters for extended configuration.', + 'nestedCollapsible': { + 'fields': [ + { + 'id': '_redis', + 'fields': [ + { + 'id': 'config-redis-database', + 'model': 'config-redis-database', + 'type': 'input', + 'label': 'Config.Redis.Database', + 'help': 'Database to use for the Redis connection when using the redis
strategy
A string representing a host name, such as example.com.
\n', + 'inputType': 'text', + 'valueType': 'string', + }, + { + 'id': 'config-redis-password', + 'model': 'config-redis-password', + 'type': 'input', + 'referenceable': true, + 'label': 'Config.Redis.Password', + 'help': 'Password to use for Redis connections. If undefined, no AUTH commands are sent to Redis.
\n', + 'inputType': 'password', + 'valueType': 'string', + }, + { + 'id': 'config-redis-port', + 'model': 'config-redis-port', + 'type': 'input', + 'label': 'Config.Redis.Port', + 'help': 'An integer representing a port number between 0 and 65535, inclusive.
\n', + 'default': 6379, + 'placeholder': 'Default: 6379', + 'inputType': 'number', + 'valueType': 'number', + }, + { + 'id': 'config-redis-server_name', + 'model': 'config-redis-server_name', + 'type': 'input', + 'required': false, + 'label': 'Config.Redis.Server Name', + 'help': 'A string representing an SNI (server name indication) value for TLS.
\n', + 'inputType': 'text', + 'valueType': 'string', + }, + { + 'id': 'config-redis-ssl', + 'model': 'config-redis-ssl', + 'type': 'checkbox', + 'required': false, + 'label': 'Config.Redis.Ssl', + 'help': 'If set to true, uses SSL to connect to Redis.
\n', + 'default': false, + 'valueType': 'boolean', + }, + { + 'id': 'config-redis-ssl_verify', + 'model': 'config-redis-ssl_verify', + 'type': 'checkbox', + 'required': false, + 'label': 'Config.Redis.Ssl Verify', + 'help': 'If set to true, verifies the validity of the server SSL certificate. If setting this parameter, also configure lua_ssl_trusted_certificate
in kong.conf
to specify the CA (or server) certificate used by your Redis server. You may also need to configure lua_ssl_verify_depth
accordingly.
An integer representing a timeout in milliseconds. Must be between 0 and 2^31-2.
\n', + 'default': 2000, + 'placeholder': 'Default: 2000', + 'inputType': 'number', + 'valueType': 'number', + }, + { + 'id': 'config-redis-username', + 'model': 'config-redis-username', + 'type': 'input', + 'referenceable': true, + 'label': 'Config.Redis.Username', + 'help': 'Username to use for Redis connections. If undefined, ACL authentication won't be performed. This requires Redis v6.0.0+. To be compatible with Redis v5.x.y, you can set it to default
.
Set a custom error code to return when the rate limit is exceeded.
\n', + 'default': 429, + 'placeholder': 'Default: 429', + 'inputType': 'number', + 'valueType': 'number', + }, + { + 'id': 'config-error_message', + 'model': 'config-error_message', + 'type': 'input', + 'label': 'Config.Error Message', + 'help': 'Set a custom error message to return when the rate limit is exceeded.
\n', + 'default': 'API rate limit exceeded', + 'placeholder': 'Default: API rate limit exceeded', + 'inputType': 'text', + 'valueType': 'string', + }, + { + 'id': 'config-header_name', + 'model': 'config-header_name', + 'type': 'input', + 'label': 'Config.Header Name', + 'help': 'A string representing an HTTP header name.
\n', + 'inputType': 'text', + 'valueType': 'string', + }, + { + 'id': 'config-limit_by', + 'model': 'config-limit_by', + 'type': 'select', + 'label': 'Config.Limit By', + 'help': 'The entity that is used when aggregating the limits.
\n', + 'values': [ + 'consumer', + 'credential', + 'ip', + 'service', + 'header', + 'path', + 'consumer-group', + ], + 'selectOptions': { + 'noneSelectedText': 'No selection...', + }, + 'default': 'consumer', + 'valueType': 'string', + }, + { + 'id': 'config-path', + 'model': 'config-path', + 'type': 'input', + 'label': 'Config.Path', + 'help': 'A string representing a URL path, such as /path/to/resource. Must start with a forward slash (/) and must not contain empty segments (i.e., two consecutive forward slashes).
\n', + 'inputType': 'text', + 'valueType': 'string', + }, + { + 'id': 'config-policy', + 'model': 'config-policy', + 'type': 'select', + 'label': 'Config.Policy', + 'help': 'The rate-limiting policies to use for retrieving and incrementing the limits.
\n', + 'values': [ + 'local', + 'cluster', + 'redis', + ], + 'selectOptions': { + 'noneSelectedText': 'No selection...', + }, + 'default': 'local', + 'valueType': 'string', + }, + ], + 'triggerLabel': { + 'expand': 'View Advanced Parameters', + 'collapse': 'Hide Advanced Parameters', + }, + }, + }, + 'slots': { + 'beforeContent': 'plugin-config-before-content', + 'emptyState': 'plugin-config-empty-state', + }, + }, + ], +} + +export const RLModel = { + 'name': 'rate-limiting', + 'enabled': true, + 'service-id': null, + 'route-id': null, + 'consumer-id': null, + 'consumer_group-id': null, + 'protocols': [ + 'grpc', + 'grpcs', + 'http', + 'https', + ], + 'instance_name': '', + 'tags': null, + 'config-day': null, + 'config-error_code': 429, + 'config-error_message': 'API rate limit exceeded', + 'config-fault_tolerant': true, + 'config-header_name': null, + 'config-hide_client_headers': false, + 'config-hour': null, + 'config-limit_by': 'consumer', + 'config-minute': null, + 'config-month': null, + 'config-path': null, + 'config-policy': 'local', + 'config-redis-database': 0, + 'config-redis-host': null, + 'config-redis-password': null, + 'config-redis-port': 6379, + 'config-redis-server_name': null, + 'config-redis-ssl': false, + 'config-redis-ssl_verify': false, + 'config-redis-timeout': 2000, + 'config-redis-username': null, + 'config-second': null, + 'config-sync_rate': -1, + 'config-year': null, +} + +export const customPluginSchema = { + 'groups': [ + { + 'fields': [ + { + 'default': 'custom-redis', + 'type': 'input', + 'inputType': 'hidden', + 'styleClasses': 'd-none hidden-field', + 'pinned': true, + 'id': 'name', + 'model': 'name', + 'label': '', + }, + { + 'type': 'switch', + 'model': 'enabled', + 'label': 'Enabled', + 'textOn': 'This plugin is Enabled', + 'textOff': 'This plugin is Disabled', + 'styleClasses': 'field-switch hide-label', + 'default': true, + 'pinned': true, + 'id': 'enabled', + }, + { + 'type': 'selectionGroup', + 'disabled': false, + 'inputType': 'hidden', + 'styleClasses': 'hide-label', + 'fields': [ + { + 'label': 'Global', + 'description': 'All services, routes, and consumers', + }, + { + 'label': 'Scoped', + 'description': 'Specific Gateway Services, Routes, Consumers, and/or Consumer Groups', + 'fields': [ + { + 'id': 'service-id', + 'model': 'service-id', + 'label': 'Gateway Service', + 'placeholder': 'Select a Gateway Service', + 'type': 'AutoSuggest', + 'entity': 'services', + 'inputValues': { + 'fields': [ + 'name', + 'id', + ], + }, + 'help': 'The Gateway Service to which this plugin configuration will apply', + 'disabled': false, + }, + { + 'id': 'route-id', + 'model': 'route-id', + 'label': 'Route', + 'placeholder': 'Select a Route', + 'type': 'AutoSuggest', + 'entity': 'routes', + 'inputValues': { + 'fields': [ + 'name', + 'id', + ], + 'primaryField': 'id', + }, + 'help': 'The Route that this Plugin configuration will target', + 'disabled': false, + }, + { + 'model': 'consumer-id', + 'label': 'Consumer', + 'placeholder': 'Select a Consumer', + 'type': 'AutoSuggest', + 'entity': 'consumers', + 'inputValues': { + 'fields': [ + 'username', + 'custom_id', + 'id', + ], + 'primaryField': 'username', + }, + 'help': 'The Consumer that this plugin configuration will target', + 'disabled': false, + }, + { + 'model': 'consumer_group-id', + 'label': 'Consumer Group', + 'placeholder': 'Select a Consumer Group', + 'type': 'AutoSuggest', + 'entity': 'consumer_groups', + 'entityDataKey': 'consumer_group', + 'inputValues': { + 'fields': [ + 'name', + 'id', + ], + 'primaryField': 'name', + }, + 'help': 'The Consumer Group that this plugin configuration will target', + 'disabled': false, + }, + ], + }, + ], + 'pinned': true, + 'id': 'selectionGroup', + 'model': 'selectionGroup', + 'label': '', + }, + ], + }, + { + 'fields': [ + { + 'id': 'protocols', + 'default': [ + 'grpc', + 'grpcs', + 'http', + 'https', + ], + 'help': 'A list of the request protocols that will trigger this plugin. The default value, as well as the possible values allowed on this field, may change depending on the plugin type.', + 'label': 'Protocols', + 'required': true, + 'styleClasses': 'plugin-protocols-select', + 'type': 'multiselect', + 'values': [ + { + 'label': 'grpc', + 'value': 'grpc', + }, + { + 'label': 'grpcs', + 'value': 'grpcs', + }, + { + 'label': 'http', + 'value': 'http', + }, + { + 'label': 'https', + 'value': 'https', + }, + ], + 'placeholder': 'Select valid protocols for the plugin. Default protocols: grpc, grpcs, http, https', + 'model': 'protocols', + }, + { + 'id': 'config-test_schema', + 'model': 'config-test_schema', + 'type': 'checkbox', + 'required': true, + 'label': 'Config.Test Schema', + 'default': true, + 'valueType': 'boolean', + }, + ], + 'collapsible': { + 'title': 'Plugin Configuration', + 'description': 'Configuration parameters for this plugin. View advanced parameters for extended configuration.', + 'nestedCollapsible': { + 'fields': [ + { + 'id': '_redis', + 'fields': [ + { + 'id': 'config-redis-host', + 'model': 'config-redis-host', + 'type': 'input', + 'label': 'Config.Redis.Host', + 'help': 'A string representing a host name, such as example.com.
\n', + 'default': '127.0.0.1', + 'placeholder': 'Default: 127.0.0.1', + 'inputType': 'text', + 'valueType': 'string', + }, + { + 'id': 'config-redis-port', + 'model': 'config-redis-port', + 'type': 'input', + 'label': 'Config.Redis.Port', + 'help': 'An integer representing a port number between 0 and 65535, inclusive.
\n', + 'default': 6379, + 'placeholder': 'Default: 6379', + 'inputType': 'number', + 'valueType': 'number', + }, + ], + 'model': '__redis_partial', + 'pluginType': 'custom', + 'order': -1, + }, + { + 'default': '', + 'type': 'input', + 'label': 'Instance Name', + 'inputType': 'text', + 'help': 'A custom name for this plugin instance to help identifying from the list view.', + 'id': 'instance_name', + 'model': 'instance_name', + }, + { + 'label': 'Tags', + 'name': 'tags', + 'type': 'input', + 'inputType': 'text', + 'valueType': 'array', + 'valueArrayType': 'string', + 'placeholder': 'Enter list of tags', + 'help': 'An optional set of strings for grouping and filtering, separated by commas.', + 'hint': 'e.g. tag1, tag2, tag3', + 'id': 'tags', + 'model': 'tags', + }, + ], + 'triggerLabel': { + 'expand': 'View Advanced Parameters', + 'collapse': 'Hide Advanced Parameters', + }, + }, + }, + 'slots': { + 'beforeContent': 'plugin-config-before-content', + 'emptyState': 'plugin-config-empty-state', + }, + }, + ], +} + +export const customPluginModel = { + 'name': 'custom-redis', + 'enabled': true, + 'service-id': null, + 'route-id': null, + 'consumer-id': null, + 'consumer_group-id': null, + 'protocols': [ + 'grpc', + 'grpcs', + 'http', + 'https', + ], + 'instance_name': '', + 'tags': null, + 'config-redis-host': '127.0.0.1', + 'config-redis-port': 6379, + 'config-test_schema': true, +} diff --git a/packages/core/forms/package.json b/packages/core/forms/package.json index 7e5741eb29..374b06622b 100644 --- a/packages/core/forms/package.json +++ b/packages/core/forms/package.json @@ -55,11 +55,15 @@ "lodash-es": "^4.17.21" }, "peerDependencies": { + "@kong-ui-public/entities-shared": "workspace:^", + "@kong-ui-public/entities-redis-configurations": "workspace:^", "@kong-ui-public/i18n": "workspace:^", "@kong/kongponents": "^9.14.16", "vue": "^3.5.12" }, "devDependencies": { + "@kong-ui-public/entities-shared": "workspace:^", + "@kong-ui-public/entities-redis-configurations": "workspace:^", "@kong-ui-public/i18n": "workspace:^", "@kong/design-tokens": "1.17.2", "@kong/kongponents": "9.17.2", diff --git a/packages/core/forms/src/components/FormGenerator.vue b/packages/core/forms/src/components/FormGenerator.vue index 644795941d..da7eb1b1db 100644 --- a/packages/core/forms/src/components/FormGenerator.vue +++ b/packages/core/forms/src/components/FormGenerator.vue @@ -11,8 +11,21 @@ v-for="field in fields" :key="field.model" > +