Skip to content
Merged
16 changes: 16 additions & 0 deletions admin-ui/app/types/yaml.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
interface YamlModuleContent {
components?: {
schemas?: Record<string, unknown>
}
[key: string]: unknown
}

declare module '*.yaml' {
const content: YamlModuleContent
export default content
}

declare module '*.yml' {
const content: YamlModuleContent
export default content
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React, { useCallback, useState, useEffect } from 'react'
import GluuCommitDialog from 'Routes/Apps/Gluu/GluuCommitDialog'
import { FormGroup } from 'Components'
import { useNavigate } from 'react-router-dom'
import spec from '../../../../../configApiSpecs.yaml'
import { API_CONFIG_WRITE } from 'Utils/PermChecker'
import { useCedarling } from '@/cedarling'
import GluuCommitFooter from 'Routes/Apps/Gluu/GluuCommitFooter'
import JsonPropertyBuilderConfigApi from './JsonPropertyBuilderConfigApi'
import { toast } from 'react-toastify'
import type { ApiAppConfiguration, JsonPatch } from './types'

interface ApiConfigFormProps {
configuration: ApiAppConfiguration
onSubmit: (patches: JsonPatch[], message: string) => Promise<void>
}

interface SpecSchema {
components: {
schemas: {
ApiAppConfiguration: {
properties: Record<string, unknown>
}
}
}
}

const { properties: schema } = (spec as unknown as SpecSchema).components?.schemas
?.ApiAppConfiguration ?? { properties: {} }

const ApiConfigForm: React.FC<ApiConfigFormProps> = ({ configuration, onSubmit }) => {
const { hasCedarPermission, authorize } = useCedarling()
const navigate = useNavigate()
const [modal, setModal] = useState(false)
const [patches, setPatches] = useState<JsonPatch[]>([])

const operations = patches

useEffect(() => {
const authorizePermissions = async () => {
try {
await authorize([API_CONFIG_WRITE])
} catch (error) {
console.error('Error authorizing API config permissions:', error)
}
}

authorizePermissions()
}, [authorize])

const toggle = useCallback(() => {
if (patches?.length > 0) {
setModal((prev) => !prev)
} else {
toast.error('No changes to update')
}
}, [patches])

const submitForm = useCallback(
async (userMessage: string) => {
toggle()
await onSubmit(patches, userMessage)
},
[toggle, onSubmit, patches],
)

function generateLabel(name: string): string {
const result = name.replace(/([A-Z])/g, ' $1')
return result.charAt(0).toUpperCase() + result.slice(1)
}

const patchHandler = (patch: JsonPatch) => {
setPatches((existingPatches) => [...existingPatches, patch])
}

const handleBack = () => {
navigate('/home/dashboard')
}

return (
<>
{Object.keys(configuration).map((propKey) => (
<JsonPropertyBuilderConfigApi
key={propKey}
propKey={propKey}
propValue={configuration[propKey as keyof ApiAppConfiguration]}
lSize={6}
handler={patchHandler}
schema={schema[propKey] as { type?: string; items?: { type?: string; enum?: string[] } }}
doc_category="config_api_properties"
/>
))}

<FormGroup row></FormGroup>
{hasCedarPermission(API_CONFIG_WRITE) && (
<GluuCommitFooter
saveHandler={toggle}
hideButtons={{ back: false }}
backButtonLabel="Back"
backButtonHandler={handleBack}
/>
)}

{hasCedarPermission(API_CONFIG_WRITE) && (
<GluuCommitDialog
handler={toggle}
modal={modal}
operations={operations}
onAccept={submitForm}
/>
)}
</>
)
}

export default ApiConfigForm

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, { useState } from 'react'
import ApiConfigForm from './ApiConfigForm'
import { Card } from 'Components'
import GluuLoader from 'Routes/Apps/Gluu/GluuLoader'
import applicationStyle from 'Routes/Apps/Gluu/styles/applicationstyle'
import SetTitle from 'Utils/SetTitle'
import { useTranslation } from 'react-i18next'
import { useGetConfigApiProperties, usePatchConfigApiProperties } from 'JansConfigApi'
import { useConfigApiActions } from './hooks'
import { toast } from 'react-toastify'
import type { JsonPatch } from './types'

function ConfigApiPage(): JSX.Element {
const { t } = useTranslation()
const { logConfigApiUpdate } = useConfigApiActions()
const [errorMessage, setErrorMessage] = useState<string | null>(null)

SetTitle(t('titles.config_api_configuration'))

const { data: configuration, isLoading, error } = useGetConfigApiProperties()

const patchConfigMutation = usePatchConfigApiProperties()

const handleSubmit = async (patches: JsonPatch[], message: string) => {
try {
setErrorMessage(null)

await patchConfigMutation.mutateAsync({ data: patches })

let auditSuccess = true
try {
await logConfigApiUpdate(message, { requestBody: patches })
} catch (auditError) {
console.error('Error logging audit:', auditError)
auditSuccess = false
}

if (auditSuccess) {
toast.success(t('messages.success_in_saving'))
} else {
toast.warning(t('messages.success_in_saving_audit_failed'))
}
} catch (err) {
console.error('Error updating config:', err)
const errorMsg = err instanceof Error ? err.message : t('messages.error_in_saving')
setErrorMessage(errorMsg)
toast.error(errorMsg)
}
}

const loading = patchConfigMutation.isPending || isLoading

if (error) {
return (
<Card style={applicationStyle.mainCard}>
<div className="p-4 text-danger">
{t('messages.error_in_loading')}: {error instanceof Error ? error.message : String(error)}
</div>
</Card>
)
}

return (
<GluuLoader blocking={loading}>
<Card style={applicationStyle.mainCard}>
{configuration && <ApiConfigForm configuration={configuration} onSubmit={handleSubmit} />}
{errorMessage && (
<div className="alert alert-danger mt-3" role="alert">
{errorMessage}
</div>
)}
</Card>
</GluuLoader>
)
}

export default ConfigApiPage
Loading
Loading