@overture-stack/lyric
Version:
Data Submission system
115 lines (114 loc) • 5.63 kB
JavaScript
import { SUPPORTED_FILE_EXTENSIONS } from '../../utils/fileUtils.js';
import { failure, success } from '../../utils/result.js';
export const SUBMITTED_FILE_ERROR_CODES = {
FILE_READ_FAILURE: 'FILE_READ_ERROR',
UNSUPPORTED_FILETYPE: 'UNSUPPORTED_FILETYPE',
PARSING_FAILURE: 'PARSING_ERROR',
UNKNOWN_ENTITY: 'UNKNOWN_ENTITY',
INVALID_FILE_NAME: 'INVALID_FILE_NAME',
UNRECOGNIZED_HEADER: 'UNRECOGNIZED_HEADER',
MISSING_REQUIRED_HEADER: 'MISSING_REQUIRED_HEADER',
INCORRECT_SECTION: 'INCORRECT_SECTION',
};
/**
* Attempt to identify the file type from a submitted file. If the filetype is unsupported then this will return
* a failure with the corresponding SubmittedFileError.
*
* Note: This function uses the file extension extracted from the filename to identify the file type.
*/
export function getSubmittedFileType(file) {
const fileExtension = file.originalname.split('.').pop()?.toLowerCase();
const validationResult = SUPPORTED_FILE_EXTENSIONS.safeParse(fileExtension);
if (!validationResult.success) {
return failure({
code: SUBMITTED_FILE_ERROR_CODES.UNSUPPORTED_FILETYPE,
message: `File extension ${fileExtension} does not match any of the supported filetypes "${Object.values(SUPPORTED_FILE_EXTENSIONS.Values).join(',')}"`,
});
}
return success(validationResult.data);
}
/**
* This function will look at the file's filename and find a matching entry in the entity-file map, returning the
* corresponding entity's schema when found.
*
* There are several failure cases when looking for a file in the entity-file map:
* 1. no matches to the file name -> returns failure with `FILENAME_NOT_FOUND`
* 2. multiple matches to the file name -> if all entries map to the same entity, this is treated the same as a single
* match, but if they map to different entities then we return failure with `MULTIPLE_MATCHES`
* 3. a single match is found, but there is no schema with a matching entity name -> retunrs failure with `UNKNOWN_ENTITY`
* @param params
* @returns
*/
function findEntityByFileEntityMap(params) {
const { file, schemas, fileEntityMap } = params;
const mapMatches = fileEntityMap.filter((map) => map.filename === file.originalname);
// Multiple entries in the map match this filename
if (mapMatches.length > 1) {
const entities = new Set(mapMatches.map((entry) => entry.entity));
if (entities.size > 1) {
// There are different entities listed in the map objects, so we cannot identify which entity to use
return failure({ case: 'MULTIPLE_MATCHES', entityNames: Array.from(entities) });
}
}
// after the multiple entries check, we know that all matches have the same entity name (or there are no matches)
const candidateEntity = mapMatches[0]?.entity.toLowerCase();
if (candidateEntity === undefined) {
// No Matching Entries
return failure({ case: 'FILENAME_NOT_FOUND' });
}
// Single matching entry, or multiple matching entries but all entries have the same entity name
const matchingSchema = schemas.find((schema) => schema.name.toLowerCase() === candidateEntity);
if (!matchingSchema) {
return failure({ case: 'UNKNOWN_ENTITY', entityName: candidateEntity });
}
return success(matchingSchema);
}
function findEntityByFilename(params) {
const { file, schemas } = params;
const filenameWithoutExtension = file.originalname.split('.')[0]?.toLowerCase();
return schemas.find((schema) => schema.name.toLowerCase() === filenameWithoutExtension);
}
/**
* Attempt to match a file with the schema entity that it has data for.
*
* This function will first check if a mapping was provided to match each filename to an entity.
* If no match is found in the fileEntityMap, then the filename will be check for a match with a schema name (case insensitive).
* If no match is found, then a Failure result will return with the `UNKNOWN_ENTITY` error code.
*/
export function getSubmittedFileEntity(params) {
const { file, schemas, fileEntityMap } = params;
if (fileEntityMap) {
const mapResult = findEntityByFileEntityMap({ file, schemas, fileEntityMap });
if (mapResult.success) {
return success(mapResult.data);
}
switch (mapResult.data.case) {
case 'FILENAME_NOT_FOUND': {
// no match found, attempt to use filename instead.
break;
}
case 'UNKNOWN_ENTITY': {
// Mapped to an unknown entity, return failure
return failure({
code: 'UNKNOWN_ENTITY',
message: `Provided File-Entity map indicated the file "${file.originalname}" maps to an entity named "${mapResult.data.entityName}", which does not match any of the available Schema names.`,
});
}
case 'MULTIPLE_MATCHES': {
// Multiple mappings found, cannot map file to an entity, return failure
return failure({
code: 'UNKNOWN_ENTITY',
message: `Provided File-Entity map has multiple matches for the file ${file.originalname}: ${mapResult.data.entityNames.join(', ')}`,
});
}
}
}
const filenameEntity = findEntityByFilename({ file, schemas });
if (filenameEntity) {
return success(filenameEntity);
}
return failure({
code: 'UNKNOWN_ENTITY',
message: `The file named "${file.originalname}" cannot be mapped to an entity.`,
});
}