diff --git a/lib/create-app.js b/lib/create-app.js index da2ec9980..6423d990f 100644 --- a/lib/create-app.js +++ b/lib/create-app.js @@ -48,7 +48,11 @@ function createApp (argv = {}) { rootUrl: argv.serverUri, rootPath: path.resolve(argv.root || process.cwd()), includeHost: argv.multiuser, - defaultContentType: argv.defaultContentType + defaultContentType: argv.defaultContentType, + fileSuffixes: [ + argv.suffixAcl || '.acl', + argv.suffixMeta || '.meta' + ] }) const configPath = config.initConfigDir(argv) diff --git a/lib/resource-mapper.js b/lib/resource-mapper.js index d9811400e..653c1c76a 100644 --- a/lib/resource-mapper.js +++ b/lib/resource-mapper.js @@ -15,7 +15,8 @@ class ResourceMapper { includeHost = false, defaultContentType = 'application/octet-stream', indexFilename = 'index', - overrideTypes = { acl: 'text/turtle', meta: 'text/turtle' } + overrideTypes = { acl: 'text/turtle', meta: 'text/turtle' }, + fileSuffixes = ['.acl', '.meta'] }) { this._rootUrl = this._removeTrailingSlash(rootUrl) this._rootPath = this._removeTrailingSlash(rootPath) @@ -24,6 +25,7 @@ class ResourceMapper { this._defaultContentType = defaultContentType this._indexFilename = indexFilename this._types = { ...types, ...overrideTypes } + this._isControlFile = new RegExp(`(?:${fileSuffixes.map(fs => fs.replace('.', '\\.')).join('|')})$`) // If the host needs to be replaced on every call, pre-split the root URL if (includeHost) { @@ -68,11 +70,9 @@ class ResourceMapper { // Read all files in the corresponding folder const filename = fullPath.substr(fullPath.lastIndexOf('/') + 1) const folder = fullPath.substr(0, fullPath.length - filename.length) - const files = await this._readdir(folder) // Find a file with the same name (minus the dollar extension) - let match = !searchIndex ? '' : (files.find(f => this._removeDollarExtension(f) === filename || - (isIndex && f.startsWith(this._indexFilename + '.')))) + let match = searchIndex ? await this._getMatchingFile(folder, filename, isIndex) : '' if (match === undefined) { // Error if no match was found, // unless the URL ends with a '/', @@ -90,6 +90,17 @@ class ResourceMapper { return { path, contentType: contentType || this._defaultContentType } } + async _getMatchingFile (folder, filename, isIndex) { + const files = await this._readdir(folder) + const hasSameName = (f) => { + return this._removeDollarExtension(f) === filename + } + const isIndexFile = (f) => { + return isIndex && f.startsWith(this._indexFilename + '.') && !this._isControlFile.test(f) + } + return files.find(f => hasSameName(f) || isIndexFile(f)) + } + async getRepresentationUrlForResource (resourceUrl) { let fullPath = this.getFullPath(resourceUrl) let isIndex = fullPath.endsWith('/') diff --git a/test/unit/resource-mapper-test.js b/test/unit/resource-mapper-test.js index 0fc912c5b..d2aa7cb4d 100644 --- a/test/unit/resource-mapper-test.js +++ b/test/unit/resource-mapper-test.js @@ -286,6 +286,18 @@ describe('ResourceMapper', () => { contentType: 'application/octet-stream' }) + itMapsUrl(mapper, 'a URL of that has an accompanying acl file, but no actual file', + { + url: 'http://localhost/space/' + }, + [ + `${rootPath}space/index.acl` + ], + { + path: `${rootPath}space/`, + contentType: 'application/octet-stream' + }) + itMapsUrl(mapper, 'a URL ending with a slash to an index file for text/html when index.html not is available', { url: 'http://localhost/space/', @@ -297,6 +309,20 @@ describe('ResourceMapper', () => { contentType: 'text/html' }) + itMapsUrl(mapper, 'a URL of that has an accompanying meta file, but no actual file', + { + url: 'http://localhost/space/', + contentType: 'text/html', + createIfNotExists: true + }, + [ + `${rootPath}space/index.meta` + ], + { + path: `${rootPath}space/index.html`, + contentType: 'text/html' + }) + itMapsUrl(mapper, 'a URL ending with a slash to a folder when index is skipped', { url: 'http://localhost/space/',