feat: file-based backend fallback for unknown media types

This commit is contained in:
Wannes Kerckhove
2022-04-25 15:58:58 +02:00
committed by Joachim Van Herwegen
parent fa78bc6856
commit ff80079000
6 changed files with 70 additions and 16 deletions

View File

@@ -12,7 +12,7 @@ import { UnsupportedMediaTypeHttpError } from '../../util/errors/UnsupportedMedi
import { guardStream } from '../../util/GuardedStream';
import type { Guarded } from '../../util/GuardedStream';
import { parseContentType } from '../../util/HeaderUtil';
import { joinFilePath, isContainerIdentifier } from '../../util/PathUtil';
import { joinFilePath, isContainerIdentifier, isContainerPath } from '../../util/PathUtil';
import { parseQuads, serializeQuads } from '../../util/QuadUtil';
import { addResourceMetadata, updateModifiedDate } from '../../util/ResourceUtil';
import { toLiteral, toNamedTerm } from '../../util/TermUtil';
@@ -159,8 +159,14 @@ export class FileDataAccessor implements DataAccessor {
*/
private async getFileMetadata(link: ResourceLink, stats: Stats):
Promise<RepresentationMetadata> {
return (await this.getBaseMetadata(link, stats, false))
.set(CONTENT_TYPE_TERM, link.contentType);
const metadata = await this.getBaseMetadata(link, stats, false);
// If the resource is using an unsupported contentType, the original contentType was written to the metadata file.
// As a result, we should only set the contentType derived from the file path,
// when no previous metadata entry for contentType is present.
if (typeof metadata.contentType === 'undefined') {
metadata.set(CONTENT_TYPE_TERM, link.contentType);
}
return metadata;
}
/**
@@ -188,7 +194,12 @@ export class FileDataAccessor implements DataAccessor {
metadata.remove(RDF.terms.type, LDP.terms.Container);
metadata.remove(RDF.terms.type, LDP.terms.BasicContainer);
metadata.removeAll(DC.terms.modified);
metadata.removeAll(CONTENT_TYPE_TERM);
// When writing metadata for a document, only remove the content-type when dealing with a supported media type.
// A media type is supported if the FileIdentifierMapper can correctly store it.
// This allows restoring the appropriate content-type on data read (see getFileMetadata).
if (isContainerPath(link.filePath) || typeof link.contentType !== 'undefined') {
metadata.removeAll(CONTENT_TYPE_TERM);
}
const quads = metadata.quads();
const metadataLink = await this.resourceMapper.mapUrlToFilePath(link.identifier, true);
let wroteMetadata: boolean;

View File

@@ -23,6 +23,8 @@ export class BaseFileIdentifierMapper implements FileIdentifierMapper {
protected readonly logger = getLoggerFor(this);
protected readonly baseRequestURI: string;
protected readonly rootFilepath: string;
// Extension to use as a fallback when the media type is not supported (could be made configurable).
protected readonly unknownMediaTypeExtension = 'unknown';
public constructor(base: string, rootFilepath: string) {
this.baseRequestURI = trimTrailingSlashes(base);
@@ -85,7 +87,10 @@ export class BaseFileIdentifierMapper implements FileIdentifierMapper {
*/
protected async mapUrlToDocumentPath(identifier: ResourceIdentifier, filePath: string, contentType?: string):
Promise<ResourceLink> {
contentType = await this.getContentTypeFromUrl(identifier, contentType);
// Don't try to get content-type from URL when the file path refers to a document with unknown media type.
if (!filePath.endsWith(`.${this.unknownMediaTypeExtension}`)) {
contentType = await this.getContentTypeFromUrl(identifier, contentType);
}
this.logger.debug(`The path for ${identifier.path} is ${filePath}`);
return { identifier, filePath, contentType, isMetadata: this.isMetadataPath(filePath) };
}

View File

@@ -64,10 +64,12 @@ export class ExtensionBasedMapper extends BaseFileIdentifierMapper {
// If the extension of the identifier matches a different content-type than the one that is given,
// we need to add a new extension to match the correct type.
} else if (contentType !== await this.getContentTypeFromPath(filePath)) {
const extension: string = mime.extension(contentType) || this.customExtensions[contentType];
let extension: string = mime.extension(contentType) || this.customExtensions[contentType];
if (!extension) {
this.logger.warn(`No extension found for ${contentType}`);
throw new NotImplementedHttpError(`Unsupported content type ${contentType}`);
// When no extension is found for the provided content-type, use a fallback extension.
extension = this.unknownMediaTypeExtension;
// Signal the fallback by setting the content-type to undefined in the output link.
contentType = undefined;
}
filePath += `$.${extension}`;
}