Skip to content

Instantly share code, notes, and snippets.

@tsibley
Created November 2, 2023 23:30
Show Gist options
  • Save tsibley/fcbe564e7d31dca730affeeff67aaf90 to your computer and use it in GitHub Desktop.
Save tsibley/fcbe564e7d31dca730affeeff67aaf90 to your computer and use it in GitHub Desktop.
commit b8de8d5d3dbce28d12b5d946c2f6f1cbf7becfbb
Author: Thomas Sibley <tsibley@fredhutch.org>
Date: Mon Oct 30 13:46:58 2023 -0700
wip! version extractor
XXX FIXME: how far to take @ as not a given? i.e. update canonicalizeDataset to make the caller add the version?
XXX FIXME: default to extracting a version?
XXX FIXME: move core version extractor into a function we pass?
XXX FIXME: use route matching instead to make versionExtractor like: req => req.params.versionDescriptor?
XXX FIXME: adjust setDatasetFromPrefix
diff --git a/src/app.js b/src/app.js
index 51a198ca..db337741 100644
--- a/src/app.js
+++ b/src/app.js
@@ -213,7 +213,7 @@ const coreBuildRoutes = coreBuildPaths.map(path => [
app.use([coreBuildRoutes, "/narratives/*"], setSource(req => new CoreSource())); // eslint-disable-line no-unused-vars
app.routeAsync(coreBuildRoutes)
- .all(setDataset(req => req.path, true), canonicalizeDataset(path => `/${path}`))
+ .all(setDataset(req => req.path, req => req.path.match(/@(.*)$/)?.[1]), canonicalizeDataset(path => `/${path}`))
.getAsync(getDataset)
.putAsync(putDataset)
.deleteAsync(deleteDataset)
diff --git a/src/endpoints/sources.js b/src/endpoints/sources.js
index 8b12a388..5d61543b 100644
--- a/src/endpoints/sources.js
+++ b/src/endpoints/sources.js
@@ -33,12 +33,12 @@ const setSource = (sourceExtractor) => (req, res, next) => {
* stashes it in the request context.
*
* @param {pathExtractor} pathExtractor - Function to extract a dataset path from the request
- * @param {Boolean} extractVersion - (Attempt to) extract a version descriptor from the extracted path
+ * @param {versionExtractor} [versionExtractor] - Function to extract a version descriptor from the request
* @returns {expressMiddleware}
*/
-const setDataset = (pathExtractor, extractVersion=false) => (req, res, next) => {
+const setDataset = (pathExtractor, versionExtractor = req => LATEST) => (req, res, next) => {
console.log("middleware::setDataset")
- req.context.dataset = req.context.source.dataset(...pathParts(pathExtractor(req), extractVersion));
+ req.context.dataset = req.context.source.dataset(pathParts(pathExtractor(req)), versionExtractor(req));
next();
};
@@ -202,11 +202,10 @@ const optionsNarrative = options.forAuthzObject(req => req.context.narrative);
* and stashes it in the request context.
*
* @param {pathExtractor} pathExtractor - Function to extract a narrative path from the request
- * @param {Boolean} extractVersion - (Attempt to) extract a version descriptor from the extracted path
* @returns {expressMiddleware}
*/
-const setNarrative = (pathExtractor, extractVersion=false) => (req, res, next) => {
- req.context.narrative = req.context.source.narrative(...pathParts(pathExtractor(req), extractVersion));
+const setNarrative = (pathExtractor) => (req, res, next) => {
+ req.context.narrative = req.context.source.narrative(pathParts(pathExtractor(req)));
next();
};
@@ -261,45 +260,27 @@ const putNarrative = contentTypesConsumed([
/**
- * Split a dataset or narrative `path` into an array of parts and a version
- * descriptor.
+ * Split a dataset or narrative `path` into an array of parts.
*
* If `path` is a tangletree path (i.e. refers to two datasets), returns only
* the parts for the first dataset.
*
- * By default the returned version descriptor is LATEST. If `extractVersion` is
- * true, we attempt to extract a version descriptor from the provided path,
- * falling back to LATEST if one is not present.
+ * If `path` contains a version descriptor (i.e. @…), it is removed.
*
* @param {String} path
- * @param {Boolean} extractVersion
- * @returns {[String[], String]}
+ * @returns {String[]}
*/
-function pathParts(path = "", extractVersion = false) {
- // Use only the first dataset in a tangletree (dual dataset) path.
- let normalizedPath = path.split(":")[0]
+function pathParts(path = "") {
+ const normalizedPath = path
+ .split(":")[0] // Use only the first dataset in a tangletree (dual dataset) path.
+ .replace(/@.*$/, "") // Ignore version descriptor.
+ .replace(/^\/+/, "") // Ignore leading slashes
+ .replace(/\/+$/, "") // …and trailing slashes.
+ ;
- /* A part of the path starting with "@" is the version descriptor - this
- will later be translated to the appropriate S3 version ID / GitHub SHA,
- but we use human readable descriptors in the URL path. */
- let versionDescriptor = LATEST; // default;
- if (extractVersion) {
- const match = normalizedPath.match(/^(.+)@(.+)$/)
- if (match) {
- [normalizedPath, versionDescriptor] = match.slice(1,3);
- }
- }
+ if (!normalizedPath) return [];
- // Ignore leading & trailing slashes (after version descriptor removal)
- normalizedPath = normalizedPath
- .replace(/\/+$/, "")
- .replace(/^\/+/, "");
-
- if (!normalizedPath) return [[], LATEST];
-
- console.log(`function::pathParts(${path}, ${extractVersion}) -> `, normalizedPath.split("/"), versionDescriptor)
-
- return [normalizedPath.split("/"), versionDescriptor]
+ return normalizedPath.split("/");
}
@@ -369,6 +350,12 @@ function receiveSubresource(subresourceExtractor) {
* @returns {String} Path for {@link Source#dataset} or {@link Source#narrative}
*/
+/**
+ * @callback versionExtractor
+ * @param {express.request} req
+ * @returns {?String} Version descriptor for {@link Source#dataset} or {@link Source#narrative}
+ */
+
/* Confused about the duplication below? It's the documented way to handle
* overloaded (e.g. arity-dependent) function signatures.¹ Note that it relies
* on the "nestled" or "cuddled" end and start comment markers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment