Skip to content

Commit

Permalink
feat(entities-plugins): support array fields in custom plugins (#1289)
Browse files Browse the repository at this point in the history
  • Loading branch information
Leopoldthecoder authored Mar 26, 2024
1 parent 8f7e881 commit a1529e4
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
78 changes: 78 additions & 0 deletions packages/entities/entities-plugins/fixtures/mockData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,84 @@ export const credentialSchema = {
],
}

// custom plugin with array of custom schema objects
export const customPluginSchema = {
fields: [
{
consumer: {
description: 'Custom type for representing a foreign key with a null value allowed.',
eq: null,
reference: 'consumers',
type: 'foreign',
},
},
{
protocols: {
default: [
'grpc',
'grpcs',
'http',
'https',
],
description: 'A set of strings representing HTTP protocols.',
elements: {
one_of: [
'grpc',
'grpcs',
'http',
'https',
],
type: 'string',
},
required: true,
type: 'set',
},
},
{
config: {
fields: [
{
discovery_uris: {
elements: {
fields: [
{
issuer: {
required: true,
type: 'string',
},
},
{
requires_proxy: {
default: true,
type: 'boolean',
},
},
{
ssl_verify: {
default: false,
type: 'boolean',
},
},
{
timeout_ms: {
default: 5000,
type: 'number',
},
},
],
type: 'record',
},
type: 'array',
},
},
],
required: true,
type: 'record',
},
},
],
}

const serviceId = '6ecce9f2-4f3e-45aa-af18-a0553d354845'
// CORS plugin
export const plugin1 = {
Expand Down
48 changes: 48 additions & 0 deletions packages/entities/entities-plugins/src/components/PluginForm.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
schema2,
scopedService,
scopedConsumer,
customPluginSchema,
} from '../../fixtures/mockData'
import PluginForm from './PluginForm.vue'
import { VueFormGenerator } from '../../src'
Expand Down Expand Up @@ -297,6 +298,29 @@ describe('<PluginForm />', () => {
cy.get('@advancedFields').find('#config-random_status_code').should('be.visible')
})

it('should show correct form components for custom plugin with arrays of objects', () => {
interceptKMSchema({ mockData: customPluginSchema })

cy.mount(PluginForm, {
global: { components: { VueFormGenerator } },
props: {
config: baseConfigKM,
pluginType: 'custom',
},
router,
})

cy.wait('@getPluginSchema')
cy.get('.kong-ui-entities-plugin-form-container').should('be.visible')

// array field
cy.getTestId('add-config-discovery_uris').click()
cy.get('#config-discovery_uris-issuer-0').should('have.attr', 'required')
cy.get('#config-discovery_uris-requires_proxy-0').should('have.attr', 'type', 'checkbox').and('be.checked')
cy.get('#config-discovery_uris-ssl_verify-0').should('have.attr', 'type', 'checkbox').and('not.be.checked')
cy.get('#config-discovery_uris-timeout_ms-0').should('have.attr', 'type', 'number').and('have.value', '5000')
})

it('should hide scope selection when hideScopeSelection is true', () => {
interceptKMSchema()

Expand Down Expand Up @@ -1020,6 +1044,30 @@ describe('<PluginForm />', () => {
cy.get('@advancedFields').find('#config-random_status_code').should('be.visible')
})

it('should show correct form components for custom plugin with arrays of objects', () => {
interceptKonnectSchema({ mockData: customPluginSchema })

cy.mount(PluginForm, {
global: { components: { VueFormGenerator } },
props: {
config: baseConfigKonnect,
pluginType: 'custom',
useCustomNamesForPlugin: true,
},
router,
})

cy.wait('@getPluginSchema')
cy.get('.kong-ui-entities-plugin-form-container').should('be.visible')

// array field
cy.getTestId('add-config-discovery_uris').click()
cy.get('#config-discovery_uris-issuer-0').should('have.attr', 'required')
cy.get('#config-discovery_uris-requires_proxy-0').should('have.attr', 'type', 'checkbox').and('be.checked')
cy.get('#config-discovery_uris-ssl_verify-0').should('have.attr', 'type', 'checkbox').and('not.be.checked')
cy.get('#config-discovery_uris-timeout_ms-0').should('have.attr', 'type', 'number').and('have.value', '5000')
})

it('should hide scope selection when hideScopeSelection is true', () => {
interceptKonnectSchema()

Expand Down
37 changes: 37 additions & 0 deletions packages/entities/entities-plugins/src/components/PluginForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,43 @@ const buildFormSchema = (parentKey: string, response: Record<string, any>, initi
}
}
// If itemFields is not defined, it means no custom schema for this field is defined
// This usually happens for a custom plugin, so we need to build the schema
if (!itemFields) {
initialFormSchema[field].fieldClasses = 'array-card-container-wrapper'
initialFormSchema[field].itemContainerComponent = 'FieldArrayCardContainer'
initialFormSchema[field].items = {
type: 'object',
schema: {
fields: Object.values(buildFormSchema(field, scheme.elements, {})),
},
}
initialFormSchema[field].type = 'array'
initialFormSchema[field].newElementButtonLabelClasses = 'kong-form-new-element-button-label'
// Set the model to the field name, and the label to the formatted field name
initialFormSchema[field].items.schema.fields.forEach(
(field: { id?: string, model?: string, label?: string }) => {
for (const f of scheme.elements.fields) {
const modelName = Object.keys(f)[0]
const idParts = field.id?.split?.('-') ?? []
if (idParts[idParts.length - 1] === modelName) {
field.model = modelName
field.label = formatPluginFieldLabel(modelName)
break
}
}
},
)
}
// If the field is an array of objects, set the default value to an object
// with the default values of the nested fields
initialFormSchema[field].items.default = () =>
scheme.elements.fields.reduce((acc: Record<string, any>, current: Record<string, { default?: string }>) => {
const key = Object.keys(current)[0]
acc[key] = current[key].default
return acc
}, {})
}
if (treatAsCredential.value && props.config.app === 'kongManager' && credentialSchema) {
Expand Down

0 comments on commit a1529e4

Please sign in to comment.