diff --git a/lib/middleware/MiddlewareManager.js b/lib/middleware/MiddlewareManager.js
index c9fea043..1c8073cd 100644
--- a/lib/middleware/MiddlewareManager.js
+++ b/lib/middleware/MiddlewareManager.js
@@ -1,5 +1,6 @@
import middlewareRepository from "./middlewareRepository.js";
import MiddlewareUtil from "./MiddlewareUtil.js";
+import logger from "@ui5/logger";
const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
/**
@@ -20,20 +21,22 @@ const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
* @alias @ui5/server/internal/MiddlewareManager
*/
class MiddlewareManager {
- constructor({graph, resources, options = {
+ constructor({graph, rootProject, resources, options = {
sendSAPTargetCSP: false,
serveCSPReports: false
}}) {
- if (!graph || !resources || !resources.all || !resources.rootProject || !resources.dependencies) {
+ if (!graph || !rootProject || !resources || !resources.all ||
+ !resources.rootProject || !resources.dependencies) {
throw new Error("[MiddlewareManager]: One or more mandatory parameters not provided");
}
this.graph = graph;
+ this.rootProject = rootProject;
this.resources = resources;
this.options = options;
this.middleware = Object.create(null);
this.middlewareExecutionOrder = [];
- this.middlewareUtil = new MiddlewareUtil();
+ this.middlewareUtil = new MiddlewareUtil({graph, project: rootProject});
}
/**
@@ -275,19 +278,22 @@ class MiddlewareManager {
await this.addMiddleware(middlewareDef.name, {
customMiddleware: async ({resources, middlewareUtil}) => {
const customMiddleware = this.graph.getExtension(middlewareDef.name);
- const specVersion = customMiddleware.getSpecVersion();
- const options = {
- configuration: middlewareDef.configuration
+
+ const params = {
+ resources,
+ options: {
+ configuration: middlewareDef.configuration
+ }
};
- const params = {resources, options};
- if (
- specVersion === "2.0" || specVersion === "2.1" ||
- specVersion === "2.2" || specVersion === "2.3" ||
- specVersion === "2.4" || specVersion === "2.5" ||
- specVersion === "2.6"
- ) {
- // Supply interface to MiddlewareUtil instance starting with specVersion 2.0
- params.middlewareUtil = middlewareUtil.getInterface(specVersion);
+
+ const specVersion = customMiddleware.getSpecVersion();
+ if (specVersion.gte("3.0")) {
+ params.options.middlewareName = middlewareDef.name;
+ params.log = logger.getGroupLogger(`server:custom-middleware:${middlewareDef.name}`);
+ }
+ const middlewareUtilInterface = middlewareUtil.getInterface(specVersion);
+ if (middlewareUtilInterface) {
+ params.middlewareUtil = middlewareUtilInterface;
}
return (await customMiddleware.getMiddleware())(params);
},
diff --git a/lib/middleware/MiddlewareUtil.js b/lib/middleware/MiddlewareUtil.js
index 48b3060a..5fa270b4 100644
--- a/lib/middleware/MiddlewareUtil.js
+++ b/lib/middleware/MiddlewareUtil.js
@@ -1,5 +1,12 @@
import parseurl from "parseurl";
import mime from "mime-types";
+import {
+ createReaderCollectionPrioritized,
+ createResource,
+ createFilterReader,
+ createLinkReader,
+ createFlatReader
+} from "@ui5/fs/resourceFactory";
/**
* Convenience functions for UI5 Server middleware.
@@ -17,33 +24,21 @@ import mime from "mime-types";
*/
class MiddlewareUtil {
/**
- * Get an interface to an instance of this class that only provides those functions
- * that are supported by the given custom middleware extension specification version.
*
- * @param {string} specVersion Specification Version of custom middleware extension
- * @returns {object} An object with bound instance methods supported by the given specification version
+ * @param {object} parameters
+ * @param {@ui5/project/graph/ProjectGraph} parameters.graph Relevant ProjectGraph
+ * @param {@ui5/project/specifications/Project} parameters.project Project that is being served
+ * @public
*/
- getInterface(specVersion) {
- const baseInterface = {
- getPathname: this.getPathname.bind(this),
- getMimeInfo: this.getMimeInfo.bind(this)
- };
- switch (specVersion) {
- case "0.1":
- case "1.0":
- case "1.1":
- return undefined;
- case "2.0":
- case "2.1":
- case "2.2":
- case "2.3":
- case "2.4":
- case "2.5":
- case "2.6":
- return baseInterface;
- default:
- throw new Error(`MiddlewareUtil: Unknown or unsupported Specification Version ${specVersion}`);
+ constructor({graph, project}) {
+ if (!graph) {
+ throw new Error(`Missing parameter "graph"`);
+ }
+ if (!project) {
+ throw new Error(`Missing parameter "project"`);
}
+ this._graph = graph;
+ this._project = project;
}
/**
@@ -100,6 +95,148 @@ class MiddlewareUtil {
contentType: type + (charset ? "; charset=" + charset : "")
};
}
+ /**
+ * Specification Version-dependent [Project]{@link @ui5/project/specifications/Project} interface.
+ * For details on individual functions, see [Project]{@link @ui5/project/specifications/Project}
+ *
+ * @public
+ * @typedef {object} @ui5/project/build/helpers/MiddlewareUtill~ProjectInterface
+ * @property {Function} getType Get the project type
+ * @property {Function} getName Get the project name
+ * @property {Function} getVersion Get the project version
+ * @property {Function} getNamespace Get the project namespace
+ * @property {Function} getRootReader Get the project rootReader
+ * @property {Function} getReader Get the project reader
+ * @property {Function} getCustomConfiguration Get the project Custom Configuration
+ * @property {Function} isFrameworkProject Check whether the project is a UI5-Framework project
+ */
+
+ /**
+ * Retrieve a single project from the dependency graph
+ *
+ *
+ * This method is only available to custom server middleware extensions defining
+ * Specification Version 3.0 and above.
+ *
+ * @param {string} [projectName] Name of the project to retrieve. Defaults to the project currently being built
+ * @returns {@ui5/project/build/helpers/MiddlewareUtill~ProjectInterface|undefined}
+ * project instance or undefined if the project is unknown to the graph
+ * @public
+ */
+ getProject(projectName) {
+ if (projectName) {
+ return this._graph.getProject(projectName);
+ }
+ return this._project;
+ }
+
+ /**
+ * Retrieve a list of direct dependencies of a given project from the dependency graph.
+ * Note that this list does not include transitive dependencies.
+ *
+ *
+ * This method is only available to custom server middleware extensions defining
+ * Specification Version 3.0 and above.
+ *
+ * @param {string} [projectName] Name of the project to retrieve. Defaults to the project currently being built
+ * @returns {string[]} Names of all direct dependencies
+ * @throws {Error} If the requested project is unknown to the graph
+ * @public
+ */
+ getDependencies(projectName) {
+ return this._graph.getDependencies(projectName || this._project.getName());
+ }
+
+ /**
+ * Specification Version-dependent set of [@ui5/fs/resourceFactory]{@link @ui5/fs/resourceFactory}
+ * functions provided to middleware.
+ * For details on individual functions, see [@ui5/fs/resourceFactory]{@link @ui5/fs/resourceFactory}
+ *
+ * @public
+ * @typedef {object} @ui5/project/build/helpers/MiddlewareUtill~resourceFactory
+ * @property {Function} createResource Creates a [Resource]{@link @ui5/fs/Resource}.
+ * Accepts the same parameters as the [Resource]{@link @ui5/fs/Resource} constructor.
+ * @property {Function} createReaderCollectionPrioritized Creates a prioritized reader collection:
+ * [ReaderCollectionPrioritized]{@link @ui5/fs/ReaderCollectionPrioritized}
+ * @property {Function} createFilterReader
+ * Create a [Filter-Reader]{@link @ui5/fs/readers/Filter} with the given reader.
+ * @property {Function} createLinkReader
+ * Create a [Link-Reader]{@link @ui5/fs/readers/Filter} with the given reader.
+ * @property {Function} createFlatReader Create a [Link-Reader]{@link @ui5/fs/readers/Link}
+ * where all requests are prefixed with /resources/
.
+ */
+
+ /**
+ * Provides limited access to [@ui5/fs/resourceFactory]{@link @ui5/fs/resourceFactory} functions
+ *
+ *
+ * This attribute is only available to custom server middleware extensions defining
+ * Specification Version 3.0 and above.
+ *
+ * @type {@ui5/project/build/helpers/MiddlewareUtill~resourceFactory}
+ * @public
+ */
+ resourceFactory = {
+ createResource,
+ createReaderCollectionPrioritized,
+ createFilterReader,
+ createLinkReader,
+ createFlatReader,
+ };
+
+ /**
+ * Get an interface to an instance of this class that only provides those functions
+ * that are supported by the given custom middleware extension specification version.
+ *
+ * @param {@ui5/project/specifications/SpecificationVersion} specVersion
+ * SpecVersionComparator instance of the custom server middleware
+ * @returns {object} An object with bound instance methods supported by the given specification version
+ */
+ getInterface(specVersion) {
+ if (specVersion.lt("2.0")) {
+ // Custom middleware defining specVersion <2.0 does not have access to any MiddlewareUtil API
+ return undefined;
+ }
+
+ const baseInterface = {};
+ bindFunctions(this, baseInterface, [
+ "getPathname", "getMimeInfo"
+ ]);
+
+ if (specVersion.gte("3.0")) {
+ // getProject function, returning an interfaced project instance
+ baseInterface.getProject = (projectName) => {
+ const project = this.getProject(projectName);
+ const baseProjectInterface = {};
+ bindFunctions(project, baseProjectInterface, [
+ "getType", "getName", "getVersion", "getNamespace",
+ "getRootReader", "getReader", "getCustomConfiguration", "isFrameworkProject"
+ ]);
+ return baseProjectInterface;
+ };
+ // getDependencies function, returning an array of project names
+ baseInterface.getDependencies = (projectName) => {
+ return this.getDependencies(projectName);
+ };
+
+ baseInterface.resourceFactory = Object.create(null);
+ [
+ // Once new functions get added, extract this array into a variable
+ // and enhance based on spec version once new functions get added
+ "createResource", "createReaderCollectionPrioritized",
+ "createFilterReader", "createLinkReader", "createFlatReader",
+ ].forEach((factoryFunction) => {
+ baseInterface.resourceFactory[factoryFunction] = this.resourceFactory[factoryFunction];
+ });
+ }
+ return baseInterface;
+ }
+}
+
+function bindFunctions(sourceObject, targetObject, funcNames) {
+ funcNames.forEach((funcName) => {
+ targetObject[funcName] = sourceObject[funcName].bind(sourceObject);
+ });
}
export default MiddlewareUtil;
diff --git a/lib/middleware/serveResources.js b/lib/middleware/serveResources.js
index 15b95fbe..c38e5de5 100644
--- a/lib/middleware/serveResources.js
+++ b/lib/middleware/serveResources.js
@@ -42,7 +42,7 @@ function createMiddleware({resources, middlewareUtil}) {
let propertiesFileSourceEncoding = project?.getPropertiesFileSourceEncoding();
if (!propertiesFileSourceEncoding) {
- if (project && ["0.1", "1.0", "1.1"].includes(project.getSpecVersion())) {
+ if (project && project.getSpecVersion().lte("1.1")) {
// default encoding to "ISO-8859-1" for old specVersions
propertiesFileSourceEncoding = "ISO-8859-1";
} else {
diff --git a/lib/server.js b/lib/server.js
index 3a35e713..f5780259 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -165,6 +165,7 @@ export async function serve(graph, {
const middlewareManager = new MiddlewareManager({
graph,
+ rootProject,
resources,
options: {
sendSAPTargetCSP,
diff --git a/test/lib/server/middleware/MiddlewareManager.js b/test/lib/server/middleware/MiddlewareManager.js
index 806d908a..548ff5a5 100644
--- a/test/lib/server/middleware/MiddlewareManager.js
+++ b/test/lib/server/middleware/MiddlewareManager.js
@@ -1,12 +1,30 @@
import test from "ava";
-import sinon from "sinon";
+import sinonGlobal from "sinon";
+import esmock from "esmock";
import MiddlewareManager from "../../../../lib/middleware/MiddlewareManager.js";
import middlewareRepository from "../../../../lib/middleware/middlewareRepository.js";
+test.beforeEach(async (t) => {
+ const sinon = t.context.sinon = sinonGlobal.createSandbox();
+
+ t.context.logger = {
+ getGroupLogger: sinon.stub().returns("group logger")
+ };
+
+ t.context.MiddlewareManager = await esmock("../../../../lib/middleware/MiddlewareManager.js", {
+ "@ui5/logger": t.context.logger
+ });
+});
+
+test.afterEach.always((t) => {
+ t.context.sinon.restore();
+});
+
test("Missing parameters", (t) => {
const err = t.throws(() => {
new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {}
});
});
@@ -18,6 +36,7 @@ test("Correct parameters", (t) => {
t.notThrows(() => {
new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -28,8 +47,10 @@ test("Correct parameters", (t) => {
});
test("applyMiddleware", async (t) => {
+ const {sinon} = t.context;
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "love",
@@ -61,6 +82,7 @@ test("applyMiddleware", async (t) => {
test("addMiddleware: Adding already added middleware produces unique middleware name", async (t) => {
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -97,6 +119,7 @@ test("addMiddleware: Adding already added middleware produces unique middleware
test("addMiddleware: Adding middleware already added to middlewareExecutionOrder", async (t) => {
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -117,6 +140,7 @@ test("addMiddleware: Adding middleware already added to middlewareExecutionOrder
test("addMiddleware: Add middleware", async (t) => {
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -140,6 +164,7 @@ test("addMiddleware: Add middleware", async (t) => {
test("addMiddleware: Add middleware with beforeMiddleware and mountPath parameter", async (t) => {
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -166,6 +191,7 @@ test("addMiddleware: Add middleware with beforeMiddleware and mountPath paramete
test("addMiddleware: Add middleware with afterMiddleware parameter", async (t) => {
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -192,6 +218,7 @@ test("addMiddleware: Add middleware with afterMiddleware parameter", async (t) =
test("addMiddleware: Add middleware with invalid afterMiddleware parameter", async (t) => {
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -214,8 +241,10 @@ test("addMiddleware: Add middleware with invalid afterMiddleware parameter", asy
});
test("addMiddleware: Add middleware with wrapperCallback parameter", async (t) => {
+ const {sinon} = t.context;
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -251,8 +280,10 @@ test("addMiddleware: Add middleware with wrapperCallback parameter", async (t) =
});
test("addMiddleware: Add middleware with async wrapperCallback", async (t) => {
+ const {sinon} = t.context;
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -271,8 +302,10 @@ test("addMiddleware: Add middleware with async wrapperCallback", async (t) => {
});
test("addStandardMiddleware: Adds standard middleware in correct order", async (t) => {
+ const {sinon} = t.context;
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -303,6 +336,7 @@ test("addStandardMiddleware: Adds standard middleware in correct order", async (
});
test("addCustomMiddleware: No custom middleware defined", async (t) => {
+ const {sinon} = t.context;
const graph = {
getRoot: () => {
return {
@@ -313,6 +347,7 @@ test("addCustomMiddleware: No custom middleware defined", async (t) => {
};
const middlewareManager = new MiddlewareManager({
graph,
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -326,6 +361,7 @@ test("addCustomMiddleware: No custom middleware defined", async (t) => {
});
test("addCustomMiddleware: Custom middleware got added", async (t) => {
+ const {sinon} = t.context;
const graph = {
getRoot: () => {
return {
@@ -343,6 +379,7 @@ test("addCustomMiddleware: Custom middleware got added", async (t) => {
};
const middlewareManager = new MiddlewareManager({
graph,
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -375,6 +412,7 @@ test("addCustomMiddleware: Custom middleware got added", async (t) => {
});
test("addCustomMiddleware: No special handling for custom middleware with duplicate name", async (t) => {
+ const {sinon} = t.context;
const graph = {
getRoot: () => {
return {
@@ -388,6 +426,7 @@ test("addCustomMiddleware: No special handling for custom middleware with duplic
};
const middlewareManager = new MiddlewareManager({
graph,
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -416,6 +455,7 @@ test("addCustomMiddleware: Missing name configuration", async (t) => {
};
const middlewareManager = new MiddlewareManager({
graph,
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -445,6 +485,7 @@ test("addCustomMiddleware: Both before- and afterMiddleware configuration", asyn
};
const middlewareManager = new MiddlewareManager({
graph,
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -473,6 +514,7 @@ test("addCustomMiddleware: Missing before- or afterMiddleware configuration", as
};
const middlewareManager = new MiddlewareManager({
graph,
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -489,10 +531,15 @@ test("addCustomMiddleware: Missing before- or afterMiddleware configuration", as
});
test("addCustomMiddleware", async (t) => {
+ const {sinon} = t.context;
const middlewareModuleStub = sinon.stub().returns("ok");
- const getSpecVersionStub = sinon.stub().returns("2.6");
+ const specVersionGteStub = sinon.stub().returns(false);
+ const mockSpecificationVersion = {
+ toString: () => "2.6",
+ gte: specVersionGteStub
+ };
const getExtensionStub = sinon.stub().returns({
- getSpecVersion: getSpecVersionStub,
+ getSpecVersion: () => mockSpecificationVersion,
getMiddleware: () => middlewareModuleStub
});
const graph = {
@@ -512,6 +559,7 @@ test("addCustomMiddleware", async (t) => {
};
const middlewareManager = new MiddlewareManager({
graph,
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -533,9 +581,12 @@ test("addCustomMiddleware", async (t) => {
});
t.is(res, "ok", "Wrapper callback returned expected value");
+ t.is(specVersionGteStub.callCount, 1, "SpecificationVersion#gte got called once");
+ t.is(specVersionGteStub.getCall(0).args[0], "3.0",
+ "SpecificationVersion#gte got called with correct arguments");
t.is(middlewareUtil.getInterface.callCount, 1, "middlewareUtil.getInterface got called once");
- t.is(middlewareUtil.getInterface.getCall(0).args[0], "2.6",
- "middlewareUtil.getInterface got called correct arguments");
+ t.is(middlewareUtil.getInterface.getCall(0).args[0], mockSpecificationVersion,
+ "middlewareUtil.getInterface got called with correct arguments");
t.is(middlewareModuleStub.callCount, 1, "Middleware module got called once");
t.deepEqual(middlewareModuleStub.getCall(0).args[0], {
resources: "resources",
@@ -548,9 +599,82 @@ test("addCustomMiddleware", async (t) => {
}, "Middleware module got called with correct arguments");
});
+test("addCustomMiddleware with specVersion 3.0", async (t) => {
+ const {sinon, MiddlewareManager} = t.context;
+ const middlewareModuleStub = sinon.stub().returns("ok");
+ const specVersionGteStub = sinon.stub().returns(true);
+ const mockSpecificationVersion = {
+ toString: () => "3.0",
+ gte: specVersionGteStub
+ };
+ const getExtensionStub = sinon.stub().returns({
+ getSpecVersion: () => mockSpecificationVersion,
+ getMiddleware: () => middlewareModuleStub
+ });
+ const graph = {
+ getRoot: () => {
+ return {
+ getName: () => "my project",
+ getCustomMiddleware: () => [{
+ name: "my custom middleware A",
+ beforeMiddleware: "cors",
+ configuration: {
+ "🦊": "🐰"
+ }
+ }]
+ };
+ },
+ getExtension: getExtensionStub
+ };
+ const middlewareManager = new MiddlewareManager({
+ graph,
+ rootProject: "root project",
+ resources: {
+ all: "I",
+ rootProject: "like",
+ dependencies: "ponies"
+ }
+ });
+ const addMiddlewareStub = sinon.stub(middlewareManager, "addMiddleware").resolves();
+ await middlewareManager.addCustomMiddleware();
+
+ t.is(addMiddlewareStub.callCount, 1, "addMiddleware was called once");
+
+ const customMiddleware = addMiddlewareStub.getCall(0).args[1].customMiddleware;
+ const middlewareUtil = {
+ getInterface: sinon.stub().returns("interfacedMiddlewareUtil")
+ };
+ const res = await customMiddleware({
+ resources: "resources",
+ middlewareUtil
+ });
+
+ t.is(res, "ok", "Wrapper callback returned expected value");
+ t.is(specVersionGteStub.callCount, 1, "SpecificationVersion#gte got called once");
+ t.is(specVersionGteStub.getCall(0).args[0], "3.0",
+ "SpecificationVersion#gte got called with correct arguments");
+ t.is(middlewareUtil.getInterface.callCount, 1, "middlewareUtil.getInterface got called once");
+ t.is(middlewareUtil.getInterface.getCall(0).args[0], mockSpecificationVersion,
+ "middlewareUtil.getInterface got called with correct arguments");
+ t.is(middlewareModuleStub.callCount, 1, "Middleware module got called once");
+ t.deepEqual(middlewareModuleStub.getCall(0).args[0], {
+ resources: "resources",
+ options: {
+ configuration: {
+ "🦊": "🐰"
+ },
+ middlewareName: "my custom middleware A"
+ },
+ middlewareUtil: "interfacedMiddlewareUtil",
+ log: "group logger"
+ }, "Middleware module got called with correct arguments");
+});
+
test("addStandardMiddleware: CSP middleware configured correctly (default)", async (t) => {
+ const {sinon} = t.context;
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -595,8 +719,10 @@ test("addStandardMiddleware: CSP middleware configured correctly (default)", asy
});
test("addStandardMiddleware: CSP middleware configured correctly (enabled)", async (t) => {
+ const {sinon} = t.context;
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
@@ -653,8 +779,10 @@ test("addStandardMiddleware: CSP middleware configured correctly (enabled)", asy
});
test("addStandardMiddleware: CSP middleware configured correctly (custom)", async (t) => {
+ const {sinon} = t.context;
const middlewareManager = new MiddlewareManager({
graph: {},
+ rootProject: "root project",
resources: {
all: "I",
rootProject: "like",
diff --git a/test/lib/server/middleware/MiddlewareUtil.js b/test/lib/server/middleware/MiddlewareUtil.js
index ca89355b..0dc87d2f 100644
--- a/test/lib/server/middleware/MiddlewareUtil.js
+++ b/test/lib/server/middleware/MiddlewareUtil.js
@@ -3,17 +3,22 @@ import sinon from "sinon";
import esmock from "esmock";
import mime from "mime-types";
import MiddlewareUtil from "../../../../lib/middleware/MiddlewareUtil.js";
+import SpecificationVersion from "@ui5/project/specifications/SpecificationVersion";
test.afterEach.always((t) => {
sinon.restore();
});
+function getSpecificationVersion(specVersion) {
+ return new SpecificationVersion(specVersion);
+}
+
test.serial("getPathname", async (t) => {
const parseurlStub = sinon.stub().returns({pathname: "path%20name"});
const MiddlewareUtil = await esmock("../../../../lib/middleware/MiddlewareUtil.js", {
parseurl: parseurlStub
});
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
const pathname = middlewareUtil.getPathname("req");
t.is(parseurlStub.callCount, 1, "parseurl got called once");
@@ -22,7 +27,7 @@ test.serial("getPathname", async (t) => {
});
test.serial("getMimeInfo", (t) => {
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
const lookupStub = sinon.stub(mime, "lookup").returns("mytype");
const charsetStub = sinon.stub(mime, "charset").returns("mycharset");
@@ -40,7 +45,7 @@ test.serial("getMimeInfo", (t) => {
});
test.serial("getMimeInfo: unknown type", (t) => {
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
const lookupStub = sinon.stub(mime, "lookup");
const charsetStub = sinon.stub(mime, "charset");
@@ -57,18 +62,110 @@ test.serial("getMimeInfo: unknown type", (t) => {
}, "Correct pathname returned");
});
+test("getProject", (t) => {
+ const getProjectStub = sinon.stub().returns("Pony farm!");
+ const getProjectNameStub = sinon.stub().returns("root project name");
+ const middlewareUtil = new MiddlewareUtil({
+ graph: {
+ getProject: getProjectStub
+ },
+ project: {
+ getName: getProjectNameStub
+ }
+ });
+
+ const res = middlewareUtil.getProject("pony farm");
+
+ t.is(getProjectStub.callCount, 1, "ProjectGraph#getProject got called once");
+ t.is(getProjectStub.getCall(0).args[0], "pony farm",
+ "ProjectGraph#getProject got called with correct arguments");
+ t.is(getProjectNameStub.callCount, 0, "#getName of root project has not been called");
+ t.is(res, "Pony farm!", "Correct result");
+});
+
+test("getProject: Default name", (t) => {
+ const getProjectStub = sinon.stub().returns("Pony farm!");
+ const middlewareUtil = new MiddlewareUtil({
+ graph: {
+ getProject: getProjectStub
+ },
+ project: "root project"
+ });
+
+ const res = middlewareUtil.getProject();
+
+ t.is(getProjectStub.callCount, 0, "ProjectGraph#getProject never got called");
+ t.is(res, "root project", "Correct result");
+});
+
+test("getDependencies", (t) => {
+ const getDependenciesStub = sinon.stub().returns("Pony farm!");
+ const getProjectNameStub = sinon.stub().returns("root project name");
+ const middlewareUtil = new MiddlewareUtil({
+ graph: {
+ getDependencies: getDependenciesStub
+ },
+ project: {
+ getName: getProjectNameStub
+ }
+ });
+
+ const res = middlewareUtil.getDependencies("pony farm");
+
+ t.is(getDependenciesStub.callCount, 1, "ProjectGraph#getDependencies got called once");
+ t.is(getDependenciesStub.getCall(0).args[0], "pony farm",
+ "ProjectGraph#getDependencies got called with correct arguments");
+ t.is(getProjectNameStub.callCount, 0, "#getName of root project has not been called");
+ t.is(res, "Pony farm!", "Correct result");
+});
+
+test("getDependencies: Default name", (t) => {
+ const getDependenciesStub = sinon.stub().returns("Pony farm!");
+ const getProjectNameStub = sinon.stub().returns("root project name");
+ const middlewareUtil = new MiddlewareUtil({
+ graph: {
+ getDependencies: getDependenciesStub
+ },
+ project: {
+ getName: getProjectNameStub
+ }
+ });
+
+ const res = middlewareUtil.getDependencies();
+
+ t.is(getDependenciesStub.callCount, 1, "ProjectGraph#getDependencies got called once");
+ t.is(getDependenciesStub.getCall(0).args[0], "root project name",
+ "ProjectGraph#getDependencies got called with correct arguments");
+ t.is(getProjectNameStub.callCount, 1, "#getName of root project has been called once");
+ t.is(res, "Pony farm!", "Correct result");
+});
+
+test.serial("resourceFactory", (t) => {
+ const {resourceFactory} = new MiddlewareUtil({graph: "graph", project: "project"});
+ t.is(typeof resourceFactory.createResource, "function",
+ "resourceFactory function createResource is available");
+ t.is(typeof resourceFactory.createReaderCollectionPrioritized, "function",
+ "resourceFactory function createReaderCollectionPrioritized is available");
+ t.is(typeof resourceFactory.createFilterReader, "function",
+ "resourceFactory function createFilterReader is available");
+ t.is(typeof resourceFactory.createLinkReader, "function",
+ "resourceFactory function createLinkReader is available");
+ t.is(typeof resourceFactory.createFlatReader, "function",
+ "resourceFactory function createFlatReader is available");
+});
+
test("getInterface: specVersion 1.0", (t) => {
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
- const interfacedMiddlewareUtil = middlewareUtil.getInterface("1.0");
+ const interfacedMiddlewareUtil = middlewareUtil.getInterface(getSpecificationVersion("1.0"));
t.is(interfacedMiddlewareUtil, undefined, "no interface provided");
});
test("getInterface: specVersion 2.0", (t) => {
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
- const interfacedMiddlewareUtil = middlewareUtil.getInterface("2.0");
+ const interfacedMiddlewareUtil = middlewareUtil.getInterface(getSpecificationVersion("2.0"));
t.deepEqual(Object.keys(interfacedMiddlewareUtil), [
"getPathname",
@@ -80,9 +177,9 @@ test("getInterface: specVersion 2.0", (t) => {
});
test("getInterface: specVersion 2.1", (t) => {
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
- const interfacedMiddlewareUtil = middlewareUtil.getInterface("2.1");
+ const interfacedMiddlewareUtil = middlewareUtil.getInterface(getSpecificationVersion("2.1"));
t.deepEqual(Object.keys(interfacedMiddlewareUtil), [
"getPathname",
@@ -94,9 +191,9 @@ test("getInterface: specVersion 2.1", (t) => {
});
test("getInterface: specVersion 2.2", (t) => {
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
- const interfacedMiddlewareUtil = middlewareUtil.getInterface("2.2");
+ const interfacedMiddlewareUtil = middlewareUtil.getInterface(getSpecificationVersion("2.2"));
t.deepEqual(Object.keys(interfacedMiddlewareUtil), [
"getPathname",
@@ -108,9 +205,9 @@ test("getInterface: specVersion 2.2", (t) => {
});
test("getInterface: specVersion 2.3", (t) => {
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
- const interfacedMiddlewareUtil = middlewareUtil.getInterface("2.3");
+ const interfacedMiddlewareUtil = middlewareUtil.getInterface(getSpecificationVersion("2.3"));
t.deepEqual(Object.keys(interfacedMiddlewareUtil), [
"getPathname",
@@ -122,9 +219,9 @@ test("getInterface: specVersion 2.3", (t) => {
});
test("getInterface: specVersion 2.4", (t) => {
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
- const interfacedMiddlewareUtil = middlewareUtil.getInterface("2.4");
+ const interfacedMiddlewareUtil = middlewareUtil.getInterface(getSpecificationVersion("2.4"));
t.deepEqual(Object.keys(interfacedMiddlewareUtil), [
"getPathname",
@@ -136,9 +233,9 @@ test("getInterface: specVersion 2.4", (t) => {
});
test("getInterface: specVersion 2.5", (t) => {
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
- const interfacedMiddlewareUtil = middlewareUtil.getInterface("2.5");
+ const interfacedMiddlewareUtil = middlewareUtil.getInterface(getSpecificationVersion("2.5"));
t.deepEqual(Object.keys(interfacedMiddlewareUtil), [
"getPathname",
@@ -150,9 +247,9 @@ test("getInterface: specVersion 2.5", (t) => {
});
test("getInterface: specVersion 2.6", (t) => {
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
- const interfacedMiddlewareUtil = middlewareUtil.getInterface("2.6");
+ const interfacedMiddlewareUtil = middlewareUtil.getInterface(getSpecificationVersion("2.6"));
t.deepEqual(Object.keys(interfacedMiddlewareUtil), [
"getPathname",
@@ -163,23 +260,94 @@ test("getInterface: specVersion 2.6", (t) => {
t.is(typeof interfacedMiddlewareUtil.getMimeInfo, "function", "function getMimeInfo is provided");
});
-test("getInterface: specVersion undefined", (t) => {
- const middlewareUtil = new MiddlewareUtil();
-
- const err = t.throws(() => {
- middlewareUtil.getInterface();
+test("getInterface: specVersion 3.0", (t) => {
+ const getProjectStub = sinon.stub().returns({
+ getSpecVersion: () => "specVersion",
+ getType: () => "type",
+ getName: () => "name",
+ getVersion: () => "version",
+ getNamespace: () => "namespace",
+ getRootReader: () => "rootReader",
+ getReader: () => "reader",
+ getCustomConfiguration: () => "customConfiguration",
+ isFrameworkProject: () => "isFrameworkProject",
+ hasBuildManifest: () => "hasBuildManifest", // Should not be exposed
+ getFrameworkVersion: () => "frameworkVersion", // Should not be exposed
});
+ const getDependenciesStub = sinon.stub().returns(["dep a", "dep b"]);
- t.is(err.message, "MiddlewareUtil: Unknown or unsupported Specification Version undefined",
- "Throw with correct error message");
+ const mockGraph = {
+ getProject: getProjectStub,
+ getDependencies: getDependenciesStub
+ };
+
+ const middlewareUtil = new MiddlewareUtil({graph: mockGraph, project: "project"});
+
+ const interfacedMiddlewareUtil = middlewareUtil.getInterface(getSpecificationVersion("3.0"));
+
+ t.deepEqual(Object.keys(interfacedMiddlewareUtil), [
+ "getPathname",
+ "getMimeInfo",
+ "getProject",
+ "getDependencies",
+ "resourceFactory",
+ ], "Correct methods are provided");
+
+ t.is(typeof interfacedMiddlewareUtil.getPathname, "function", "function getPathname is provided");
+ t.is(typeof interfacedMiddlewareUtil.getMimeInfo, "function", "function getMimeInfo is provided");
+ t.is(typeof interfacedMiddlewareUtil.getProject, "function", "function getProject is provided");
+ t.is(typeof interfacedMiddlewareUtil.getDependencies, "function", "function getDependencies is provided");
+
+ // getProject
+ const interfacedProject = interfacedMiddlewareUtil.getProject("pony");
+ t.deepEqual(Object.keys(interfacedProject), [
+ "getType",
+ "getName",
+ "getVersion",
+ "getNamespace",
+ "getRootReader",
+ "getReader",
+ "getCustomConfiguration",
+ "isFrameworkProject",
+ ], "Correct methods are provided");
+
+ t.is(interfacedProject.getType(), "type", "getType function is bound correctly");
+ t.is(interfacedProject.getName(), "name", "getName function is bound correctly");
+ t.is(interfacedProject.getVersion(), "version", "getVersion function is bound correctly");
+ t.is(interfacedProject.getNamespace(), "namespace", "getNamespace function is bound correctly");
+ t.is(interfacedProject.getRootReader(), "rootReader", "getRootReader function is bound correctly");
+ t.is(interfacedProject.getReader(), "reader", "getReader function is bound correctly");
+ t.is(interfacedProject.getCustomConfiguration(), "customConfiguration",
+ "getCustomConfiguration function is bound correctly");
+ t.is(interfacedProject.isFrameworkProject(), "isFrameworkProject",
+ "isFrameworkProject function is bound correctly");
+
+ // getDependencies
+ t.deepEqual(interfacedMiddlewareUtil.getDependencies("pony"), ["dep a", "dep b"],
+ "getDependencies function is available and bound correctly");
+
+ // resourceFactory
+ const resourceFactory = interfacedMiddlewareUtil.resourceFactory;
+ t.is(typeof resourceFactory.createResource, "function",
+ "resourceFactory function createResource is available");
+ t.is(typeof resourceFactory.createReaderCollectionPrioritized, "function",
+ "resourceFactory function createReaderCollectionPrioritized is available");
+ t.is(typeof resourceFactory.createFilterReader, "function",
+ "resourceFactory function createFilterReader is available");
+ t.is(typeof resourceFactory.createLinkReader, "function",
+ "resourceFactory function createLinkReader is available");
+ t.is(typeof resourceFactory.createFlatReader, "function",
+ "resourceFactory function createFlatReader is available");
});
test("getInterface: specVersion unknown", (t) => {
- const middlewareUtil = new MiddlewareUtil();
+ const middlewareUtil = new MiddlewareUtil({graph: "graph", project: "project"});
const err = t.throws(() => {
- middlewareUtil.getInterface("1.5");
+ middlewareUtil.getInterface(getSpecificationVersion("1.5"));
});
- t.is(err.message, "MiddlewareUtil: Unknown or unsupported Specification Version 1.5",
+ t.is(err.message,
+ "Unsupported Specification Version 1.5 defined. Your UI5 CLI installation might be outdated. " +
+ "For details, see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions",
"Throw with correct error message");
});
diff --git a/test/lib/server/middleware/serveIndex.js b/test/lib/server/middleware/serveIndex.js
index a783fddb..884d3351 100644
--- a/test/lib/server/middleware/serveIndex.js
+++ b/test/lib/server/middleware/serveIndex.js
@@ -26,7 +26,7 @@ test.serial("serveIndex default", async (t) => {
writeResource(readerWriter, "/.myFile4", Buffer.alloc(1024)), // hidden 1 KB
]);
const middleware = serveIndexMiddleware({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources: {
all: readerWriter
}
@@ -92,7 +92,7 @@ test.serial("serveIndex no hidden", async (t) => {
writeResource(readerWriter, "/.myFile4", Buffer.alloc(1024)), // hidden 1 KB
]);
const middleware = serveIndexMiddleware({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources: {
all: readerWriter
},
@@ -161,7 +161,7 @@ test.serial("serveIndex no details", async (t) => {
writeResource(readerWriter, "/.myFile4", Buffer.alloc(1024)), // hidden 1 KB
]);
const middleware = serveIndexMiddleware({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources: {
all: readerWriter
},
diff --git a/test/lib/server/middleware/serveResources.js b/test/lib/server/middleware/serveResources.js
index 0edaeb77..7a43d7de 100644
--- a/test/lib/server/middleware/serveResources.js
+++ b/test/lib/server/middleware/serveResources.js
@@ -61,7 +61,7 @@ test.serial("Check if properties file is served properly", async (t) => {
const setStringSpy = sinon.spy(resource, "setString");
const middleware = serveResourcesMiddleware({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources: {
all: readerWriter
}
@@ -101,7 +101,7 @@ test.serial("Check if properties file is served properly with UTF-8", async (t)
const setStringSpy = sinon.spy(resource, "setString");
const middleware = serveResourcesMiddleware({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources: {
all: readerWriter
}
@@ -137,7 +137,7 @@ test.serial("Check if properties file is served properly without property settin
);
const setStringSpy = sinon.spy(resource, "setString");
const middleware = serveResourcesMiddleware({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources: {
all: readerWriter
}
@@ -171,14 +171,19 @@ test.serial("Check if properties file is served properly without property settin
const readerWriter = resourceFactory.createAdapter({virBasePath: "/"});
const project = {
getPropertiesFileSourceEncoding: () => "",
- getSpecVersion: () => "1.1"
+ getSpecVersion: () => {
+ return {
+ toString: () => "1.1",
+ lte: () => true,
+ };
+ }
};
const resource = await writeResource(readerWriter, "/myFile3.properties",
1024 * 1024, "key=titel\nfame=straße", "latin1", project
);
const setStringSpy = sinon.spy(resource, "setString");
const middleware = serveResourcesMiddleware({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources: {
all: readerWriter
}
@@ -209,14 +214,19 @@ test.serial("Check if properties file is served properly without property settin
const readerWriter = resourceFactory.createAdapter({virBasePath: "/"});
const project = {
getPropertiesFileSourceEncoding: () => "",
- getSpecVersion: () => "2.0"
+ getSpecVersion: () => {
+ return {
+ toString: () => "2.0",
+ lte: () => false,
+ };
+ }
};
const resource = await writeResource(readerWriter, "/myFile3.properties",
1024 * 1024, "key=titel\nfame=straße", "utf8", project
);
const setStringSpy = sinon.spy(resource, "setString");
const middleware = serveResourcesMiddleware({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources: {
all: readerWriter
}
@@ -287,7 +297,7 @@ test.serial("Check verbose logging", async (t) => {
}
};
const middleware = serveResourcesMiddlewareWithMock({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources
});
@@ -354,7 +364,7 @@ test.serial("Check if version replacement is done", (t) => {
}
};
const middleware = serveResourcesMiddleware({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources
});
@@ -433,7 +443,7 @@ test.serial("Check if utf8 characters are correctly processed in version replace
}
};
const middleware = serveResourcesMiddleware({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources
});
diff --git a/test/lib/server/middleware/serveThemes.js b/test/lib/server/middleware/serveThemes.js
index a58ddb64..deb58536 100644
--- a/test/lib/server/middleware/serveThemes.js
+++ b/test/lib/server/middleware/serveThemes.js
@@ -130,7 +130,7 @@ test.beforeEach(async (t) => {
t.context.byPath = resources.all.byPath;
t.context.middleware = t.context.serveThemes({
- middlewareUtil: new MiddlewareUtil(),
+ middlewareUtil: new MiddlewareUtil({graph: "graph", project: "project"}),
resources
});
});