Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(*): schema desc transformation in plugin forms #1021

Merged
merged 1 commit into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
Loading