-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FEATURE] Add Link-reader and WriterCollection
Link reader: Allows automatic rewriting of paths and glob patterns to other paths. I.e. a path "/" can be rewritten to "/resorces/project/namespace" WriterCollection: Allows to define a collection of writers mapped to a set of path prefixes. I.e. one writer should be used when writing a resource with the path prefix "/resources/project/namespace/*" and another one for all other paths ("/")
- Loading branch information
1 parent
adba34a
commit a0e5cf3
Showing
6 changed files
with
501 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
const AbstractReaderWriter = require("./AbstractReaderWriter"); | ||
const ReaderCollection = require("./ReaderCollection"); | ||
|
||
/** | ||
* Resource Locator WriterCollection | ||
* | ||
* @public | ||
* @memberof module:@ui5/fs | ||
* @augments module:@ui5/fs.AbstractReaderWriter | ||
*/ | ||
class WriterCollection extends AbstractReaderWriter { | ||
/** | ||
* The constructor. | ||
* | ||
* @param {object} parameters Parameters | ||
* @param {object[]<string,module:@ui5/fs.AbstractReaderWriter[]>} parameters.writerMapping | ||
* Mapping of virtual base paths to writers. Path are matched greedy | ||
* @param {string} parameters.name The collection name | ||
*/ | ||
constructor({writerMapping, name}) { | ||
super(); | ||
this._name = name; | ||
|
||
if (!writerMapping) { | ||
throw new Error("Missing parameter 'writerMapping'"); | ||
} | ||
const basePaths = Object.keys(writerMapping); | ||
if (!basePaths.length) { | ||
throw new Error("Empty parameter 'writerMapping'"); | ||
} | ||
|
||
// Create a regular expression (which is greedy by nature) from all paths to easily | ||
// find the correct writer for any given resource path | ||
this._basePathRegex = basePaths.sort().reduce((regex, basePath, idx) => { | ||
// Validate base path | ||
if (!basePath) { | ||
throw new Error(`Empty path in path mapping of WriterCollection ${this._name}`); | ||
} | ||
if (!basePath.startsWith("/")) { | ||
throw new Error( | ||
`Missing leading slash in path mapping '${basePath}' of WriterCollection ${this._name}`); | ||
} | ||
if (!basePath.endsWith("/")) { | ||
throw new Error( | ||
`Missing trailing slash in path mapping '${basePath}' of WriterCollection ${this._name}`); | ||
} | ||
|
||
return `${regex}(?:${basePath.replace(/\//g, "\\/")})??`; | ||
}, "^(") + ")+.*?$"; | ||
|
||
this._writerMapping = writerMapping; | ||
this._readerCollection = new ReaderCollection({ | ||
name: `Reader collection of writer collection '${this._name}'`, | ||
readers: Object.values(writerMapping) | ||
}); | ||
} | ||
|
||
/** | ||
* Locates resources by glob. | ||
* | ||
* @private | ||
* @param {string|string[]} pattern glob pattern as string or an array of | ||
* glob patterns for virtual directory structure | ||
* @param {object} options glob options | ||
* @param {module:@ui5/fs.tracing.Trace} trace Trace instance | ||
* @returns {Promise<module:@ui5/fs.Resource[]>} Promise resolving to list of resources | ||
*/ | ||
_byGlob(pattern, options, trace) { | ||
return this._readerCollection._byGlob(pattern, options, trace); | ||
} | ||
|
||
/** | ||
* Locates resources by path. | ||
* | ||
* @private | ||
* @param {string} virPath Virtual path | ||
* @param {object} options Options | ||
* @param {module:@ui5/fs.tracing.Trace} trace Trace instance | ||
* @returns {Promise<module:@ui5/fs.Resource>} Promise resolving to a single resource | ||
*/ | ||
_byPath(virPath, options, trace) { | ||
return this._readerCollection._byPath(virPath, options, trace); | ||
} | ||
|
||
/** | ||
* Writes the content of a resource to a path. | ||
* | ||
* @private | ||
* @param {module:@ui5/fs.Resource} resource The Resource to write | ||
* @param {object} [options] Write options, see above | ||
* @returns {Promise<undefined>} Promise resolving once data has been written | ||
*/ | ||
_write(resource, options) { | ||
const resourcePath = resource.getPath(); | ||
|
||
const basePathMatch = resourcePath.match(this._basePathRegex); | ||
if (!basePathMatch || basePathMatch.length < 2) { | ||
throw new Error( | ||
`Failed to find a writer for resource with path ${resourcePath} in WriterCollection ${this._name}. ` + | ||
`Base paths handled by this collection are: ${Object.keys(this._writerMapping).join(", ")}`); | ||
} | ||
const writer = this._writerMapping[basePathMatch[1]]; | ||
return writer._write(resource, options); | ||
} | ||
|
||
_validateBasePath(writerMapping) { | ||
Object.keys(writerMapping).forEach((path) => { | ||
if (!path) { | ||
throw new Error(`Empty path in path mapping of WriterCollection ${this._name}`); | ||
} | ||
if (!path.startsWith("/")) { | ||
throw new Error(`Missing leading slash in path mapping '${path}' of WriterCollection ${this._name}`); | ||
} | ||
if (!path.endsWith("/")) { | ||
throw new Error(`Missing trailing slash in path mapping '${path}' of WriterCollection ${this._name}`); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
module.exports = WriterCollection; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
const AbstractReader = require("../AbstractReader"); | ||
const ResourceFacade = require("../ResourceFacade"); | ||
const resourceFactory = require("../resourceFactory"); | ||
const log = require("@ui5/logger").getLogger("resources:readers:Link"); | ||
|
||
/** | ||
* A reader that allows modification of all resources passed through it. | ||
* | ||
* @public | ||
* @memberof module:@ui5/fs.readers | ||
* @augments module:@ui5/fs.AbstractReader | ||
*/ | ||
class Link extends AbstractReader { | ||
/** | ||
* Path mapping for a [Link]{@link module:@ui5/fs.readers.Link} | ||
* | ||
* @public | ||
* @typedef {object} PathMapping | ||
* @property {string} pathMapping.linkPath Input path to rewrite | ||
* @property {string} pathMapping.targetPath Path to rewrite to | ||
*/ | ||
/** | ||
* Constructor | ||
* | ||
* @public | ||
* @param {object} parameters Parameters | ||
* @param {module:@ui5/fs.AbstractReader} parameters.reader The resource reader to wrap | ||
* @param {PathMapping} parameters.pathMapping | ||
*/ | ||
constructor({reader, pathMapping}) { | ||
super(); | ||
if (!reader) { | ||
throw new Error(`Missing parameter "reader"`); | ||
} | ||
if (!pathMapping) { | ||
throw new Error(`Missing parameter "pathMapping"`); | ||
} | ||
this._reader = reader; | ||
this._pathMapping = pathMapping; | ||
this._validatePathMapping(pathMapping); | ||
} | ||
|
||
/** | ||
* Locates resources by glob. | ||
* | ||
* @private | ||
* @param {string|string[]} patterns glob pattern as string or an array of | ||
* glob patterns for virtual directory structure | ||
* @param {object} options glob options | ||
* @param {module:@ui5/fs.tracing.Trace} trace Trace instance | ||
* @returns {Promise<module:@ui5/fs.Resource[]>} Promise resolving to list of resources | ||
*/ | ||
async _byGlob(patterns, options, trace) { | ||
if (!(patterns instanceof Array)) { | ||
patterns = [patterns]; | ||
} | ||
patterns = patterns.map((pattern) => { | ||
if (pattern.startsWith(this._pathMapping.linkPath)) { | ||
pattern = pattern.substr(this._pathMapping.linkPath.length); | ||
} | ||
return resourceFactory.prefixGlobPattern(pattern, this._pathMapping.targetPath); | ||
}); | ||
|
||
// Flatten prefixed patterns | ||
patterns = Array.prototype.concat.apply([], patterns); | ||
|
||
// Keep resource's internal path unchanged for now | ||
const resources = await this._reader._byGlob(patterns, options, trace); | ||
return resources.map((resource) => { | ||
const resourcePath = resource.getPath(); | ||
if (resourcePath.startsWith(this._pathMapping.targetPath)) { | ||
return new ResourceFacade({ | ||
resource, | ||
path: this._pathMapping.linkPath + resourcePath.substr(this._pathMapping.targetPath.length) | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Locates resources by path. | ||
* | ||
* @private | ||
* @param {string} virPath Virtual path | ||
* @param {object} options Options | ||
* @param {module:@ui5/fs.tracing.Trace} trace Trace instance | ||
* @returns {Promise<module:@ui5/fs.Resource>} Promise resolving to a single resource | ||
*/ | ||
async _byPath(virPath, options, trace) { | ||
if (!virPath.startsWith(this._pathMapping.linkPath)) { | ||
return null; | ||
} | ||
const targetPath = this._pathMapping.targetPath + virPath.substr(this._pathMapping.linkPath.length); | ||
log.verbose(`byPath: Rewriting virtual path ${virPath} to ${targetPath}`); | ||
|
||
const resource = await this._reader._byPath(targetPath, options, trace); | ||
if (resource) { | ||
return new ResourceFacade({ | ||
resource, | ||
path: this._pathMapping.linkPath + resource.getPath().substr(this._pathMapping.targetPath.length) | ||
}); | ||
} | ||
return null; | ||
} | ||
|
||
_validatePathMapping({linkPath, targetPath}) { | ||
if (!linkPath) { | ||
throw new Error(`Path mapping is missing attribute "linkPath"`); | ||
} | ||
if (!targetPath) { | ||
throw new Error(`Path mapping is missing attribute "targetPath"`); | ||
} | ||
if (!linkPath.endsWith("/")) { | ||
throw new Error(`Link path must end with a slash: ${linkPath}`); | ||
} | ||
if (!targetPath.endsWith("/")) { | ||
throw new Error(`Target path must end with a slash: ${targetPath}`); | ||
} | ||
} | ||
} | ||
|
||
module.exports = Link; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.