diff --git a/common/schema/loader.js b/common/schema/loader.js index c34b532e..93231cb0 100644 --- a/common/schema/loader.js +++ b/common/schema/loader.js @@ -16,34 +16,48 @@ import { fallbackFilePath, localSchemaList } from './config' * @param {boolean} reportNoFallbackError Whether to report an error on a failed schema load when no fallback was used. * @returns {Promise|Promise<[object, Issue[]]>} The schema XML data or an error. */ -export const loadSchema = function (schemaDef = null, useFallback = true, reportNoFallbackError = true) { - const schemaPromise = loadPromise(schemaDef) - if (schemaPromise === null) { - return Promise.reject([generateIssue('invalidSchemaSpecification', { spec: JSON.stringify(schemaDef) })]) +export async function loadSchema(schemaDef = null, useFallback = true, reportNoFallbackError = true) { + try { + const xmlData = await loadPromise(schemaDef) + if (xmlData === null) { + return Promise.reject([generateIssue('invalidSchemaSpecification', { spec: JSON.stringify(schemaDef) })]) + } + return [xmlData, []] + } catch (issues) { + return loadFallbackSchema(schemaDef, useFallback, reportNoFallbackError, issues) + } +} + +/** + * Load fallback schema XML data from a schema version or path description. + * + * @param {SchemaSpec} schemaDef The description of which schema to use. + * @param {boolean} useFallback Whether to use a bundled fallback schema if the requested schema cannot be loaded. + * @param {boolean} reportNoFallbackError Whether to report an error on a failed schema load when no fallback was used. + * @param {Issue[]} issues Any issues already found. + * @returns {Promise|Promise<[object, Issue[]]>} The fallback schema XML data or an error. + */ +async function loadFallbackSchema(schemaDef, useFallback, reportNoFallbackError, issues) { + if (useFallback) { + issues.push(generateIssue('requestedSchemaLoadFailedFallbackUsed', { spec: JSON.stringify(schemaDef) })) + const fallbackSchemaPath = fallbackFilePath.get(schemaDef.library) + if (fallbackSchemaPath === undefined) { + issues.push(generateIssue('noFallbackSchemaForLibrary', { library: schemaDef.library })) + throw issues + } + try { + const fallbackXmlData = await loadLocalSchema(fallbackSchemaPath) + return [fallbackXmlData, issues] + } catch (fallbackIssues) { + fallbackIssues.push(generateIssue('fallbackSchemaLoadFailed', {})) + throw issues.concat(fallbackIssues) + } + } else { + if (reportNoFallbackError) { + issues.push(generateIssue('requestedSchemaLoadFailedNoFallbackUsed', { spec: JSON.stringify(schemaDef) })) + } + throw issues } - return schemaPromise - .then((xmlData) => [xmlData, []]) - .catch((issues) => { - if (useFallback) { - issues.push(generateIssue('requestedSchemaLoadFailedFallbackUsed', { spec: JSON.stringify(schemaDef) })) - const fallbackSchemaPath = fallbackFilePath.get(schemaDef.library) - if (fallbackSchemaPath === undefined) { - issues.push(generateIssue('noFallbackSchemaForLibrary', { library: schemaDef.library })) - return Promise.reject(issues) - } - return loadLocalSchema(fallbackSchemaPath) - .then((xmlData) => [xmlData, issues]) - .catch((fallbackIssues) => { - fallbackIssues.push(generateIssue('fallbackSchemaLoadFailed', {})) - return Promise.reject(issues.concat(fallbackIssues)) - }) - } else { - if (reportNoFallbackError) { - issues.push(generateIssue('requestedSchemaLoadFailedNoFallbackUsed', { spec: JSON.stringify(schemaDef) })) - } - return Promise.reject(issues) - } - }) } /** @@ -54,12 +68,12 @@ export const loadSchema = function (schemaDef = null, useFallback = true, report * @param {SchemaSpec} schemaDef The description of which schema to use. * @returns {Promise|Promise<[object, Issue[]]>} The schema XML data or an error. */ -export const loadSchemaFromSpec = function (schemaDef = null) { - const schemaPromise = loadPromise(schemaDef) - if (schemaPromise === null) { - return Promise.reject([generateIssue('invalidSchemaSpecification', { spec: JSON.stringify(schemaDef) })]) +export async function loadSchemaFromSpec(schemaDef = null) { + const xmlData = await loadPromise(schemaDef) + if (xmlData === null) { + throw [generateIssue('invalidSchemaSpecification', { spec: JSON.stringify(schemaDef) })] } - return schemaPromise.then((xmlData) => [xmlData, []]) + return [xmlData, []] } /** @@ -68,17 +82,17 @@ export const loadSchemaFromSpec = function (schemaDef = null) { * @param {SchemaSpec} schemaDef The description of which schema to use. * @returns {Promise} The schema XML data or an error. */ -const loadPromise = function (schemaDef) { +async function loadPromise(schemaDef) { if (schemaDef === null) { return null } else if (schemaDef.path) { // TODO: Replace with localPath in 4.0.0. - return loadLocalSchema(schemaDef.path) + return await loadLocalSchema(schemaDef.path) } else { if (localSchemaList.has(schemaDef.localName)) { - return loadBundledSchema(schemaDef) + return await loadBundledSchema(schemaDef) } else { - return loadRemoteSchema(schemaDef) + return await loadRemoteSchema(schemaDef) } } } @@ -89,7 +103,7 @@ const loadPromise = function (schemaDef) { * @param {SchemaSpec} schemaDef The standard schema version to load. * @returns {Promise} The schema XML data. */ -const loadRemoteSchema = function (schemaDef) { +function loadRemoteSchema(schemaDef) { let url if (schemaDef.library) { url = `https://raw.githubusercontent.com/hed-standard/hed-schemas/main/library_schemas/${schemaDef.library}/hedxml/HED_${schemaDef.library}_${schemaDef.version}.xml` @@ -105,7 +119,7 @@ const loadRemoteSchema = function (schemaDef) { * @param {string} path The path to the schema XML data. * @returns {Promise} The schema XML data. */ -const loadLocalSchema = function (path) { +function loadLocalSchema(path) { return loadSchemaFile(files.readFile(path), 'localSchemaLoadFailed', { path: path }) } @@ -115,11 +129,13 @@ const loadLocalSchema = function (path) { * @param {SchemaSpec} schemaDef The description of which schema to use. * @returns {Promise} The schema XML data. */ -const loadBundledSchema = function (schemaDef) { - return parseSchemaXML(localSchemaList.get(schemaDef.localName)).catch((error) => { +async function loadBundledSchema(schemaDef) { + try { + return parseSchemaXML(localSchemaList.get(schemaDef.localName)) + } catch (error) { const issueArgs = { spec: schemaDef, error: error.message } - return Promise.reject([generateIssue('bundledSchemaLoadFailed', issueArgs)]) - }) + throw [generateIssue('bundledSchemaLoadFailed', issueArgs)] + } } /** @@ -130,11 +146,14 @@ const loadBundledSchema = function (schemaDef) { * @param {Object} issueArgs The issue arguments passed from the calling function. * @returns {Promise} The parsed schema XML data. */ -const loadSchemaFile = function (xmlDataPromise, issueCode, issueArgs) { - return xmlDataPromise.then(parseSchemaXML).catch((error) => { +async function loadSchemaFile(xmlDataPromise, issueCode, issueArgs) { + try { + const data = await xmlDataPromise + return parseSchemaXML(data) + } catch (error) { issueArgs.error = error.message - return Promise.reject([generateIssue(issueCode, issueArgs)]) - }) + throw [generateIssue(issueCode, issueArgs)] + } } /** @@ -143,6 +162,6 @@ const loadSchemaFile = function (xmlDataPromise, issueCode, issueArgs) { * @param {string} data The XML data. * @returns {Promise} The schema XML data. */ -const parseSchemaXML = function (data) { +function parseSchemaXML(data) { return xml2js.parseStringPromise(data, { explicitCharkey: true }) } diff --git a/utils/files.js b/utils/files.js index 4fd52380..7fd16d91 100644 --- a/utils/files.js +++ b/utils/files.js @@ -8,10 +8,14 @@ import fetch from 'cross-fetch' * @param {string} fileName The file path. * @returns {Promise} A promise with the file contents. */ -export const readFile = function (fileName) { - return new Promise((resolve) => { +export function readFile(fileName) { + return new Promise((resolve, reject) => { fs.readFile(fileName, 'utf8', (err, data) => { - process.nextTick(() => resolve(data)) + if (err) { + reject(err) + } else { + resolve(data) + } }) }) } @@ -22,11 +26,10 @@ export const readFile = function (fileName) { * @param {string} url The remote URL. * @returns {Promise} A promise with the file contents. */ -export const readHTTPSFile = function (url) { - return fetch(url).then((response) => { - if (!response.ok) { - throw new Error(`Server responded to ${url} with status code ${response.status}: ${response.statusText}`) - } - return response.text() - }) +export async function readHTTPSFile(url) { + const response = await fetch(url) + if (!response.ok) { + throw new Error(`Server responded to ${url} with status code ${response.status}: ${response.statusText}`) + } + return response.text() }