diff --git a/src/components/licenseDetailForm.jsx b/src/components/licenseDetailForm.jsx index 36ae51e..128bc33 100644 --- a/src/components/licenseDetailForm.jsx +++ b/src/components/licenseDetailForm.jsx @@ -31,7 +31,7 @@ import { fetchSimilarLicenses, } from '../api/api'; import 'react-toastify/dist/ReactToastify.css'; -import { loadYaml } from '../utils/loadYaml'; +import { loadExternalRefFields } from '../utils/loadYaml'; import { resolveComponentPath } from '../utils/componentPathMap'; function LicenseDetailForm({ @@ -49,14 +49,7 @@ function LicenseDetailForm({ const [similarLicenses, setSimilarLicenses] = useState([]); useEffect(() => { - const fetchConfig = async () => { - const config = await loadYaml( - `${import.meta.env.VITE_DOMAIN_SUBDIRECTORY ? `/${import.meta.env.VITE_DOMAIN_SUBDIRECTORY}` : ''}/externalRef.yaml`, - ); - setFields(config.license.fields); - }; - - fetchConfig(); + loadExternalRefFields('license').then(setFields); }, []); const { diff --git a/src/pages/createLicense.jsx b/src/pages/createLicense.jsx index a0032e1..2e62079 100644 --- a/src/pages/createLicense.jsx +++ b/src/pages/createLicense.jsx @@ -17,7 +17,7 @@ import { fetchSimilarLicenses, fetchObligationPreviews, } from '../api/api'; -import { loadYaml } from '../utils/loadYaml'; +import { loadExternalRefFields } from '../utils/loadYaml'; import { resolveComponentPath } from '../utils/componentPathMap'; import SimilarityResultList from '../components/SimilarityResultList'; @@ -49,14 +49,7 @@ function CreateLicense() { const navigate = useNavigate(); useEffect(() => { - const fetchConfig = async () => { - const config = await loadYaml( - `${import.meta.env.VITE_DOMAIN_SUBDIRECTORY ? `/${import.meta.env.VITE_DOMAIN_SUBDIRECTORY}` : ''}/externalRef.yaml`, - ); - setFields(config.license.fields); - }; - - fetchConfig(); + loadExternalRefFields('license').then(setFields); }, []); useEffect(() => { diff --git a/src/pages/createObligation.jsx b/src/pages/createObligation.jsx index c192ecb..9be3eeb 100644 --- a/src/pages/createObligation.jsx +++ b/src/pages/createObligation.jsx @@ -13,7 +13,7 @@ import { useNavigate } from 'react-router-dom'; import CustomSelect from '../components/customStyleSelect'; import SimilarityResultList from '../components/SimilarityResultList'; import { resolveComponentPath } from '../utils/componentPathMap'; -import { loadYaml } from '../utils/loadYaml'; +import { loadExternalRefFields } from '../utils/loadYaml'; import { postObligation, @@ -36,14 +36,7 @@ function CreateObligation() { label: `${l.shortname} (Id:${l.id})`, })); useEffect(() => { - const fetchConfig = async () => { - const config = await loadYaml( - `${import.meta.env.VITE_DOMAIN_SUBDIRECTORY ? `/${import.meta.env.VITE_DOMAIN_SUBDIRECTORY}` : ''}/externalRef.yaml`, - ); - setFields(config.obligation.fields); - }; - - fetchConfig(); + loadExternalRefFields('obligation').then(setFields); }, []); const navigate = useNavigate(); diff --git a/src/pages/obligationDetailForm.jsx b/src/pages/obligationDetailForm.jsx index 12285c0..f91775d 100644 --- a/src/pages/obligationDetailForm.jsx +++ b/src/pages/obligationDetailForm.jsx @@ -36,7 +36,7 @@ import { fetchObligationCategories } from '../api/api'; import SimilarityResultList from '../components/SimilarityResultList'; -import { loadYaml } from '../utils/loadYaml'; +import { loadExternalRefFields } from '../utils/loadYaml'; import { resolveComponentPath } from '../utils/componentPathMap'; function ObligationDetailForm({ @@ -217,14 +217,7 @@ function ObligationDetailForm({ const [fields, setFields] = useState([]); useEffect(() => { - const fetchConfig = async () => { - const config = await loadYaml( - `${import.meta.env.VITE_DOMAIN_SUBDIRECTORY ? `/${import.meta.env.VITE_DOMAIN_SUBDIRECTORY}` : ''}/externalRef.yaml`, - ); - setFields(config.obligation.fields); - }; - - fetchConfig(); + loadExternalRefFields('obligation').then(setFields); }, []); const handleChangeExt = e => { diff --git a/src/utils/loadYaml.js b/src/utils/loadYaml.js index 74e64e5..16b9ed1 100644 --- a/src/utils/loadYaml.js +++ b/src/utils/loadYaml.js @@ -2,10 +2,44 @@ // SPDX-FileCopyrightText: © 2025 Siemens AG // SPDX-FileContributor: Sourav Bhowmik -import yaml from 'js-yaml'; +import { load } from 'js-yaml'; export const loadYaml = async filePath => { const response = await fetch(filePath); - const text = await response.text(); - return yaml.load(text); + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + const config = load(await response.text()); + // A missing file can be answered with index.html by the SPA fallback, + // which parses as a plain string rather than a mapping. + if (config === null || typeof config !== 'object') { + throw new Error('file is missing or is not a YAML mapping'); + } + return config; +}; + +const externalRefPath = () => { + const subdirectory = import.meta.env.VITE_DOMAIN_SUBDIRECTORY; + return `${subdirectory ? `/${subdirectory}` : ''}/externalRef.yaml`; +}; + +// Returns the custom external_ref fields configured for `entity` +// ('license' or 'obligation'), or an empty list if the config can't be used. +export const loadExternalRefFields = async entity => { + try { + const config = await loadYaml(externalRefPath()); + const fields = config[entity]?.fields; + if (!Array.isArray(fields)) { + throw new Error(`no "${entity}.fields" list found`); + } + return fields; + } catch (err) { + // externalRef.yaml is optional, so this is a developer hint, not a user-facing error. + // eslint-disable-next-line no-console + console.warn( + `Could not load external reference fields from externalRef.yaml (${err.message}). ` + + 'Copy public/externalRef.example.yaml to public/externalRef.yaml to add custom fields.', + ); + return []; + } };