diff --git a/package.json b/package.json index ccc2fc3..051cd2e 100644 --- a/package.json +++ b/package.json @@ -31,13 +31,13 @@ "@kong-ui-public/entities-gateway-services": "^2.2.12", "@kong-ui-public/entities-key-sets": "^2.1.16", "@kong-ui-public/entities-keys": "^2.1.16", - "@kong-ui-public/entities-plugins": "^2.5.4", + "@kong-ui-public/entities-plugins": "^2.5.8", "@kong-ui-public/entities-routes": "^2.2.9", "@kong-ui-public/entities-shared": "^2.2.0", "@kong-ui-public/entities-snis": "^2.1.16", "@kong-ui-public/entities-upstreams-targets": "^2.1.16", "@kong-ui-public/entities-vaults": "^2.1.16", - "@kong-ui-public/forms": "^2.1.10", + "@kong-ui-public/forms": "^2.1.16", "@kong-ui-public/i18n": "^2.0.3", "@kong-ui-public/misc-widgets": "^2.0.7", "@kong/icons": "^1.8.8", diff --git a/src/components/EntityForm/ConfirmModalDialog.vue b/src/components/EntityForm/ConfirmModalDialog.vue deleted file mode 100644 index 222cf9b..0000000 --- a/src/components/EntityForm/ConfirmModalDialog.vue +++ /dev/null @@ -1,162 +0,0 @@ - - - - - - {{ bodyText }} - - - - - - - - {{ dismissText }} - - - - {{ proceedText }} - - - - - - - - - diff --git a/src/components/EntityForm/EntityForm.vue b/src/components/EntityForm/EntityForm.vue deleted file mode 100644 index c46cf59..0000000 --- a/src/components/EntityForm/EntityForm.vue +++ /dev/null @@ -1,775 +0,0 @@ - - - - - - - - - - - - - - - - - - - - {{ isModalOpen ? buttonText : confirmButtonText }} - - - - Cancel - - - - - - - - - - diff --git a/src/components/EntityForm/NativeEntityForm.vue b/src/components/EntityForm/NativeEntityForm.vue deleted file mode 100644 index a668dfb..0000000 --- a/src/components/EntityForm/NativeEntityForm.vue +++ /dev/null @@ -1,697 +0,0 @@ - - - - - - - - - - - - - - - - - - {{ isModalOpen ? buttonText : confirmButtonText }} - - - - Cancel - - - - - - - - - - diff --git a/src/components/EntityForm/fields.js b/src/components/EntityForm/fields.js deleted file mode 100644 index c23bd74..0000000 --- a/src/components/EntityForm/fields.js +++ /dev/null @@ -1,15 +0,0 @@ -const ArrayStringFieldSchema = { - type: 'array', - valueType: 'string', - valueArrayType: 'array', - itemContainerComponent: 'FieldArrayItem', - fieldClasses: 'kong-form-array-string-field', - fieldItemsClasses: 'kong-form-array-string-field-item', - inputAttributes: { class: 'form-control', style: { minWidth: '200px' } }, - validator: 'array', - styleClasses: 'kong-form-field-wrapper', - newElementButtonLabel: '+ Add', - newElementButtonLabelClasses: 'kong-form-new-element-button-label', -} - -export { ArrayStringFieldSchema } diff --git a/src/components/EntityForm/helpers/index.js b/src/components/EntityForm/helpers/index.js deleted file mode 100644 index 60f6539..0000000 --- a/src/components/EntityForm/helpers/index.js +++ /dev/null @@ -1,150 +0,0 @@ -const capitalizeRegEx = /(?:^|[\s-:'"])\w/g - -export const capitalize = (str) => { - if (!str) return '' - - return str.replace(capitalizeRegEx, (a) => a.toUpperCase()) -} - -export const convertToDotNotation = (key) => { - return key.replace(/-/g, '.') -} - -const camelCase = (str) => { - return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) { - return index === 0 ? word.toLowerCase() : word.toUpperCase() - }).replace(/\s+/g, '') -} - -export const generateFieldLabel = (fieldKey) => { - return convertToDotNotation(fieldKey) - .split('.') - .map((name) => capitalize(camelCase(name))) - .join('.') -} - -export const getMessageFromError = (error) => { - if (!error) { - return error - } - - if (error && error.response && error.response.data) { - if (error.response.data.message) { - return error.response.data.message - } - - if (typeof error.response.data === 'string') { - return error.response.data - } - - if (typeof error.response.data === 'object') { - return Object.keys(error.response.data) - .map(key => `${key} ${error.response.data[key]}`) - .join(', ') - } - } - - return error.message || 'There was an error' -} - -export const pickReadableField = (item, typeHint) => { - if (!item) return undefined - - let preferred - - switch (typeHint) { - case 'services': - case 'routes': - case 'upstreams': - case 'snis': - case 'keys': - case 'key-sets': - preferred = item.name - break - case 'plugins': - preferred = item.instance_name || item.name - break - case 'targets': - preferred = item.target - break - case 'consumers': - preferred = item.username || item.custom_id - break - case 'certificates': - case 'ca_certificates': - preferred = item.id - break - case 'vaults': - preferred = item.prefix || item.name - break - default: - preferred = item.name || item.username || item.email || item.group || item.key || - item.path || item.target - break - } - - return preferred || item.id -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const unFlattenObject = (obj) => { - const result = {} - - // Loop object and reduce each key to build nested structure - for (const key in obj) { - const keys = key.split('.') - - keys.reduce((acc, cur, curIdx) => { - return acc[cur] || - // If current key in acc is the next - // item in the split array (dot notation) - // set its value - (acc[cur] = isNaN(keys[curIdx + 1]) - ? (keys.length - 1 === curIdx ? obj[key] : {}) - : []) - }, result) - } - - return result -} - -export const uuidRegEx = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' - -export const redirectOnResponseStatus = ($router, status, location, options) => { - const opts = options || {} - const changeRoute = opts.replace ? $router.replace : $router.push - - return function (response) { - // Handle both success and error responses - const resp = response.response ? response.response : response - - if (resp && resp.status === status) { - changeRoute.call($router, location) - } - } -} - -export const compareObjects = (a, b) => { - return JSON.stringify(a) === JSON.stringify(b) -} - -export const isObjectEmpty = (obj) => { - return Object.keys(obj).length === 0 -} - -export const sortAlpha = (property) => { - return (a, b) => { - let propertyA = a[property] === undefined || a[property] === null ? '' : a[property] - let propertyB = b[property] === undefined || b[property] === null ? '' : b[property] - - if (Array.isArray(a[property])) { - propertyA = a[property][0] - } - - if (Array.isArray(b[property])) { - propertyB = b[property][0] - } - - return propertyA.localeCompare(propertyB) - } -} diff --git a/src/components/EntityForm/mixins/EntityMixin.js b/src/components/EntityForm/mixins/EntityMixin.js deleted file mode 100644 index 8c5dd47..0000000 --- a/src/components/EntityForm/mixins/EntityMixin.js +++ /dev/null @@ -1,357 +0,0 @@ -import { customFields } from '@kong-ui-public/forms' -import { capitalize, generateFieldLabel } from '../helpers' -import { ArrayStringFieldSchema } from '../fields' - -function buildCustomFields (schema) { - return schema.fields.reduce((acc, field) => { - if (field.fields) { - field.fields.forEach(subField => { - acc[subField.model] = subField - }) - } - - if (field.model) acc[field.model] = field - - return acc - }, {}) -} - -function setFieldDefaultValue (schema) { - return Array.isArray(schema.default) || (schema.default != null && - typeof schema.default !== 'object' && - schema.default !== 'function') -} - -/** - * Format field label - * @param {Object} schema - vue-form-generator schema - * @param {string} fieldName - form field name - * @returns {string} - */ -function formatFieldLabel (schema, fieldName) { - if (schema.inputType === 'hidden') { - // Remove field label or return dot notion label - return '' - - // When the field is not hidden convert the field name to display the exact same way - // it is documented (dot notation), not the way it is referenced in the DOM or in code. - // This can be overridden in the field schema - } - - return schema.label || generateFieldLabel(fieldName) -} - -export default { - computed: { - entityId () { - return this.$route && this.$route.query && this.$route.query.entity_id - }, - }, - - methods: { - scrollToBottom () { - this.$nextTick(() => { - window.scrollTo({ - top: document.body.clientHeight - window.innerHeight, - left: 0, - behavior: 'smooth', - }) - }) - }, - - /** - * a list of form fields not to render across all entity forms - * @returns {Array} an array of form fields not to render across all entity forms - */ - getBlacklist () { - return ['created_at', 'updated_at', 'id'] - }, - - /** - * this method takes in a combined inputSchema and outputs an object containing a model and a schema for VFG form consumption - * @param {Object} backendSchema the schema obtained via the fetchSchema() method - * @param {Object} frontendSchema the schema defined in the custom js files - * @returns {Object} an object containing a formModel and formSchema, both of which will be consumed by the VFG form generator - */ - parseSchema (backendSchema, frontendSchema) { - let inputSchema = {} - if (backendSchema || this.schema) { - inputSchema = backendSchema || (this.schema.fields ? this.schema.fields : this.schema) - } - - const blacklist = this.getBlacklist().concat(this.schema ? this.schema.blacklist : []) - - const inputSchemaFields = Object.keys(inputSchema).filter(f => !blacklist.includes(f)) - - // Comparator function for comparing schema objects should not be added to fields - const comparatorIdx = inputSchemaFields.indexOf('comparator') - - comparatorIdx > -1 && inputSchemaFields.splice(comparatorIdx, 1) - - const formSchema = { fields: [] } - const formModel = {} - - // Iterate over each schema field to augment with display configuration. - inputSchemaFields.forEach(fieldName => { - this.buildFormSchema(inputSchema[fieldName], fieldName, inputSchema, formModel, formSchema, frontendSchema) - }) - - // Assume the fields are sorted, unless they have an `order` property - formSchema.fields.sort((a, b) => { - a.order = a.order || 0 - b.order = b.order || 0 - - return a.order - b.order - }) - - return { - schema: formSchema, - model: formModel, - options: { - noneSelectedText: 'Nothing Selected...', - helpAsHtml: true, - }, - } - }, - - /** - * This is a helper function for mergeSchema. It takes in a field and depending on the field type, - * sets the appropiate properties for it to be consumed by VFG (vue form generator) - * @param {Object} field the field coming from either the backend schema OR a recursive call to be merged with the frontend schema - * @param {string} key the current backend schema key we are looping over - * @param {Object} inputSchema this is the inputSchema we are building to be consumed later by parseSchema - * @param {Object} formModel VFG form model - * @param {Object} formSchema VFG form schema - * @param {Object} frontendSchema the schema coming from the frontend (if it exists) - * @returns {Object} an object representing the combined inputSchema - which will be used by mergeSchema as well as recursive calls - */ - // eslint-disable-next-line max-params - buildFormSchema (field, key, inputSchema, formModel, formSchema, frontendSchema) { - const fieldHasDefaultValue = setFieldDefaultValue(field, key) - - // Set default value should one exist, or empty string. Existing data - // will replace this later. - formModel[key] = fieldHasDefaultValue ? field.default : null - // Update model to be field name for reference within Vue - field.id = key - field.model = key - - if (field.fields && field.type === 'record') { - this.handleFieldRecord(field, key, inputSchema, formModel, formSchema, frontendSchema) - } else { - inputSchema[key] = field - this.fieldSchemaHandler(field, formModel) - if (frontendSchema && frontendSchema[key]) { - this.mergeFrontendSchema(inputSchema[key], frontendSchema[key]) - } - - // Set VFG form schema - formSchema.fields.push(field) - } - - return inputSchema - }, - - /** - * - * @param {*} backendSchemaVal backend schema key value - * @param {*} frontendSchemaVal frontend schema key value - */ - mergeFrontendSchema (backendSchemaVal, frontendSchemaVal) { - if (frontendSchemaVal) { - Object.assign(backendSchemaVal, frontendSchemaVal) - if (frontendSchemaVal.label) { - backendSchemaVal.label = frontendSchemaVal.label - } - } - - return backendSchemaVal - }, - - /** - * Remove id of dot notated foreign key if null - * @param {string} key - * @param {string} model - * @returns {string} - */ - unsetNullForeignKey (key, model) { - const keys = ['service.id', 'route.id', 'consumer.id'] - - if (keys.indexOf(key) > -1 && model[key] === null) { - delete model[key] - model[key.replace('.id', '')] = null - } - }, - - /** - * Catchall handler for Kong's schema fields coming from the Kong dao schema - * parsing. Handles things like inferring schema types and modifying the - * formModel to account for custom fields e.g. autosuggest, foreign, etc. - * @param {Object} schema vfg schema to potentially modify - * @param {Object} formModel vfg form model to potentially modify - */ - fieldSchemaHandler (schema, formModel) { - this.handleCustomFields(schema, formModel) - - switch (schema.type) { - case 'foreign': - this.handleFieldForeign(schema, formModel) - break - case 'number': - this.handleFieldNumber(schema) - schema.attributes = { step: 'any' } - break - case 'integer': - this.handleFieldNumber(schema) - break - case 'string': - this.handleFieldString(schema) - break - case 'set': - this.handleFieldSet(schema) - break - case 'boolean': - this.handleFieldBoolean(schema) - break - case 'map': - this.handleFieldMap(schema) - break - } - - // Set the field label - schema.label = formatFieldLabel(schema, schema.model) - }, - - /** - * Handles field of Kong's schema type "foreign" so VFG can understand - * @param {Object} schema vfg schema to modify - * @param {*} formModel vfg form model to modify - */ - handleFieldForeign (schema, formModel) { - schema.type = 'input' - schema.inputType = 'hidden' - schema.styleClasses = 'kong-form-hidden-field-wrapper' - const foreignKeyId = (this.entityId && this.entityId.split(',')[0]) || this.$route.params.id - - formModel[schema.model] = foreignKeyId ? { id: foreignKeyId } : null - }, - - handleFieldNumber (schema) { - schema.type = 'input' - schema.inputType = 'number' - - if (schema.between && schema.between.length === 2) { - schema.min = schema.between[0] - schema.max = schema.between[1] - } - }, - - handleFieldString (schema) { - if (Object.prototype.hasOwnProperty.call(schema, 'one_of')) { - schema.type = 'select' - schema.values = schema.one_of - schema.selectOptions = { - hideNoneSelectedText: true, - } - } else { - schema.type = 'input' - schema.inputType = 'text' - } - }, - - handleFieldSet (schema) { - schema.type = 'input' - schema.inputType = 'text' - schema.valueType = 'array' - schema.valueArrayType = 'string' - }, - - handleFieldBoolean (schema) { - schema.type = 'checkbox' - schema.default = true - schema.required = false - }, - - handleFieldMap (schema) { - schema.type = 'object-advanced' - - if (schema.values.type === 'array') { - const { type: elementsType } = schema.values.elements || {} - - schema.schema = { - fields: [{ - schema: { - fields: [{ - ...ArrayStringFieldSchema, - model: schema.model, - valueArrayType: elementsType === 'integer' ? 'number' : elementsType || 'string', - inputAttributes: { - ...ArrayStringFieldSchema.inputAttributes, - type: elementsType === 'integer' ? 'number' : 'text', - inputMode: elementsType === 'integer' ? 'numeric' : 'text', - }, - }], - }, - }], - } - } else if (schema.values.fields) { - schema.schema = { - fields: [ - { - type: 'object', - model: 'object', - schema: { - fields: schema.values.fields.map(f => { - const modelName = Object.keys(f)[0] - - return { - model: modelName, - type: 'input', - label: capitalize(modelName), - placeholder: modelName, - inputType: f[modelName].type, - } - }), - }, - }, - ], - } - } - }, - - // eslint-disable-next-line max-params - handleFieldRecord (field, key, inputSchema, formModel, formSchema, frontendSchema) { - // some fields have subfields inside of it which have their own arrays and keys - which requires a recursive call to setField - // in order to flatten them up to the top level inputSchema array for displaying - these will be unflattened later in the getModel() call when submitting - field.fields.forEach(topLevelSchema => { - Object.keys(topLevelSchema).forEach(propName => { - const subfield = topLevelSchema[propName] - - inputSchema = this.buildFormSchema(subfield, key + '-' + propName, inputSchema, formModel, formSchema, frontendSchema) - }) - }) - }, - - /** - * - * @param {Object} schema hard-coded schema from file - * @param {*} formModel VFG Form model - */ - handleCustomFields (schema, formModel) { - if (!customFields.includes(schema.model)) { - return - } - - const customFieldsSchema = buildCustomFields(schema) - - customFields.forEach(field => { - Object.keys(customFieldsSchema).forEach(field => { - const fieldHasDefaultValue = setFieldDefaultValue(customFieldsSchema[field], field) - - formModel[field] = fieldHasDefaultValue ? customFieldsSchema[field].default : null - }) - delete formModel[field] - }) - }, - }, -} diff --git a/src/components/EntityForm/mixins/FormActionsMixin.js b/src/components/EntityForm/mixins/FormActionsMixin.js deleted file mode 100644 index 6cd75c4..0000000 --- a/src/components/EntityForm/mixins/FormActionsMixin.js +++ /dev/null @@ -1,30 +0,0 @@ -export default { - data () { - return { - isSaveActionDisabled: false, - } - }, - - computed: { - formRef () { - return this.$refs.form - }, - }, - - mounted () { - this.isSaveActionDisabled = this.formRef.isSaveActionDisabled - this.$watch('formRef.isSaveActionDisabled', (disabled) => { - this.isSaveActionDisabled = disabled - }) - }, - - methods: { - handleFormSave () { - return this.formRef.confirm() - }, - - handleFormCancel () { - return this.formRef.cancel() - }, - }, -} diff --git a/src/components/EntityForm/mixins/FormPage.js b/src/components/EntityForm/mixins/FormPage.js deleted file mode 100644 index ca5959c..0000000 --- a/src/components/EntityForm/mixins/FormPage.js +++ /dev/null @@ -1,137 +0,0 @@ -/** - Default form page mixins - - Requires: - - `resourceEndpoint` - resource endpoint - - Optional: - - `id` - resource identifier - - Provides: - - `isEditing` - `true` when `id` exists - - `redirectRoute` - route to redirect to, defaults to `{ name: }`. - - `buttonText` - `Update` or `Create` depending on `isEditing` - - `onFormLoad` - handler for `onLoad` callback of `EntityForm` - - `onFormSubmit` - handler for `onSubmit` callback of `EntityForm` - - `getRecord` - method to retrieve resource record - - `createRecord` - method to create a resource record - - `updateRecord` - method to update a resource record - - `transformRecord` - method to transform a resource record before update and create -*/ -import { redirectOnResponseStatus } from '../helpers' -import { apiService } from '@/services/apiService' - -export default { - computed: { - $_redirectOnSubmit () { - return this.redirectOnSubmit !== undefined ? this.redirectOnSubmit : true - }, - - isEditing () { - return !!this.id - }, - - buttonText () { - return this.isEditing ? 'Save' : 'Create' - }, - - /** - * @returns {RawLocation|-1} returns a vue router location or -1 indicating - * that the router should go back, e.g. $router.go(-1) - */ - redirectRoute () { - if (this.redirectPath) { - return this.redirectPath - } - - return { name: this.resourceEndpoint } - }, - }, - - methods: { - onFormLoad () { - return Promise.resolve(this.id ? this.getRecord() : false) - .catch(redirectOnResponseStatus(this.$router, 404, '/404', { replace: true })) - }, - - async onFormSubmit (model, callback = this.redirectRoute) { - const record = this.transformRecord(model) - - return await this.isEditing - ? this.updateRecord(record) - : this.createRecord(record, callback) - }, - - getRecord () { - return apiService.findRecord(this.resourceEndpoint, this.id) - }, - - /** - * @param {Object} model - form model - * @param {RawLocation|Function} callback - vue router location or function - * to calculate the vue router location from the response - */ - async createRecord (model, callback) { - return apiService.createRecord(this.resourceEndpoint, model) - .then(res => { - if (this.hideSubmit) { - return res.data - } - - this.$emit('submit', res.data) - - // if the parent form passes in redirectOnSubmit = false, then we don't do anything after creation - if (!this.$_redirectOnSubmit) { - return null - } - - // if parent form defines a redirectRouteNames object, we either go back to previous page if -1 is passed - // else we go to the named route - const redirectCreateRoute = this.redirectRouteNames && this.redirectRouteNames.create - if (redirectCreateRoute) { - return redirectCreateRoute === '-1' ? this.$router.go(-1) : this.$router.push({ name: redirectCreateRoute, params: this.$router.params }) - } - - const link = this.redirectPath || this.returnLink - - if (link) { - return this.$router.push(link) - } - - const location = typeof callback === 'function' - ? callback(model, res.data) - : callback - - redirectOnResponseStatus(this.$router, 201, location)(res) - - return res.data - }) - }, - - async updateRecord (model) { - return apiService.updateRecord(this.resourceEndpoint, this.id, model) - .then(res => { - const link = this.redirectPath || this.returnLink - - // if parent form defines a redirectRouteNames object, we either go back to previous page if -1 is passed - // else we go to the named route - const redirectUpdateRoute = this.redirectRouteNames && this.redirectRouteNames.update - if (redirectUpdateRoute) { - return redirectUpdateRoute === '-1' ? this.$router.go(-1) : this.$router.push({ name: redirectUpdateRoute, params: this.$router.params }) - } - - if (link) { - return this.$router.push(link) - } - - redirectOnResponseStatus(this.$router, 200, { name: this.resourceEndpoint })(res) - - return res.data - }) - }, - - transformRecord (model) { - return model - }, - }, -} diff --git a/src/components/EntityForm/mixins/RedirectMixin.js b/src/components/EntityForm/mixins/RedirectMixin.js deleted file mode 100644 index f83db76..0000000 --- a/src/components/EntityForm/mixins/RedirectMixin.js +++ /dev/null @@ -1,58 +0,0 @@ -const extractRedirectPath = ($route, urlKey) => { - const query = $route.query[urlKey] - let url = null - - if (Array.isArray(query)) { - url = query[0] - console.warn(`extractRedirectPath: "${urlKey}" in query should not be an array, using first element this time`) - } else if (typeof query === 'string') { - url = query - } - - return url -} - -export default { - computed: { - redirectPath () { - return extractRedirectPath(this.$route, 'redirect') - }, - redirectRouteQuery () { - return { redirect: this.redirectPath } - }, - postDeletePath () { - return extractRedirectPath(this.$route, 'postDelete') - }, - postDeleteRouteQuery () { - return { postDelete: this.postDeletePath } - }, - }, - methods: { - createRedirectRouteQuery (redirect = this.$route.fullPath) { - return { redirect } - }, - createPostDeleteRouteQuery (postDelete) { - if (typeof postDelete !== 'string') { - postDelete = this.postDeletePath ?? this.$route.fullPath - } - - return { postDelete } - }, - redirect (replace = false, goBack = false) { - const routerFn = replace ? this.$router.replace : this.$router.push - if (this.redirectPath) { - routerFn(this.redirectPath) - - return true - } - - if (goBack) { - this.$router.go(-1) - - return true - } - - return false - }, - }, -} diff --git a/src/pages/consumers/CredentialForm.vue b/src/pages/consumers/CredentialForm.vue index 17c8623..55ffe54 100644 --- a/src/pages/consumers/CredentialForm.vue +++ b/src/pages/consumers/CredentialForm.vue @@ -1,47 +1,62 @@ - - + -const createRecord = async (model) => { - return apiService - .post(resourceEndpoint.value, model) - .then(onSuccess) -} - -const updateRecord = async (model) => { - return apiService - .patch(`${resourceEndpoint.value}/${credentialId.value}`, model) - .then(onSuccess) -} - -const onSubmit = (model) => { - return isEditing.value ? updateRecord(model) : createRecord(model) + diff --git a/src/pages/consumers/CredentialPlugins.ts b/src/pages/consumers/CredentialPlugins.ts deleted file mode 100644 index 55cf57d..0000000 --- a/src/pages/consumers/CredentialPlugins.ts +++ /dev/null @@ -1,45 +0,0 @@ -import ACLSchema from '@/schemas/ACL' -import BasicAuthSchema from '@/schemas/BasicAuth' -import KeyAuthSchema from '@/schemas/KeyAuth' -import KeyAuthEncSchema from '@/schemas/KeyAuthEnc' -import OAuth2Schema from '@/schemas/OAuth2' -import HMACSchema from '@/schemas/HMAC' -import JWTSchema from '@/schemas/JWT' - -export default { - 'acl': { - schema: ACLSchema, - endpoint: '/acls', - schemaEndpoint: 'acls', - }, - 'basic-auth': { - schema: BasicAuthSchema, - endpoint: '/basic-auth', - schemaEndpoint: 'basicauth_credentials', - }, - 'key-auth': { - schema: KeyAuthSchema, - endpoint: '/key-auth', - schemaEndpoint: 'keyauth_credentials', - }, - 'key-auth-enc': { - schema: KeyAuthEncSchema, - endpoint: '/key-auth-enc', - schemaEndpoint: 'keyauth_enc_credentials', - }, - 'oauth2': { - schema: OAuth2Schema, - endpoint: '/oauth2', - schemaEndpoint: 'oauth2_credentials', - }, - 'hmac-auth': { - schema: HMACSchema, - endpoint: '/hmac-auth', - schemaEndpoint: 'hmacauth_credentials', - }, - 'jwt': { - schema: JWTSchema, - endpoint: '/jwt', - schemaEndpoint: 'jwt_secrets', - }, -} diff --git a/src/pages/plugins/Detail.vue b/src/pages/plugins/Detail.vue index 3d6d9e3..886007d 100644 --- a/src/pages/plugins/Detail.vue +++ b/src/pages/plugins/Detail.vue @@ -14,17 +14,16 @@ class="button-edit" entity="plugin" :route-options="{ - query: { - entity_type: route.query?.entity_type, - entity_id: route.query?.entity_id, - }, + query: entityScope ? { + [entityScope.keyInQuery]: entityScope.id, + } : undefined, }" /> @@ -47,19 +46,28 @@ const { t } = useI18n() const id = computed(() => (route.params.id as string) ?? '') const pluginType = computed(() => (route.params.pluginType ?? '') as string) -const entityType = computed(() => { - if (!route.query?.entity_type) { - return undefined +const entityScope = computed(() => { + if (route.query.serviceId) { + return { + id: route.query.serviceId as string, + typeLiteral: 'services', + keyInQuery: 'serviceId', + } + } else if (route.query.routeId) { + return { + id: route.query.routeId as string, + typeLiteral: 'routes', + keyInQuery: 'routeId', + } + } else if (route.query.consumerId) { + return { + id: route.query.consumerId as string, + typeLiteral: 'consumers', + keyInQuery: 'consumerId', + } } - return `${(route.query.entity_type as string).split('_')[0]}s` -}) -const entityId = computed(() => { - if (!route.query?.entity_id) { - return undefined - } - - return route.query.entity_id as string + return null }) const pluginDetailConfig = reactive({ diff --git a/src/pages/plugins/Form.vue b/src/pages/plugins/Form.vue index 5524acd..7157aec 100644 --- a/src/pages/plugins/Form.vue +++ b/src/pages/plugins/Form.vue @@ -1,781 +1,125 @@ - - - - - - - - - - - - - - Error: Something went wrong - - - - - - - - - - - {{ lang.DISABLED_TEXT }} - - - - Visit documentation - - - - + + + View documentation + + + - - diff --git a/src/pages/plugins/List.vue b/src/pages/plugins/List.vue index c1e7f5c..6c72b26 100644 --- a/src/pages/plugins/List.vue +++ b/src/pages/plugins/List.vue @@ -55,11 +55,11 @@ const entityType = computed(() => route.meta?.scopedIn as ScopedEntityType) const scopedQuery = computed(() => { switch (entityType.value) { case 'services': - return { entity_type: 'service_id', entity_id: route.params?.id } + return { serviceId: route.params?.id } case 'routes': - return { entity_type: 'route_id', entity_id: route.params?.id } + return { routeId: route.params?.id } case 'consumers': - return { entity_type: 'consumer_id', entity_id: route.params?.id } + return { consumerId: route.params?.id } default: return {} } diff --git a/src/pages/plugins/Select.vue b/src/pages/plugins/Select.vue index e3feea5..3bab23b 100644 --- a/src/pages/plugins/Select.vue +++ b/src/pages/plugins/Select.vue @@ -17,29 +17,31 @@ defineOptions({ const route = useRoute() -const entityType = computed(() => { - const entityTypeInRoute = route.query.entity_type ?? '' - - switch (entityTypeInRoute) { - case 'service_id': - return 'services' - case 'route_id': - return 'routes' - case 'consumer_id': - return 'consumers' - case 'consumer_group_id': - return 'consumer_groups' - default: - return undefined +const entityScope = computed(() => { + if (route.query.serviceId) { + return { + id: route.query.serviceId as string, + typeLiteral: 'services', + } as const + } else if (route.query.routeId) { + return { + id: route.query.routeId as string, + typeLiteral: 'routes', + } as const + } else if (route.query.consumerId) { + return { + id: route.query.consumerId as string, + typeLiteral: 'consumers', + } as const } -}) -const entityId = computed(() => (route.query.entity_id ?? '') as string) + return null +}) const config = reactive({ ...toRefs(useBaseGeneralConfig()), - entityType, - entityId, + entityType: computed(() => entityScope.value?.typeLiteral), + entityId: computed(() => entityScope.value?.id), getCreateRoute: (plugin: string) => ({ name: 'plugin-create', params: { diff --git a/src/schemas/ACL.ts b/src/schemas/ACL.ts deleted file mode 100644 index 4cbc215..0000000 --- a/src/schemas/ACL.ts +++ /dev/null @@ -1,9 +0,0 @@ -export default { - fields: [ - { - group: { - hint: 'The arbitrary group name to associate to the consumer.', - }, - }, - ], -} diff --git a/src/schemas/ApplicationRegistration.ts b/src/schemas/ApplicationRegistration.ts deleted file mode 100644 index d82b382..0000000 --- a/src/schemas/ApplicationRegistration.ts +++ /dev/null @@ -1,36 +0,0 @@ -import typedefs from './typedefs' - -// Note: unlike most plugin schemas this schema is not modify the config fields, -// but rather the defaultFormSchema set in pages/Plugins/Form.vue -export default { - 'enabled': { - type: 'switch', - model: 'enabled', - label: 'Enabled', - textOn: 'This plugin is Enabled', - textOff: 'This plugin is Disabled', - inputType: 'hidden', - styleClasses: 'field-switch top-border bottom-border hide-label', - default: true, - }, - 'name': { - default: 'application-registration', - type: 'input', - inputType: 'hidden', - styleClasses: 'kong-form-hidden-field-wrapper', - }, - 'service-id': { - type: 'AutoSuggest', - label: 'Gateway Service', - styleClasses: 'bottom-border', - description: 'Specific Gateway Service in this workspace', - model: 'service-id', - entity: 'services', - placeholder: 'Select a Gateway Service', - inputValues: { - fields: ['name', 'id'], - }, - help: 'The Gateway Service that this plugin configuration will target', - }, - 'tags': typedefs.tags, -} diff --git a/src/schemas/ArrayCardContainerFields.ts b/src/schemas/ArrayCardContainerFields.ts deleted file mode 100644 index c7fe526..0000000 --- a/src/schemas/ArrayCardContainerFields.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default { - type: 'array', - showRemoveButton: false, - newElementButtonLabelClasses: 'kong-form-new-element-button-label', - itemContainerComponent: 'FieldArrayCardContainer', - fieldClasses: 'array-card-container-wrapper', -} diff --git a/src/schemas/BasicAuth.ts b/src/schemas/BasicAuth.ts deleted file mode 100644 index fc39347..0000000 --- a/src/schemas/BasicAuth.ts +++ /dev/null @@ -1,13 +0,0 @@ -export default { - fields: [ - { - username: {}, - }, - - { - password: { - inputType: 'password', - }, - }, - ], -} diff --git a/src/schemas/CustomSchemas.ts b/src/schemas/CustomSchemas.ts deleted file mode 100644 index f441d90..0000000 --- a/src/schemas/CustomSchemas.ts +++ /dev/null @@ -1,83 +0,0 @@ -import ApplicationRegistrationSchema from './ApplicationRegistration' -import DataDogSchema from './Datadog' -import StatsDSchema from './StatsD' -import StatsDAdvancedSchema from './StatsDAdvanced' -import KafkaSchema from './Kafka' -import MockingSchema from './Mocking' -import RateLimitingSchema from './RateLimiting' -import RequestValidatorSchema from './RequestValidator' -import ZipkinSchema from './Zipkin' - -export default { - 'application-registration': { - overwriteDefault: true, - formSchema: { - ...ApplicationRegistrationSchema, - }, - }, - - 'datadog': { - ...DataDogSchema, - }, - - 'upstream-tls': { - 'config-trusted_certificates': { - type: 'textArea', - valueType: 'array', - rows: 4, - help: 'A comma separated list of certificate values', - }, - }, - - 'kafka-upstream': { - ...KafkaSchema, - }, - - 'kafka-log': { - ...KafkaSchema, - }, - - 'statsd': { - ...StatsDSchema, - }, - - 'statsd-advanced': { - ...StatsDAdvancedSchema, - }, - - 'route-by-header': { - configurationDisabled: true, - }, - - 'mocking': { - ...MockingSchema, - }, - - 'rate-limiting': { - useKonnectSchema: true, - ...RateLimitingSchema, - }, - - 'rate-limiting-advanced': { - useKonnectSchema: true, - ...RateLimitingSchema, - }, - - 'graphql-rate-limiting-advanced': { - useKonnectSchema: true, - ...RateLimitingSchema, - }, - - 'response-ratelimiting': { - useKonnectSchema: true, - ...RateLimitingSchema, - }, - - 'request-validator': { - ...RequestValidatorSchema, - }, - - 'zipkin': { - ...ZipkinSchema, - }, -} diff --git a/src/schemas/Datadog.ts b/src/schemas/Datadog.ts deleted file mode 100644 index 9e4b1ec..0000000 --- a/src/schemas/Datadog.ts +++ /dev/null @@ -1,59 +0,0 @@ -import * as _ from 'lodash-es' -import { ArrayStringFieldSchema } from '@/components/EntityForm/fields' -import MetricFields from './MetricFields' - -export default { - 'config-metrics': { - ...MetricFields, - items: { - type: 'object', - default: () => ({}), - schema: { - fields: [{ - model: 'name', - label: 'Name', - type: 'select', - values: [ - 'request_count', - 'request_size', - 'response_size', - 'latency', - 'upstream_latency', - 'kong_latency', - ], - }, { - model: 'stat_type', - label: 'Stat Type', - type: 'select', - values: [ - 'gauge', - 'timer', - 'counter', - 'histogram', - 'meter', - 'set', - ], - }, { - model: 'sample_rate', - label: 'Sample Rate', - type: 'input', - inputType: 'number', - id: 'sample_rate', - }, { - model: 'consumer_identifier', - label: 'Consumer Identifier', - type: 'select', - values: [ - 'consumer_id', - 'custom_id', - 'username', - ], - }, { - model: 'tags', - label: 'Tags', - ..._.cloneDeep(ArrayStringFieldSchema), - }], - }, - }, - }, -} diff --git a/src/schemas/HMAC.ts b/src/schemas/HMAC.ts deleted file mode 100644 index 3100379..0000000 --- a/src/schemas/HMAC.ts +++ /dev/null @@ -1,18 +0,0 @@ -export default { - fields: [ - { - username: { - hint: 'The username to use in the HMAC Signature verification.', - }, - }, - { - secret: { - inputType: 'password', - submitWhenNull: false, - hint: `The secret to use in the HMAC Signature verification. Note that - if this parameter isn't provided, Kong will generate a value for you and - send it as part of the response body.`, - }, - }, - ], -} diff --git a/src/schemas/JWT.ts b/src/schemas/JWT.ts deleted file mode 100644 index 03522ab..0000000 --- a/src/schemas/JWT.ts +++ /dev/null @@ -1,36 +0,0 @@ -export default { - fields: [ - { - key: { - submitWhenNull: false, - hint: `A unique string identifying the credential. If left out, it will - be auto-generated.`, - }, - }, - { - algorithm: { - order: 1, - hint: `The algorithm used to verify the token's signature.`, - }, - }, - { - rsa_public_key: { - order: 2, - type: 'textArea', - label: 'RSA public-key', - hint: `If algorithm is RS256 or ES256, the public key (in PEM format) to - use to verify the token's signature.`, - rows: 10, - visible: (model) => { - return model && model.algorithm && /(ES|RS)[\d]{3}/.test(model.algorithm) - }, - }, - }, - { - secret: { - inputType: 'password', - hint: `If algorithm is HS256 or ES256, the secret used to sign JWTs for - this credential. If left out, will be auto-generated.`, - }, - }], -} diff --git a/src/schemas/Kafka.ts b/src/schemas/Kafka.ts deleted file mode 100644 index 8737ceb..0000000 --- a/src/schemas/Kafka.ts +++ /dev/null @@ -1,27 +0,0 @@ -import ArrayCardContainerFields from './ArrayCardContainerFields' - -export default { - 'config-bootstrap_servers': { - ...ArrayCardContainerFields, - newElementButtonLabel: '+ Add Bootstrap Server', - label: 'Bootstrap Server(s)', - placeholder: 'Enter a Bootstrap Server', - help: 'A list of bootstrap servers', - items: { - type: 'object', - schema: { - fields: [{ - label: 'Host', - model: 'host', - type: 'input', - inputType: 'text', - }, { - label: 'Port', - model: 'port', - type: 'input', - inputType: 'number', - }], - }, - }, - }, -} diff --git a/src/schemas/KeyAuth.ts b/src/schemas/KeyAuth.ts deleted file mode 100644 index 1252bb0..0000000 --- a/src/schemas/KeyAuth.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default { - fields: [ - { - key: { - submitWhenNull: false, - hint: `You can optionally set your own unique key to authenticate the - client. If missing, it will be generated for you.`, - }, - }, - ], -} diff --git a/src/schemas/KeyAuthEnc.ts b/src/schemas/KeyAuthEnc.ts deleted file mode 100644 index aadfa42..0000000 --- a/src/schemas/KeyAuthEnc.ts +++ /dev/null @@ -1,13 +0,0 @@ -export default { - fields: [ - { - key: { - submitWhenNull: false, - hint: `You can optionally set your own unique key to authenticate the - client. If missing, it will be generated for you.`, - inputType: 'password', - encrypted: true, - }, - }, - ], -} diff --git a/src/schemas/MetricFields.ts b/src/schemas/MetricFields.ts deleted file mode 100644 index accd4f1..0000000 --- a/src/schemas/MetricFields.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default { - type: 'array', - showRemoveButton: false, - newElementButtonLabelClasses: 'kong-form-new-element-button-label', - itemContainerComponent: 'FieldMetric', - fieldClasses: 'metrics-wrapper', -} diff --git a/src/schemas/Mocking.ts b/src/schemas/Mocking.ts deleted file mode 100644 index 3ac29ed..0000000 --- a/src/schemas/Mocking.ts +++ /dev/null @@ -1,8 +0,0 @@ -export default { - 'config-api_specification': { - label: 'Config.API Specification', - placeholder: 'Enter an API spec', - type: 'textArea', - rows: 10, - }, -} diff --git a/src/schemas/OAuth2.ts b/src/schemas/OAuth2.ts deleted file mode 100644 index 601cbf7..0000000 --- a/src/schemas/OAuth2.ts +++ /dev/null @@ -1,36 +0,0 @@ -import typedefs from './typedefs' - -export default { - fields: [ - { name: {} }, - { - client_id: { - submitWhenNull: false, - hint: `You can optionally set your own unique client_id. If missing, it - will be generated for you.`, - inputType: 'password', - encrypted: true, - }, - }, - { - client_secret: { - submitWhenNull: false, - hint: `You can optionally set your own unique client_secret. If missing, - it will be generated for you.`, - inputType: 'password', - encrypted: true, - }, - }, - { - redirect_uris: typedefs.fields.arrayItems({ - label: 'Redirect URI(s)', - hint: `One or more URLs in your app where users will be sent after - authorization (RFC 6742 Section 3.1.2)`, - newElementButtonLabel: '+ Add Redirect URI', - inputAttributes: { - placeholder: 'Enter URI', - }, - }), - }, - ], -} diff --git a/src/schemas/RateLimiting.ts b/src/schemas/RateLimiting.ts deleted file mode 100644 index 13e436d..0000000 --- a/src/schemas/RateLimiting.ts +++ /dev/null @@ -1,14 +0,0 @@ -export default { - 'config-policy': { - label: 'Config.Policy', - type: 'select', - default: 'redis', - values: ['local', 'redis'], - }, - 'config-strategy': { - label: 'Config.Strategy', - type: 'select', - default: 'redis', - values: ['local', 'redis'], - }, -} diff --git a/src/schemas/RequestValidator.ts b/src/schemas/RequestValidator.ts deleted file mode 100644 index 1d6d8f0..0000000 --- a/src/schemas/RequestValidator.ts +++ /dev/null @@ -1,64 +0,0 @@ -import MetricFields from './MetricFields' - -export default { - 'config-parameter_schema': { - ...MetricFields, - items: { - type: 'object', - default: () => ({ - required: false, - explode: false, - }), - schema: { - fields: [ - { - model: 'in', - label: 'in', - type: 'select', - values: ['query', 'header', 'path'], - }, - { - model: 'name', - label: 'name', - type: 'input', - inputType: 'string', - id: 'schema_name', - }, - { - model: 'required', - label: 'required', - type: 'switch', - id: 'schema_required', - }, - { - model: 'style', - label: 'style', - type: 'select', - values: [ - 'label', - 'form', - 'matrix', - 'simple', - 'spaceDelimited', - 'pipeDelimited', - 'deepObject', - ], - }, - { - model: 'explode', - label: 'explode', - type: 'switch', - id: 'schema_explode', - }, - { - model: 'schema', - label: 'schema', - type: 'input', - inputType: 'string', - id: 'schema_schema', - }, - ], - }, - }, - }, -} diff --git a/src/schemas/StatsD.ts b/src/schemas/StatsD.ts deleted file mode 100644 index a5c27f6..0000000 --- a/src/schemas/StatsD.ts +++ /dev/null @@ -1,78 +0,0 @@ -import MetricFields from './MetricFields' - -export default { - 'config-metrics': { - ...MetricFields, - items: { - type: 'object', - default: () => ({}), - schema: { - fields: [{ - model: 'name', - label: 'Name', - type: 'select', - values: [ - 'request_count', - 'request_size', - 'response_size', - 'latency', - 'status_count', - 'unique_users', - 'request_per_user', - 'upstream_latency', - 'kong_latency', - 'status_count_per_user', - 'status_count_per_user_per_route', - 'status_count_per_workspace', - 'shdict_usage', - ], - }, { - model: 'stat_type', - label: 'Stat Type', - type: 'select', - values: [ - 'gauge', - 'timer', - 'counter', - 'histogram', - 'meter', - 'set', - ], - }, { - model: 'sample_rate', - label: 'Sample Rate', - type: 'input', - inputType: 'number', - id: 'sample_rate', - }, { - model: 'consumer_identifier', - label: 'Consumer Identifier', - type: 'select', - values: [ - 'consumer_id', - 'custom_id', - 'username', - ], - }, { - model: 'service_identifier', - label: 'Service Identifier', - type: 'select', - values: [ - 'service_id', - 'service_name', - 'service_host', - 'service_name_or_host', - ], - }, { - model: 'workspace_identifier', - label: 'Workspace Identifier', - type: 'select', - values: [ - 'workspace_id', - 'workspace_name', - ], - }], - }, - }, - }, -} diff --git a/src/schemas/StatsDAdvanced.ts b/src/schemas/StatsDAdvanced.ts deleted file mode 100644 index 62d316b..0000000 --- a/src/schemas/StatsDAdvanced.ts +++ /dev/null @@ -1,88 +0,0 @@ -import MetricFields from './MetricFields' - -export default { - 'config-allow_status_codes': { - type: 'input', - inputType: 'text', - valueArrayType: 'string', - valueType: 'array', - label: 'config.allow_status_codes', - help: 'A list of status code ranges, separated by commas. e.g. 200-204, 500-505', - }, - 'config-metrics': { - ...MetricFields, - items: { - type: 'object', - default: () => ({}), - schema: { - fields: [{ - model: 'name', - label: 'Name', - type: 'select', - values: [ - 'request_count', - 'request_size', - 'response_size', - 'latency', - 'status_count', - 'unique_users', - 'request_per_user', - 'upstream_latency', - 'kong_latency', - 'status_count_per_user', - 'status_count_per_user_per_route', - 'status_count_per_workspace', - 'shdict_usage', - 'cache_datastore_hits_total', - 'cache_datastore_misses_total', - ], - }, { - model: 'stat_type', - label: 'Stat Type', - type: 'select', - values: [ - 'gauge', - 'timer', - 'counter', - 'histogram', - 'meter', - 'set', - ], - }, { - model: 'sample_rate', - label: 'Sample Rate', - type: 'input', - inputType: 'number', - id: 'sample_rate', - }, { - model: 'consumer_identifier', - label: 'Consumer Identifier', - type: 'select', - values: [ - 'consumer_id', - 'custom_id', - 'username', - ], - }, { - model: 'service_identifier', - label: 'Service Identifier', - type: 'select', - values: [ - 'service_id', - 'service_name', - 'service_host', - 'service_name_or_host', - ], - }, { - model: 'workspace_identifier', - label: 'Workspace Identifier', - type: 'select', - values: [ - 'workspace_id', - 'workspace_name', - ], - }], - }, - }, - }, -} diff --git a/src/schemas/Zipkin.ts b/src/schemas/Zipkin.ts deleted file mode 100644 index 7e8eb8a..0000000 --- a/src/schemas/Zipkin.ts +++ /dev/null @@ -1,29 +0,0 @@ -import MetricFields from './MetricFields' - -export default { - 'config-static_tags': { - ...MetricFields, - items: { - type: 'object', - default: () => ({}), - schema: { - fields: [ - { - model: 'name', - label: 'name', - type: 'input', - inputType: 'string', - id: 'schema_name', - }, - { - model: 'value', - label: 'value', - type: 'input', - inputType: 'string', - id: 'schema_value', - }, - ], - }, - }, - }, -} diff --git a/src/schemas/typedefs.ts b/src/schemas/typedefs.ts deleted file mode 100644 index 381419f..0000000 --- a/src/schemas/typedefs.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Get fields to for displaying in table from schema - * @param {fields} schema Credential schema from schemas/*.js - */ -export const getColumnFields = schema => { - const fields = Object.fromEntries(schema.fields.map(o => Object.entries(o)[0])) - - // Add `id` field to the last if not exist since this affects the ordering of - // the fields in the table head. - if (!('id' in fields)) fields.id = {} - - return fields -} - -export const tags = { - 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', -} - -export const id = { - label: 'ID', -} - -export const enabled = { - label: 'Enabled', - indexed: true, - type: 'boolean', -} - -export const created_at = { - label: 'Created', -} - -export const updated_at = { - label: 'Last updated', -} - -export const fields = { - arrayItems (item) { - const inputAttributes = item.inputAttributes || {} - - delete item.inputAttributes - - return { - type: 'array', - itemContainerComponent: 'FieldArrayItem', - fieldClasses: 'kong-form-array-field', - fieldItemsClasses: 'kong-form-array-field-item', - newElementButtonLabelClasses: 'kong-form-new-element-button-label', - inputAttributes: { class: 'form-control', style: { minWidth: '200px' }, ...inputAttributes }, - removeElementButtonLabel: 'remove', - styleClasses: 'kong-form-field-wrapper', - inputType: 'text', - valueType: 'array', - valueArrayType: 'string', - ...item, - } - }, -} - -export default { - getColumnFields, - tags, - id, - enabled, - created_at, - updated_at, - fields, -} diff --git a/src/styles/inputs.scss b/src/styles/inputs.scss index e7569c9..5394c36 100644 --- a/src/styles/inputs.scss +++ b/src/styles/inputs.scss @@ -59,10 +59,6 @@ select.form-control, } } -.form-group.required > label:after { - display: none; -} - .form-group.optional > label, .form-group.semi-optional > label { position: relative; @@ -134,11 +130,6 @@ select.form-control, margin-right: 0.5rem; } -.form-group.required input[type="checkbox"], -.form-group [required="required"] { - box-shadow: none; -} - /* Override Bootstrap & allow k-button to style hrefs */ diff --git a/tests/playwright/pages/index.ts b/tests/playwright/pages/index.ts index fe1769b..b440f48 100644 --- a/tests/playwright/pages/index.ts +++ b/tests/playwright/pages/index.ts @@ -4,7 +4,7 @@ import type { Page } from '@playwright/test' export class POM { public static $ = { success: '.k-alert.success', - submitButton: '[data-testid="form-footer-actions"] .k-button.primary', + submitButton: '[data-testid="form-actions"] .k-button.primary', } constructor (public readonly page: Page, public readonly url: string) { } diff --git a/tests/playwright/specs/consumers/02-ConsumerCredentials.spec.ts b/tests/playwright/specs/consumers/02-ConsumerCredentials.spec.ts index 55ebb87..e58a801 100644 --- a/tests/playwright/specs/consumers/02-ConsumerCredentials.spec.ts +++ b/tests/playwright/specs/consumers/02-ConsumerCredentials.spec.ts @@ -42,7 +42,7 @@ test.describe('consumer credentials', () => { await basicAuthLocator.locator('[data-testid="new-basic-auth-credential"]').click() await page.locator('#username').fill(mockCredential) await page.locator('#password').fill(mockCredentialPassword) - await page.locator('[data-testid="form-footer-actions"] .primary').click() + await page.locator('[data-testid="form-actions"] .primary').click() await expect(basicAuthLocator.locator('table [data-testid="username"]')).toContainText(mockCredential) }) diff --git a/tests/playwright/specs/consumers/03-ConsumerPlugins.spec.ts b/tests/playwright/specs/consumers/03-ConsumerPlugins.spec.ts index 475bd22..3141a79 100644 --- a/tests/playwright/specs/consumers/03-ConsumerPlugins.spec.ts +++ b/tests/playwright/specs/consumers/03-ConsumerPlugins.spec.ts @@ -50,7 +50,7 @@ test.describe('consumer plugins', () => { await expect(page.locator('.autosuggest #consumer-id')).toHaveValue(new RegExp(`${mockConsumerName}\\s*-\\s*${uuid}`)) await withNavigation( page, - async () => await page.locator('.entity-form [data-testid="form-footer-actions"] .primary').click() + async () => await page.locator('[data-testid="form-actions"] .primary').click() ) }) @@ -61,11 +61,7 @@ test.describe('consumer plugins', () => { await page.locator('#tags').fill(mockTag) await withNavigation( page, - async () => { - await page.locator('.entity-form [data-testid="form-footer-actions"] .primary').click() - await expect(page.locator('.k-modal-dialog.modal-dialog')).toBeVisible() - await page.locator('.k-prompt-action-buttons .primary').click() - } + () => page.locator('[data-testid="form-actions"] .primary').click(), ) await expect(page.locator('.k-table [data-testid="tags"]')).toHaveText(mockTag) @@ -73,7 +69,7 @@ test.describe('consumer plugins', () => { await page.locator('#tags').fill(`${mockTag}${mockTag}`) await withNavigation( page, - async () => await page.locator('.entity-form [data-testid="form-footer-action-cancel"]').click() + async () => await page.locator('[data-testid="form-cancel"]').click() ) await expect(page.locator('.k-table [data-testid="tags"]')).toHaveText(mockTag) }) @@ -92,25 +88,24 @@ test.describe('consumer plugins', () => { await pluginListPage.goto() await withNavigation(page, async () => await page.locator('.kong-ui-entities-plugins-list [data-testid="new-plugin"]').click()) await page.locator('[data-testid="Rate Limiting"]').click() - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await page.locator('#config-second').fill('30') await withNavigation( page, - async () => await page.locator('.entity-form [data-testid="form-footer-actions"] .primary').click() + async () => await page.locator('[data-testid="form-actions"] .primary').click() ) await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Global') // Update plugin and scope it to consumer await withNavigation(page, () => clickEntityListAction(page, 'edit')) - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await page.click('.selection-group .Scoped-check') await page.click('#consumer-id') await page.fill('#consumer-id', mockConsumerName) await page.waitForTimeout(300) await expect(page.locator('.k-select-item')).toContainText(mockConsumerName) await page.click('.k-select-item') - await page.click(consumerListPage.$.submitButton) - await withNavigation(page, async () => await page.click('.k-modal .k-modal-footer .k-button.primary')) + await withNavigation(page, () => page.click(consumerListPage.$.submitButton)) await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Consumer') }) @@ -122,10 +117,9 @@ test.describe('consumer plugins', () => { await pluginListPage.goto() await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Consumer') await withNavigation(page, () => clickEntityListAction(page, 'edit')) - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await page.click('.selection-group .Global-check') - await page.click(consumerListPage.$.submitButton) - await withNavigation(page, async () => await page.click('.k-modal .k-modal-footer .k-button.primary')) + await withNavigation(page, () => page.click(consumerListPage.$.submitButton)) await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Global') }) }) diff --git a/tests/playwright/specs/plugins/01-Plugins.spec.ts b/tests/playwright/specs/plugins/01-Plugins.spec.ts index 8475be1..9635948 100644 --- a/tests/playwright/specs/plugins/01-Plugins.spec.ts +++ b/tests/playwright/specs/plugins/01-Plugins.spec.ts @@ -91,7 +91,7 @@ test.describe('plugins', () => { await withNavigation( page, - async () => await page.locator('.plugin-form [data-testid="form-footer-actions"] .primary').click() + async () => await page.locator('[data-testid="form-actions"] .primary').click() ) await expect(page.locator('.k-table tbody tr')).toHaveCount(1) await expect(page.locator('td[data-testid="name"]')).toContainText('Basic Authentication') @@ -201,7 +201,7 @@ test.describe('plugins', () => { await withNavigation( page, - async () => await page.locator('.plugin-form [data-testid="form-footer-actions"] .primary').click() + async () => await page.locator('[data-testid="form-actions"] .primary').click() ) await expect(page.locator('.k-table tbody tr')).toHaveCount(1) await expect(page.locator('td[data-testid="name"]')).toContainText('Basic Authentication') @@ -230,7 +230,7 @@ test.describe('plugins', () => { await expect(page.locator('.autosuggest #consumer-id')).toHaveValue(new RegExp(`${mockConsumerName}\\s*-\\s*${uuid}`)) await withNavigation( page, - async () => await page.locator('.plugin-form [data-testid="form-footer-actions"] .primary').click() + async () => await page.locator('[data-testid="form-actions"] .primary').click() ) await expect(page.locator('.k-table tbody tr')).toHaveCount(1) await expect(page.locator('td[data-testid="name"]')).toContainText('Datadog') @@ -268,7 +268,6 @@ test.describe('plugins', () => { await fillEntityForm({ page, withAction: 'submit', - handleModal: true, })) await withNavigation(page, async () => await clickEntityListAction(page, 'view')) @@ -309,7 +308,7 @@ test.describe('plugins', () => { await withNavigation( page, - async () => await page.locator('.plugin-form [data-testid="form-footer-actions"] .primary').click() + async () => await page.locator('[data-testid="form-actions"] .primary').click() ) await withNavigation(page, async () => await clickEntityListAction(page, 'view')) @@ -329,7 +328,6 @@ test.describe('plugins', () => { tags: mockTag, }, withAction: 'submit', - handleModal: true, }) ) @@ -375,7 +373,7 @@ test.describe('plugins', () => { await withNavigation(page, async () => await clickEntityListAction(page, 'view')) await expect(getPropertyValue(page, 'instance_name')).toContainText('') await withNavigation(page, async () => await page.getByTestId('header-edit-button').click()) - await expect(page.locator('.entity-form #instance_name')).toHaveValue('') + await expect(page.locator('#instance_name')).toHaveValue('') }) test('supports instance_name param', async ({ page, pluginListPage }) => { @@ -392,7 +390,7 @@ test.describe('plugins', () => { await withNavigation(page, async () => await clickEntityListAction(page, 'view')) await expect(getPropertyValue(page, 'instance_name')).toContainText(mockInstanceName) await withNavigation(page, async () => await page.getByTestId('header-edit-button').click()) - await expect(page.locator('.entity-form #instance_name')).toHaveValue(mockInstanceName) + await expect(page.locator('#instance_name')).toHaveValue(mockInstanceName) }) test('filter should work in plugin select page', async ({ page }) => { @@ -422,7 +420,7 @@ test.describe('plugins', () => { await withNavigation(page, async () => await page.locator('.kong-ui-entities-plugins-list [data-testid="new-plugin"]').click()) await page.locator('[data-testid="Key Authentication"]').click() - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await page.waitForSelector('[data-testid="config-key_names-item-0"]') await expect(page.locator('[data-testid="config-key_names-item-0"] input')).toHaveValue('apikey') }) @@ -453,7 +451,7 @@ test.describe('plugins', () => { await withNavigation(page, async () => await page.locator('.kong-ui-entities-plugins-list [data-testid="new-plugin"]').click()) await page.locator('[data-testid="IP Restriction"]').click() - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await expect(page.locator('#service-id')).not.toBeVisible() await expect(page.locator('#route-id')).not.toBeVisible() await expect(page.locator('#consumer-id')).not.toBeVisible() @@ -496,7 +494,7 @@ test.describe('plugins', () => { ) await clickEntityListAction(page, 'edit') - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await expect(page.locator('#service-id')).toBeVisible() await expect(page.locator('#route-id')).toBeVisible() @@ -529,7 +527,7 @@ test.describe('plugins', () => { await pluginListPage.goto() await withNavigation(page, async () => await page.locator('.kong-ui-entities-plugins-list [data-testid="new-plugin"]').click()) await page.getByTestId('basic-auth-card').click() - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await expect(page.locator('#service-id')).not.toBeVisible() // click scoped & select a service @@ -556,7 +554,7 @@ test.describe('plugins', () => { // verify the created plugin is global await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Global') await clickEntityListAction(page, 'edit') - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await expect(page.locator('#service-id')).not.toBeVisible() await page.click('.selection-group .Scoped-check') await expect(page.locator('#service-id')).toHaveValue('') @@ -576,7 +574,7 @@ test.describe('plugins', () => { // create a scoped plugin await withNavigation(page, async () => await page.locator('.kong-ui-entities-plugins-list [data-testid="new-plugin"]').click()) await page.getByTestId('basic-auth-card').click() - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await page.click('.selection-group .Scoped-check') await page.click('#service-id') await page.fill('#service-id', 'test_service') @@ -594,7 +592,6 @@ test.describe('plugins', () => { await withNavigation(page, async () => await fillEntityForm({ page, withAction: 'submit', - handleModal: true, })) // it should back to global diff --git a/tests/playwright/specs/routes/02-RoutesPlugins.spec.ts b/tests/playwright/specs/routes/02-RoutesPlugins.spec.ts index 509dbd8..6db5c15 100644 --- a/tests/playwright/specs/routes/02-RoutesPlugins.spec.ts +++ b/tests/playwright/specs/routes/02-RoutesPlugins.spec.ts @@ -71,7 +71,7 @@ test.describe('routes plugins', () => { await withNavigation( page, - async () => await page.locator('.plugin-form [data-testid="form-footer-actions"] .primary').click() + async () => await page.locator('[data-testid="form-actions"] .primary').click() ) await expect(page.locator('.k-table tbody tr')).toHaveCount(1) await expect(page.locator('td[data-testid="name"]')).toContainText('Basic Authentication') @@ -90,7 +90,6 @@ test.describe('routes plugins', () => { tags: mockTag, }, withAction: 'submit', - handleModal: true, }), ) await expect(page.locator('.k-table [data-testid="tags"]')).toHaveText(mockTag) @@ -130,21 +129,20 @@ test.describe('routes plugins', () => { await pluginListPage.goto() await withNavigation(page, async () => await page.locator('.kong-ui-entities-plugins-list [data-testid="new-plugin"]').click()) await page.locator('[data-testid="Key Authentication"]').click() - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await withNavigation(page, async () => await page.click(routeListPage.$.submitButton)) await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Global') // Update plugin and scope it to consumer await withNavigation(page, () => clickEntityListAction(page, 'edit')) - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await page.click('.selection-group .Scoped-check') await page.click('#route-id') await page.fill('#route-id', mockRouteName) await page.waitForTimeout(300) await expect(page.locator('.k-select-item')).toContainText(mockRouteName) await page.click('.k-select-item') - await page.click(routeListPage.$.submitButton) - await withNavigation(page, () => page.click('.k-modal .k-modal-footer .k-button.primary')) + await withNavigation(page, () => page.click(routeListPage.$.submitButton)) await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Route') }) @@ -153,10 +151,9 @@ test.describe('routes plugins', () => { await pluginListPage.goto() await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Route') await withNavigation(page, () => clickEntityListAction(page, 'edit')) - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await page.click('.selection-group .Global-check') - await page.click(routeListPage.$.submitButton) - await withNavigation(page, () => page.click('.k-modal .k-modal-footer .k-button.primary')) + await withNavigation(page, () => page.click(routeListPage.$.submitButton)) await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Global') }) }) diff --git a/tests/playwright/specs/services/03-ServicePlugins.spec.ts b/tests/playwright/specs/services/03-ServicePlugins.spec.ts index 8180901..d7b2ff4 100644 --- a/tests/playwright/specs/services/03-ServicePlugins.spec.ts +++ b/tests/playwright/specs/services/03-ServicePlugins.spec.ts @@ -63,7 +63,7 @@ test.describe('service plugins', () => { ) await fillEntityForm({ page }) - await withNavigation(page, () => page.getByTestId('form-footer-actions').locator('.k-button.primary').click()) + await withNavigation(page, () => page.getByTestId('form-actions').locator('.k-button.primary').click()) await waitAndDismissToasts(page) await page.waitForSelector('.kong-ui-entities-plugins-list') @@ -86,14 +86,14 @@ test.describe('service plugins', () => { test("edit action should bring the user to the plugin's edit page", async ({ page }) => { await withNavigation(page, () => clickEntityListAction(page, 'edit')) - await page.waitForSelector('.plugin-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') }) test('cancel button on the edit page should bring the user back to the plugin tab', async ({ page }) => { await withNavigation(page, () => page - .locator('[data-testid="form-footer-actions"]') - .locator('[data-testid="form-footer-action-cancel"]') + .locator('[data-testid="form-actions"]') + .locator('[data-testid="form-cancel"]') .click() ) await page.waitForSelector('.kong-ui-entities-plugins-list') @@ -121,31 +121,29 @@ test.describe('service plugins', () => { await withNavigation(page, async () => await page.locator('.kong-ui-entities-plugins-list [data-testid="new-plugin"]').click()) await expandPlugins(page) await page.getByTestId('key-auth-card').click() - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await withNavigation(page, async () => await page.click(serviceListPage.$.submitButton)) await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Global') // Update plugin and scope it to service await withNavigation(page, () => clickEntityListAction(page, 'edit')) - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await page.click('.selection-group .Scoped-check') await page.click('#service-id') await page.fill('#service-id', 'test_service') await page.waitForTimeout(300) await expect(page.locator('.k-select-item')).toContainText('test_service') await page.click('.k-select-item') - await page.click(serviceListPage.$.submitButton) - await withNavigation(page, () => page.click('.k-modal .k-modal-footer .k-button.primary')) + await withNavigation(page, () => page.click(serviceListPage.$.submitButton)) await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Service') }) test('change scope from scoped to global', async ({ page, serviceListPage }) => { await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Service') await withNavigation(page, () => clickEntityListAction(page, 'edit')) - await page.waitForSelector('.entity-form') + await page.waitForSelector('.kong-ui-entities-plugin-form-container') await page.click('.selection-group .Global-check') - await page.click(serviceListPage.$.submitButton) - await withNavigation(page, () => page.click('.k-modal .k-modal-footer .k-button.primary')) + await withNavigation(page, () => page.click(serviceListPage.$.submitButton)) await expect(page.locator('.kong-ui-entities-plugins-list [data-testid="appliedTo"] .k-badge')).toContainText('Global') }) }) diff --git a/yarn.lock b/yarn.lock index 541a3ea..937409c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -359,6 +359,13 @@ dependencies: "@kong/icons" "^1.8.3" +"@kong-ui-public/copy-uuid@^2.0.17": + version "2.0.17" + resolved "https://registry.yarnpkg.com/@kong-ui-public/copy-uuid/-/copy-uuid-2.0.17.tgz#143116e4b38e49c4805afa9c0d310a90095d890c" + integrity sha512-eeTUqwWtpNQZD3TPJWF3AYYzBk3GN3nFD5OtwYDcDXg7MolATKy32s4TbiDOMrhMsNLmPP4L5jgfqSBCVIXpCQ== + dependencies: + "@kong/icons" "^1.8.8" + "@kong-ui-public/core@^1.4.15": version "1.4.15" resolved "https://registry.yarnpkg.com/@kong-ui-public/core/-/core-1.4.15.tgz#53625cb918424af37f2f3f0146ca0904c84e3b87" @@ -382,12 +389,12 @@ dependencies: "@kong-ui-public/entities-shared" "^2.2.3" -"@kong-ui-public/entities-consumer-groups@^2.1.16": - version "2.1.16" - resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-consumer-groups/-/entities-consumer-groups-2.1.16.tgz#b8d49c22497dbd5d35882f2b9a96852a41f5465d" - integrity sha512-+eT9ONd4jot9LblAM5ucu7YwvF8m+t7Ru/aq9F+mzPXr3QVcHhe2dx3e6DbAAmgXUPDUXsD+aU88uxP2477Pcw== +"@kong-ui-public/entities-consumer-groups@^2.1.18": + version "2.1.18" + resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-consumer-groups/-/entities-consumer-groups-2.1.18.tgz#16006fc6e33b072d7764006fd877ffa0db59b65f" + integrity sha512-vjdSiedpqhLlK/JT2Jtt2pCcLdWQ2dihw2dAPDwawbY++l+zMXySqvyj18yyTCtCI7zl6RE7fQQusldd3QLwig== dependencies: - "@kong-ui-public/entities-shared" "^2.2.3" + "@kong-ui-public/entities-shared" "^2.3.0" "@kong-ui-public/entities-consumers@^2.2.12": version "2.2.12" @@ -396,6 +403,13 @@ dependencies: "@kong-ui-public/entities-shared" "^2.2.3" +"@kong-ui-public/entities-consumers@^2.2.14": + version "2.2.14" + resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-consumers/-/entities-consumers-2.2.14.tgz#fc7947964f833d1d6bb9145986b1dbc803f89394" + integrity sha512-5O/5lTDLqYPyb7IRrMB0cStL4sXe5r9Ul/3qOQtSN5XD6Zwr1QfKS0e1/csBaXKZh4nR3gAIEvDqSmuzjhdjPg== + dependencies: + "@kong-ui-public/entities-shared" "^2.3.0" + "@kong-ui-public/entities-gateway-services@^2.2.12": version "2.2.12" resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-gateway-services/-/entities-gateway-services-2.2.12.tgz#d8e8114f79e41cbd746ebfaea417c957e7ec679e" @@ -403,6 +417,13 @@ dependencies: "@kong-ui-public/entities-shared" "^2.2.3" +"@kong-ui-public/entities-gateway-services@^2.2.14": + version "2.2.14" + resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-gateway-services/-/entities-gateway-services-2.2.14.tgz#d91dee54a84b37d93be4c315b8e5688f63d5c158" + integrity sha512-EqsTnpYY/ISpKoP/B24VpQjTJ9fWZvQzWCxprl32/1BxlJWricLiVaTxRmJe7o1Cm7cZd/T/VydJsKTHExdg2A== + dependencies: + "@kong-ui-public/entities-shared" "^2.3.0" + "@kong-ui-public/entities-key-sets@^2.1.16": version "2.1.16" resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-key-sets/-/entities-key-sets-2.1.16.tgz#63dcb3e384f632f11e8924358a6fa32c11c0427e" @@ -417,22 +438,29 @@ dependencies: "@kong-ui-public/entities-shared" "^2.2.3" -"@kong-ui-public/entities-plugins@^2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-plugins/-/entities-plugins-2.5.4.tgz#e3bc41b60c95cc860cf0af67d11b4c88fe64416f" - integrity sha512-u79Fs2HoM7w2KgSQI1EmXtNjaliYVEUKDdppnCE0pDbaKyohRCqLZGoQBifVlk+5Qb819KebRkQ9yGVb8TcMGA== - dependencies: - "@kong-ui-public/copy-uuid" "^2.0.16" - "@kong-ui-public/entities-consumer-groups" "^2.1.16" - "@kong-ui-public/entities-consumers" "^2.2.12" - "@kong-ui-public/entities-gateway-services" "^2.2.12" - "@kong-ui-public/entities-routes" "^2.2.12" - "@kong-ui-public/entities-shared" "^2.2.3" - "@kong-ui-public/forms" "^2.1.13" - "@kong/icons" "^1.8.3" +"@kong-ui-public/entities-plugins@^2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-plugins/-/entities-plugins-2.5.8.tgz#12f1f4d9d1df13e4a886e87dc2d829c26b13cb3d" + integrity sha512-zkdEVEXeQlCcF++Q4LobL03kSLg3BUOLQxpULDOWt5+Z24sV7OSexZwQna0/zxx/6VhToBZGFL+I2SWegMQ2wQ== + dependencies: + "@kong-ui-public/copy-uuid" "^2.0.17" + "@kong-ui-public/entities-consumer-groups" "^2.1.18" + "@kong-ui-public/entities-consumers" "^2.2.14" + "@kong-ui-public/entities-gateway-services" "^2.2.14" + "@kong-ui-public/entities-routes" "^2.2.14" + "@kong-ui-public/entities-shared" "^2.3.0" + "@kong-ui-public/forms" "^2.1.16" + "@kong/icons" "^1.8.8" marked "^9.1.6" -"@kong-ui-public/entities-routes@^2.2.12", "@kong-ui-public/entities-routes@^2.2.9": +"@kong-ui-public/entities-routes@^2.2.14": + version "2.2.14" + resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-routes/-/entities-routes-2.2.14.tgz#6677180efe5ed23b386e763ada8527d5f2f8872d" + integrity sha512-SPQBArL34J/Z6FjjrBP5ilPeJs81vWyNcfPhlx9SDAiEzSgm+unzM1TOlw950M6dEXzELDeqSfkJoxrAdJBUtw== + dependencies: + "@kong-ui-public/entities-shared" "^2.3.0" + +"@kong-ui-public/entities-routes@^2.2.9": version "2.2.12" resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-routes/-/entities-routes-2.2.12.tgz#b511b2cbc64f68923e992e034242d1498b32fba2" integrity sha512-gS6l1nFyYnLKsgo554gvn6elWen7UtEn+EheF8uUu6S1dL+xhpV8/zJm3uK/bhrsplT/4g6nKu+xoYJL+8dPyQ== @@ -449,6 +477,16 @@ "@kong/icons" "^1.8.3" compare-versions "^6.1.0" +"@kong-ui-public/entities-shared@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-shared/-/entities-shared-2.3.0.tgz#63c68a0eda7896197be328928763806280d8b872" + integrity sha512-g8n//KljkOMJy4IU9WtDQhxJVAMQkPVvQ6zQiL1Dw9ml1rl50am0ZMwPgW6js2Df6QlK3WYnKiMgWN48YZtJzw== + dependencies: + "@kong-ui-public/copy-uuid" "^2.0.17" + "@kong-ui-public/core" "^1.4.15" + "@kong/icons" "^1.8.8" + compare-versions "^6.1.0" + "@kong-ui-public/entities-snis@^2.1.16": version "2.1.16" resolved "https://registry.yarnpkg.com/@kong-ui-public/entities-snis/-/entities-snis-2.1.16.tgz#cecd343ee009aa6ad3f1d616aaa92017604e5351" @@ -471,12 +509,12 @@ dependencies: "@kong-ui-public/entities-shared" "^2.2.3" -"@kong-ui-public/forms@^2.1.10", "@kong-ui-public/forms@^2.1.13": - version "2.1.13" - resolved "https://registry.yarnpkg.com/@kong-ui-public/forms/-/forms-2.1.13.tgz#ea37323e456b560e28f815e2547a8db89a4b6072" - integrity sha512-mMp6tSy7eZerRPnFkOg6TJUgOx+LxAFeVJfRgSuX1yD1qCaeg67UkIiECFp+6SpgYgPrnb6y7mUKNTUN49pZsQ== +"@kong-ui-public/forms@^2.1.16": + version "2.1.16" + resolved "https://registry.yarnpkg.com/@kong-ui-public/forms/-/forms-2.1.16.tgz#936ddae526b49c268f9ea042d1025221c0b1b92d" + integrity sha512-gjyjp/Z/fUPnC11VIlqnMcNFx1gug+dA42tXN3apoIwrFtPXH4mWchsLNZYZnuq8JUKfaiMA4dF8SoIMFQ9vUQ== dependencies: - "@kong/icons" "^1.8.3" + "@kong/icons" "^1.8.8" fecha "^4.2.3" lodash "^4.17.21"
{{ bodyText }}