Skip to content

Commit

Permalink
fix(*): schema desc transformation in plugin forms (#1021)
Browse files Browse the repository at this point in the history
  • Loading branch information
sumimakito committed Dec 22, 2023
1 parent 933d04f commit afad420
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 36 deletions.
57 changes: 52 additions & 5 deletions packages/core/forms/src/forms/ACMEForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@
v-if="selectedIssuer === 'letsEncrypt'"
:model="formModel"
:options="formOptions"
:schema="ACMELetsEncryptSchema"
:schema="acmeLetsEncryptSchema"
@model-updated="onModelUpdated"
/>

<VueFormGenerator
v-if="selectedIssuer === 'zeroSsl' || selectedIssuer === 'other'"
:model="formModel"
:options="formOptions"
:schema="ACMEZeroSSLSchema"
:schema="acmeZeroSSLSchema"
@model-updated="onModelUpdated"
/>
</div>
Expand Down Expand Up @@ -134,8 +134,10 @@
<script setup lang="ts">
import VueFormGenerator from '../generator/FormGenerator.vue'
import { cloneDeep } from 'lodash'
import type { PropType } from 'vue'
import { computed, onMounted, ref } from 'vue'
import type { Schema } from './schemas/ACMECommon'
import { ACMECommonSchema, ACMELetsEncryptSchema, ACMEZeroSSLSchema } from './schemas/ACMECommon'
const props = defineProps({
Expand Down Expand Up @@ -164,6 +166,51 @@ const props = defineProps({
const selectedIssuer = ref<string>('zeroSsl')
const selectedStorageConfig = ref<string>('shm')
const formFieldMap = computed(() => {
const fieldMap: Record<string, any> = {}
if (props.formSchema?.fields) {
for (const field of props.formSchema.fields) {
fieldMap[field.model] = field
}
}
return fieldMap
})
const acmeCommonSchema = computed<Schema>(() => {
const schema = cloneDeep(ACMECommonSchema)
for (const field of schema.fields) {
const help = formFieldMap.value[field.model]?.help
if (field.help === undefined && typeof help === 'string') {
field.help = help
}
}
return schema
})
const acmeLetsEncryptSchema = computed<Schema>(() => {
const schema = cloneDeep(ACMELetsEncryptSchema)
for (const field of schema.fields) {
const help = formFieldMap.value[field.model]?.help
if (field.help === undefined && typeof help === 'string') {
field.help = help
}
}
return schema
})
const acmeZeroSSLSchema = computed<Schema>(() => {
const schema = cloneDeep(ACMEZeroSSLSchema)
for (const field of schema.fields) {
const help = formFieldMap.value[field.model]?.help
if (field.help === undefined && typeof help === 'string') {
field.help = help
}
}
return schema
})
const globalFields = computed(() => {
return {
fields: props.formSchema.fields.filter((r: any) => {
Expand All @@ -173,9 +220,9 @@ const globalFields = computed(() => {
})
const commonFieldsSchema = computed(() => {
const result = ACMECommonSchema.fields.filter((item) =>
!ACMELetsEncryptSchema.fields.some((i) => i.model === item.model) &&
!ACMEZeroSSLSchema.fields.some((i) => i.model === item.model),
const result = acmeCommonSchema.value.fields.filter((item) =>
!acmeLetsEncryptSchema.value.fields.some((i) => i.model === item.model) &&
!acmeZeroSSLSchema.value.fields.some((i) => i.model === item.model),
)
return {
Expand Down
14 changes: 14 additions & 0 deletions packages/core/forms/src/forms/OIDCForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,20 @@ export default {
prop: this.isEditing ? this.formModel['config-auth_methods'].includes('refresh_token') : false,
}]
}
const fieldMap = {}
for (const field of this.formSchema.fields) {
fieldMap[field.model] = field
}
for (const s of [this.commonFieldsSchema, this.authFieldsSchema, this.advancedFieldsSchema]) {
for (const f of s.fields) {
const help = fieldMap[f.model]?.help
if (f.help === undefined && typeof help === 'string') {
f.help = help
}
}
}
},
getAuthMethodsValue(prop, evt) {
const arr = []
Expand Down
23 changes: 20 additions & 3 deletions packages/core/forms/src/forms/schemas/ACMECommon.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
export const ACMECommonSchema = {
export interface Field {
label: string
model: string
type: string
default?: any
disabled?: boolean
help?: string
inputType?: string
order?: number
required?: boolean
valueType?: string
}

export interface Schema {
fields: Field[]
}

export const ACMECommonSchema: Schema = {
fields: [
{
inputType: 'text',
Expand Down Expand Up @@ -35,7 +52,7 @@ export const ACMECommonSchema = {
],
}

export const ACMELetsEncryptSchema = {
export const ACMELetsEncryptSchema: Schema = {
fields: [
{
type: 'input',
Expand All @@ -59,7 +76,7 @@ export const ACMELetsEncryptSchema = {
],
}

export const ACMEZeroSSLSchema = {
export const ACMEZeroSSLSchema: Schema = {
fields: [
{
type: 'input',
Expand Down
9 changes: 0 additions & 9 deletions packages/core/forms/src/forms/schemas/OIDCAuth.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,35 @@
export default {
fields: [{
help: 'The claim that contains the scopes.',
inputType: 'text',
label: 'Config.Scopes Claim',
model: 'config-scopes_claim',
type: 'input',
}, {
help: 'The scopes required to be in the access token.',
inputType: 'text',
label: 'Config.Scopes Required',
model: 'config-scopes_required',
type: 'input',
}, {
help: 'The claim that contains the audience.',
inputType: 'text',
label: 'Config.Audience Claim',
model: 'config-audience_claim',
type: 'input',
}, {
help: 'The audience required to be in the access token.',
inputType: 'text',
label: 'Config.Audience Required',
model: 'config-audience_required',
type: 'input',
}, {
help: 'The claim that contains the roles.',
inputType: 'text',
label: 'Config.Roles Claim',
model: 'config-roles_claim',
type: 'input',
}, {
help: 'The roles required to be in the access token.',
inputType: 'text',
label: 'Config.Roles Required',
model: 'config-roles_required',
type: 'input',
}, {
help: 'The claim that contains the groups.',
inputType: 'text',
label: 'Config.Groups Claim',
model: 'config-groups_claim',
Expand All @@ -51,7 +44,6 @@ export default {
styleClasses: 'w-100',
newElementButtonLabel: '+ Add',
}, {
help: 'The groups required to be in the access token.',
inputType: 'text',
label: 'Config.Groups Required',
model: 'config-groups_required',
Expand All @@ -66,7 +58,6 @@ export default {
styleClasses: 'w-100',
newElementButtonLabel: '+ Add',
}, {
help: 'The claim that contains authenticated groups.',
inputType: 'text',
label: 'Config.Authenticated Groups Claim',
model: 'config-authenticated_groups_claim',
Expand Down
4 changes: 0 additions & 4 deletions packages/core/forms/src/forms/schemas/OIDCCommon.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
export default {
fields: [{
help: 'The client id(s) that the plugin uses when it calls authenticated endpoints on the identity provider.',
inputType: 'password',
label: 'Config.Client Id',
model: 'config-client_id',
type: 'input',
}, {
help: 'The client secret.',
inputType: 'password',
label: 'Config.Client Secret',
model: 'config-client_secret',
type: 'input',
}, {
help: 'The discovery endpoint (or just the issuer identifier).',
inputType: 'text',
label: 'Config.Issuer',
model: 'config-issuer',
required: true,
type: 'input',
}, {
help: 'Types of credentials/grants to enable.',
inputType: 'text',
label: 'Config.Auth Methods',
model: 'config-auth_methods',
Expand Down
45 changes: 39 additions & 6 deletions packages/entities/entities-plugins/src/components/PluginForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,9 @@ const buildFormSchema = (parentKey: string, response: Record<string, any>, initi
initialFormSchema[field].label = formatPluginFieldLabel(field)
}
// plugin configuration fields should set description to `help` prop
if (parentKey === 'config') {
// Apply descriptions from BE schema
// KAG-3347: Add /config-.*/ to cover deep fields like `config.redis.*` in the rate-limiting-advanced plugin
if (parentKey === 'config' || parentKey.startsWith('config-')) {
if (schema[key]?.description) {
initialFormSchema[field].help = marked.parse(schema[key].description, { mangle: false, headerIds: false } as MarkedOptions)
}
Expand Down Expand Up @@ -515,8 +516,12 @@ const buildFormSchema = (parentKey: string, response: Record<string, any>, initi
const elements = scheme.elements
if (elements.type === 'string' && !elements.one_of) {
const { help, label, hint } = initialFormSchema[field]
initialFormSchema[field] = { help, label, hint, ...JSON.parse(JSON.stringify(ArrayStringFieldSchema)) }
const { help: helpOverride, ...overrides } = JSON.parse(JSON.stringify(ArrayStringFieldSchema))
initialFormSchema[field] = { help, label, hint, ...overrides }
// Only replace the help text when it is not defined because ArrayStringFieldSchema is more generic
if (initialFormSchema[field].help === undefined && typeof helpOverride === 'string') {
initialFormSchema[field].help = marked.parse(helpOverride, { mangle: false, headerIds: false } as MarkedOptions)
}
}
}
Expand All @@ -531,12 +536,40 @@ const buildFormSchema = (parentKey: string, response: Record<string, any>, initi
if (plugin === field) {
// Use custom defined schema instead of building from default && set field label
const { help, label, hint } = initialFormSchema[field]
initialFormSchema[field] = { help, label, hint, ...(pluginSchema[plugin as keyof typeof pluginSchema] as Record<string, any>) }
const { help: helpOverride, ...overrides } = pluginSchema[plugin as keyof typeof pluginSchema] as Record<string, any>
initialFormSchema[field] = { help, label, hint, ...overrides }
// Eagerly replace the help text because we are overriding
if (typeof helpOverride === 'string') {
initialFormSchema[field].help = marked.parse(helpOverride, { mangle: false, headerIds: false } as MarkedOptions)
}
}
})
}
// Apply descriptions from BE schema
// KAG-3347: Fix help text in nested fields like `metrics` in the datadog and `parameter_schema`
// in the request-validator plugin
if (scheme.type === 'array' && scheme.elements?.type === 'record' && Array.isArray(scheme.elements.fields)) {
let schemaFieldMap: Record<string, { description?: string }> = {}
for (const field of scheme.elements.fields) {
schemaFieldMap = { ...schemaFieldMap, ...field }
}
const itemFields = initialFormSchema[field]?.items?.schema?.fields
if (Array.isArray(itemFields)) {
for (const itemField of initialFormSchema[field].items.schema.fields) {
const description = schemaFieldMap[itemField.model]?.description
// Only replace the help text when it is not defined because it may have already been
// overridden by the previous step
if (itemField.help === undefined && typeof description === 'string') {
itemField.help = marked.parse(description, { mangle: false, headerIds: false } as MarkedOptions)
}
}
}
}
// required fields, if it's boolean (a checkbox) it will be marked as required even though it isn't
if (scheme.required && scheme.type !== 'boolean') {
initialFormSchema[field].required = true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import type { JwtFieldSchema } from '../../types/plugins/jwt'
import type { JwtSecretFieldSchema, JWTPluginSchema } from '../../types/plugins/jwt'

export const jwtSchema: JwtFieldSchema = {
// KAG-3347: BE descriptions missing. Should remove when BE descriptions are available
export const jwtSchema: JWTPluginSchema = {
'config-cookie_names': {
label: 'config.cookie_names',
type: 'set',
help: 'A comma-separated list of cookie names that Kong will inspect to retrieve JWTs.',
},
'config-uri_param_names': {
label: 'config.uri_param_names',
type: 'set',
help: 'A comma-separated list of querystring parameters that Kong will inspect to retrieve JWTs.',
},
}

export const jwtSecretSchema: JwtSecretFieldSchema = {
fields: [
{
key: {
Expand Down Expand Up @@ -34,5 +48,6 @@ export const jwtSchema: JwtFieldSchema = {
hint: `If algorithm is HMAC, the secret used to sign JWTs for
this credential. If left out, will be auto-generated.`,
},
}],
},
],
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const kafkaSchema: KafkaSchema = {
newElementButtonLabel: '+ Add Bootstrap Server',
label: 'Bootstrap Server(s)',
placeholder: 'Enter a Bootstrap Server',
help: 'A list of bootstrap servers',
help: 'Set of bootstrap brokers.', // KAG-3347: UI text updated. Should remove when BE description is available
items: {
type: 'object',
schema: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { keyAuthSchema } from './plugin-schemas/KeyAuth'
import keyAuthCredentialsSchema from './plugin-schemas/credentials/mockedKeyAuthSchema.json'
import { hmacAuthSchema } from './plugin-schemas/HMAC'
import hmacAuthCredentialsSchema from './plugin-schemas/credentials/mockedHmacAuthSchema.json'
import { jwtSchema } from './plugin-schemas/JWT'
import { jwtSecretSchema } from './plugin-schemas/JWT'
import jwtCredentialsSchema from './plugin-schemas/credentials/mockedJwtSchema.json'
import OAuth2Schema from './plugin-schemas/OAuth2'
import oauthCredentialSchema from './plugin-schemas/credentials/mockedOAuthSchema.json'
Expand Down Expand Up @@ -649,7 +649,7 @@ export const usePluginMetaData = () => {
jwt: {
title: t('plugins.meta.jwt.name'),
plugin: 'jwt',
schema: jwtSchema,
schema: jwtSecretSchema,
name: t('plugins.meta.jwt.credential_name'),
endpoint: '/jwt',
schemaEndpoint: 'jwt_secrets',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { applicationRegistrationSchema } from './plugin-schemas/ApplicationRegis
import { dataDogSchema } from './plugin-schemas/Datadog'
import { statsDAdvancedSchema } from './plugin-schemas/StatsDAdvanced'
import { kafkaSchema } from './plugin-schemas/Kafka'
import { jwtSchema } from './plugin-schemas/JWT'
import { mockingSchema } from './plugin-schemas/Mocking'
import { preFunctionSchema } from './plugin-schemas/PreFunction'
import { rateLimitingSchema } from './plugin-schemas/RateLimiting'
Expand Down Expand Up @@ -48,6 +49,11 @@ export const useSchemas = (entityId?: string) => {
},
},

// KAG-3347: BE descriptions missing. Should remove when BE descriptions are available
jwt: {
...jwtSchema,
},

'kafka-upstream': {
...kafkaSchema,
},
Expand Down
Loading

0 comments on commit afad420

Please sign in to comment.