Skip to content

Commit

Permalink
Rewrite schema file loading to use async/await
Browse files Browse the repository at this point in the history
  • Loading branch information
happy5214 committed Apr 26, 2024
1 parent 37cc7aa commit 19bf153
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 57 deletions.
113 changes: 66 additions & 47 deletions common/schema/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<never>|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<never>|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)
}
})
}

/**
Expand All @@ -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<never>|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, []]
}

/**
Expand All @@ -68,17 +82,17 @@ export const loadSchemaFromSpec = function (schemaDef = null) {
* @param {SchemaSpec} schemaDef The description of which schema to use.
* @returns {Promise<object>} 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)
}
}
}
Expand All @@ -89,7 +103,7 @@ const loadPromise = function (schemaDef) {
* @param {SchemaSpec} schemaDef The standard schema version to load.
* @returns {Promise<object>} 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`
Expand All @@ -105,7 +119,7 @@ const loadRemoteSchema = function (schemaDef) {
* @param {string} path The path to the schema XML data.
* @returns {Promise<object>} The schema XML data.
*/
const loadLocalSchema = function (path) {
function loadLocalSchema(path) {
return loadSchemaFile(files.readFile(path), 'localSchemaLoadFailed', { path: path })
}

Expand All @@ -115,11 +129,13 @@ const loadLocalSchema = function (path) {
* @param {SchemaSpec} schemaDef The description of which schema to use.
* @returns {Promise<object>} 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)]
}
}

/**
Expand All @@ -130,11 +146,14 @@ const loadBundledSchema = function (schemaDef) {
* @param {Object<string, string>} issueArgs The issue arguments passed from the calling function.
* @returns {Promise<object>} 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)]
}
}

/**
Expand All @@ -143,6 +162,6 @@ const loadSchemaFile = function (xmlDataPromise, issueCode, issueArgs) {
* @param {string} data The XML data.
* @returns {Promise<object>} The schema XML data.
*/
const parseSchemaXML = function (data) {
function parseSchemaXML(data) {
return xml2js.parseStringPromise(data, { explicitCharkey: true })
}
23 changes: 13 additions & 10 deletions utils/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ import fetch from 'cross-fetch'
* @param {string} fileName The file path.
* @returns {Promise<unknown>} 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)
}
})
})
}
Expand All @@ -22,11 +26,10 @@ export const readFile = function (fileName) {
* @param {string} url The remote URL.
* @returns {Promise<string>} 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()
}

0 comments on commit 19bf153

Please sign in to comment.