dev-approuter 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.1.2](https://github.com/ui5-community/ui5-ecosystem-showcase/compare/dev-approuter@0.1.1...dev-approuter@0.1.2) (2023-08-25)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **dev-approuter:** make CAP projects work without reference in xs-dev.json ([#809](https://github.com/ui5-community/ui5-ecosystem-showcase/issues/809)) ([d52a432](https://github.com/ui5-community/ui5-ecosystem-showcase/commit/d52a432609630da3ac215cdcc1a501d7be37655d))
12
+
13
+
14
+
15
+
16
+
17
+ ## [0.1.1](https://github.com/ui5-community/ui5-ecosystem-showcase/compare/dev-approuter@0.1.0...dev-approuter@0.1.1) (2023-08-25)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **dev-approuter:** add missing require('fs') ([#808](https://github.com/ui5-community/ui5-ecosystem-showcase/issues/808)) ([0793907](https://github.com/ui5-community/ui5-ecosystem-showcase/commit/07939070bb60f4acb81864b2fd59d2b730f19e7f))
23
+
24
+
25
+
26
+
27
+
6
28
  # 0.1.0 (2023-08-25)
7
29
 
8
30
 
@@ -1,6 +1,6 @@
1
1
  const approuter = require("@sap/approuter");
2
2
  const express = require("express");
3
- const xsenv = require('@sap/xsenv')
3
+ const xsenv = require("@sap/xsenv");
4
4
 
5
5
  const findUI5Modules = require("cds-plugin-ui5/lib/findUI5Modules");
6
6
  const createPatchedRouter = require("cds-plugin-ui5/lib/createPatchedRouter");
@@ -9,139 +9,128 @@ const applyUI5Middleware = require("cds-plugin-ui5/lib/applyUI5Middleware");
9
9
  const findCAPModules = require("ui5-middleware-cap/lib/findCAPModules");
10
10
  const applyCAPMiddleware = require("ui5-middleware-cap/lib/applyCAPMiddleware");
11
11
 
12
- const {
13
- parseConfig,
14
- applyDependencyConfig,
15
- addDestination,
16
- configureCAPRoute,
17
- configureUI5Route
18
- } = require("./helpers");
19
-
12
+ const { parseConfig, applyDependencyConfig, addDestination, configureCAPRoute, configureUI5Route } = require("./helpers");
20
13
 
21
14
  class DevApprouter {
22
-
23
- constructor() {}
24
-
25
- /**
26
- * Starts the dev approuter.
27
- * Extensions passed as argument are handed to the SAP Approuter without modifications.
28
- * We suggest to check the documentation (link below) for an extensive read on how the dev approuter works.
29
- * Here is a shorter summary:
30
- * UI5 modules declared as (dev)dependencies are added as extensions
31
- * using the extension API of the SAP Approuter.
32
- * CAP modules declared as (dev)dependencies are started on a different port.
33
- * Corresponding routes and destinations are automatically created.
34
- * A custom `xs-dev.json` can be used to configure the dev approuter,
35
- * so the productive configuration can be kept in the `xs-app.json`.
36
- * @param {Object[]} [extensions] - an optional array of extensions that are handed to the SAP Approuter without modification.
37
- * @param {Object} extensions[].insertMiddleware - an object containing the middlewares.
38
- * @param {Object[]} extensions[].insertMiddleware.first - an array of middlewares to be inserted in the `first` slot.
39
- * @param {String} extensions[].insertMiddleware.first[].path - a string representing the path to handle requests for.
40
- * @param {Function} extensions[].insertMiddleware.first[].handler - a function handling `(req, res, next)`.
41
- * @param {Object[]} extensions[].insertMiddleware.beforeRequestHandler - an array of middlewares to be inserted in the `beforeRequestHandler` slot.
42
- * @param {String} extensions[].insertMiddleware.beforeRequestHandler[].path - a string representing the path to handle requests for.
43
- * @param {Function} extensions[].insertMiddleware.beforeRequestHandler[].handler - a function handling `(req, res, next)`.
44
- * @param {Object[]} extensions[].insertMiddleware.beforeErrorHandler - an array of middlewares to be inserted in the `beforeErrorHandler` slot.
45
- * @param {String} extensions[].insertMiddleware.beforeErrorHandler[].path - a string representing the path to handle requests for.
46
- * @param {Function} extensions[].insertMiddleware.beforeErrorHandler[].handler - a function handling `(req, res, next)`.
47
- * @see https://github.com/ui5-community/ui5-ecosystem-showcase/tree/main/packages/dev-approuter
48
- */
49
- async start(extensions = []) {
50
- // loads env from default-env.json
51
- xsenv.loadEnv();
52
-
53
- const config = parseConfig();
54
- const cwd = process.cwd();
55
-
56
- // lookup the CAP server root
57
- let capServerConfig;
58
- const capModules = await findCAPModules({ cwd });
59
- if (capModules.length > 1) {
60
- throw new Error(`Multiple CAP modules found. The package dev-approuter can only handle one CAP module as dependency.`);
61
- } else if (capModules.length === 1) {
62
- capServerConfig = capModules[0];
63
- }
64
-
65
- // find all UI5 modules from the CAP server root and dependencies from the approuter
66
- const ui5Modules = [...(await findUI5Modules({ cwd, skipLocalApps: true }))];
67
- if (capServerConfig) {
68
- ui5Modules.push(...(await findUI5Modules({ cwd: capServerConfig.modulePath, skipDeps: true })))
69
- }
70
-
71
- // collect UI5 middlewares
72
- const ui5Middlewares = [];
73
- for await (const ui5Module of ui5Modules) {
74
- const { moduleId, modulePath, mountPath } = ui5Module;
75
-
76
- // create a patched router
77
- const router = await createPatchedRouter();
78
-
79
- // apply the UI5 middlewares to the router
80
- await applyUI5Middleware(router, {
81
- basePath: modulePath,
82
- configPath: modulePath,
83
- });
84
-
85
- // mounting the router for the UI5 application to the CAP server
86
- console.log(`Mounting ${mountPath} to UI5 app ${modulePath}`);
87
-
88
- let middlewareMountPath
89
- // define middlewareMountPath as `/_${mountPath}` if ui5 module is referenced as "dependency" in xs-dev.json or xs-app.json
90
- if (config.dependencyRoutes && config.dependencyRoutes[moduleId]) {
91
- // configure UI5 route
92
- config.dependencyRoutes[moduleId] = configureUI5Route(moduleId, mountPath, config.dependencyRoutes[moduleId]);
93
-
94
- middlewareMountPath = "/_" + mountPath;
95
-
96
- // add destination for newly configured route
97
- addDestination(moduleId, process.env.PORT, middlewareMountPath);
98
- } else {
99
- middlewareMountPath = mountPath;
100
- }
101
-
102
- // store the router for later registration
103
- ui5Middlewares.push({
104
- path: middlewareMountPath,
105
- handler: router
106
- });
107
-
108
- }
109
-
110
- // start CAP server on different port
111
- if (capServerConfig) {
112
- const { modulePath, moduleId } = capServerConfig;
113
-
114
- // start CAP server on different port
115
- const app = express();
116
- const { servicesPaths } = await applyCAPMiddleware(app, { root: modulePath, cwd });
117
- app.listen(process.env.CAP_PORT || 4004, () => {
118
- console.log(`CAP server started at: http://localhost:${process.env.CAP_PORT || 4004}`);
119
- });
120
-
121
- // configure CAP route if referenced as "dependency" in xs-dev.json/xs-app.json
122
- if (config.dependencyRoutes && config.dependencyRoutes[moduleId]) {
123
- config.dependencyRoutes[moduleId] = configureCAPRoute(moduleId, servicesPaths, config.dependencyRoutes[moduleId]);
124
- }
125
-
126
- // add destination for newly configured route
127
- addDestination(moduleId, process.env.CAP_PORT || 4004);
128
- }
129
-
130
- // create and start the SAP Approuter
131
- // https://help.sap.com/docs/btp/sap-business-technology-platform/extension-api-of-application-router
132
- approuter().start({
133
- port: process.env.PORT || 5001,
134
- xsappConfig: applyDependencyConfig(config),
135
- extensions: [
136
- {
137
- insertMiddleware: {
138
- first: ui5Middlewares
139
- }
140
- }
141
- ].concat(extensions)
142
- });
143
- console.log(`Approuter started at: http://localhost:${process.env.PORT || 5001}`);
144
- };
15
+ constructor() {}
16
+
17
+ /**
18
+ * Starts the dev approuter.
19
+ * Extensions passed as argument are handed to the SAP Approuter without modifications.
20
+ * We suggest to check the documentation (link below) for an extensive read on how the dev approuter works.
21
+ * Here is a shorter summary:
22
+ * UI5 modules declared as (dev)dependencies are added as extensions
23
+ * using the extension API of the SAP Approuter.
24
+ * CAP modules declared as (dev)dependencies are started on a different port.
25
+ * Corresponding routes and destinations are automatically created.
26
+ * A custom `xs-dev.json` can be used to configure the dev approuter,
27
+ * so the productive configuration can be kept in the `xs-app.json`.
28
+ * @param {Object[]} [extensions] - an optional array of extensions that are handed to the SAP Approuter without modification.
29
+ * @param {Object} extensions[].insertMiddleware - an object containing the middlewares.
30
+ * @param {Object[]} extensions[].insertMiddleware.first - an array of middlewares to be inserted in the `first` slot.
31
+ * @param {String} extensions[].insertMiddleware.first[].path - a string representing the path to handle requests for.
32
+ * @param {Function} extensions[].insertMiddleware.first[].handler - a function handling `(req, res, next)`.
33
+ * @param {Object[]} extensions[].insertMiddleware.beforeRequestHandler - an array of middlewares to be inserted in the `beforeRequestHandler` slot.
34
+ * @param {String} extensions[].insertMiddleware.beforeRequestHandler[].path - a string representing the path to handle requests for.
35
+ * @param {Function} extensions[].insertMiddleware.beforeRequestHandler[].handler - a function handling `(req, res, next)`.
36
+ * @param {Object[]} extensions[].insertMiddleware.beforeErrorHandler - an array of middlewares to be inserted in the `beforeErrorHandler` slot.
37
+ * @param {String} extensions[].insertMiddleware.beforeErrorHandler[].path - a string representing the path to handle requests for.
38
+ * @param {Function} extensions[].insertMiddleware.beforeErrorHandler[].handler - a function handling `(req, res, next)`.
39
+ * @see https://github.com/ui5-community/ui5-ecosystem-showcase/tree/main/packages/dev-approuter
40
+ */
41
+ async start(extensions = []) {
42
+ // loads env from default-env.json
43
+ xsenv.loadEnv();
44
+
45
+ const config = parseConfig();
46
+ const cwd = process.cwd();
47
+
48
+ // lookup the CAP server root
49
+ let capServerConfig;
50
+ const capModules = await findCAPModules({ cwd });
51
+ if (capModules.length > 1) {
52
+ throw new Error(`Multiple CAP modules found. The package dev-approuter can only handle one CAP module as dependency.`);
53
+ } else if (capModules.length === 1) {
54
+ capServerConfig = capModules[0];
55
+ }
56
+
57
+ // find all UI5 modules from the CAP server root and dependencies from the approuter
58
+ const ui5Modules = [...(await findUI5Modules({ cwd, skipLocalApps: true }))];
59
+ if (capServerConfig) {
60
+ ui5Modules.push(...(await findUI5Modules({ cwd: capServerConfig.modulePath, skipDeps: true })));
61
+ }
62
+
63
+ // collect UI5 middlewares
64
+ const ui5Middlewares = [];
65
+ for await (const ui5Module of ui5Modules) {
66
+ const { moduleId, modulePath, mountPath } = ui5Module;
67
+
68
+ // create a patched router
69
+ const router = await createPatchedRouter();
70
+
71
+ // apply the UI5 middlewares to the router
72
+ await applyUI5Middleware(router, {
73
+ basePath: modulePath,
74
+ configPath: modulePath,
75
+ });
76
+
77
+ // mounting the router for the UI5 application to the CAP server
78
+ console.log(`Mounting ${mountPath} to UI5 app ${modulePath}`);
79
+
80
+ let middlewareMountPath;
81
+ // define middlewareMountPath as `/_${mountPath}` if ui5 module is referenced as "dependency" in xs-dev.json or xs-app.json
82
+ if (config.dependencyRoutes && config.dependencyRoutes[moduleId]) {
83
+ // configure UI5 route
84
+ config.dependencyRoutes[moduleId] = configureUI5Route(moduleId, mountPath, config.dependencyRoutes[moduleId]);
85
+
86
+ middlewareMountPath = "/_" + mountPath;
87
+
88
+ // add destination for newly configured route
89
+ addDestination(moduleId, process.env.PORT, middlewareMountPath);
90
+ } else {
91
+ middlewareMountPath = mountPath;
92
+ }
93
+
94
+ // store the router for later registration
95
+ ui5Middlewares.push({
96
+ path: middlewareMountPath,
97
+ handler: router,
98
+ });
99
+ }
100
+
101
+ // start CAP server on different port
102
+ if (capServerConfig) {
103
+ const { modulePath, moduleId } = capServerConfig;
104
+
105
+ // start CAP server on different port
106
+ const app = express();
107
+ const { servicesPaths } = await applyCAPMiddleware(app, { root: modulePath, cwd });
108
+ app.listen(process.env.CAP_PORT || 4004, () => {
109
+ console.log(`CAP server started at: http://localhost:${process.env.CAP_PORT || 4004}`);
110
+ });
111
+
112
+ config.routes.unshift(configureCAPRoute(moduleId, servicesPaths, config.dependencyRoutes[moduleId]));
113
+ config.dependencyRoutes[moduleId] = configureCAPRoute(moduleId, servicesPaths, config.dependencyRoutes[moduleId]);
114
+
115
+ // add destination for newly configured route
116
+ addDestination(moduleId, process.env.CAP_PORT || 4004);
117
+ }
118
+
119
+ // create and start the SAP Approuter
120
+ // https://help.sap.com/docs/btp/sap-business-technology-platform/extension-api-of-application-router
121
+ approuter().start({
122
+ port: process.env.PORT || 5000,
123
+ xsappConfig: applyDependencyConfig(config),
124
+ extensions: [
125
+ {
126
+ insertMiddleware: {
127
+ first: ui5Middlewares,
128
+ },
129
+ },
130
+ ].concat(extensions),
131
+ });
132
+ console.log(`Approuter started at: http://localhost:${process.env.PORT || 5000}`);
133
+ }
145
134
  }
146
135
 
147
- module.exports = new DevApprouter();
136
+ module.exports = new DevApprouter();
package/lib/helpers.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const path = require("path");
2
+ const fs = require("fs");
2
3
 
3
4
  /**
4
5
  * Parses the approuter configuration from an `xs-dev.json` file.
@@ -8,32 +9,27 @@ const path = require("path");
8
9
  * @returns {Object} the approuter configuration including all `dependencyRoutes`.
9
10
  */
10
11
  const parseConfig = () => {
11
- let config;
12
- let configFile;
13
- let configFiles = ["xs-dev.json", "xs-app.json"];
14
- for (const file of configFiles) {
15
- if (fs.existsSync(path.join(process.cwd(), file))) {
16
- config = JSON.parse(
17
- fs.readFileSync(
18
- path.join(process.cwd(), file),
19
- { encoding: "utf8" }
20
- )
21
- );
22
- configFile = file;
23
- break;
24
- }
25
- }
26
- config.dependencyRoutes = {};
27
- config.routes?.forEach(route => {
28
- if (route.dependency) {
29
- if (config.dependencyRoutes[`${route.dependency}`]) {
30
- throw new Error(`Duplicate dependency "${route.dependency}" found in file ${path.join(process.cwd(), configFile)}.`);
31
- } else {
32
- config.dependencyRoutes[`${route.dependency}`] = route;
33
- }
34
- }
35
- });
36
- return config;
12
+ let config;
13
+ let configFile;
14
+ let configFiles = ["xs-dev.json", "xs-app.json"];
15
+ for (const file of configFiles) {
16
+ if (fs.existsSync(path.join(process.cwd(), file))) {
17
+ config = JSON.parse(fs.readFileSync(path.join(process.cwd(), file), { encoding: "utf8" }));
18
+ configFile = file;
19
+ break;
20
+ }
21
+ }
22
+ config.dependencyRoutes = {};
23
+ config.routes?.forEach((route) => {
24
+ if (route.dependency) {
25
+ if (config.dependencyRoutes[`${route.dependency}`]) {
26
+ throw new Error(`Duplicate dependency "${route.dependency}" found in file ${path.join(process.cwd(), configFile)}.`);
27
+ } else {
28
+ config.dependencyRoutes[`${route.dependency}`] = route;
29
+ }
30
+ }
31
+ });
32
+ return config;
37
33
  };
38
34
 
39
35
  /**
@@ -42,13 +38,13 @@ const parseConfig = () => {
42
38
  * @returns {Object} config - the approuter configuration that can be used to start the approuter.
43
39
  */
44
40
  const applyDependencyConfig = (config) => {
45
- config.routes?.forEach(route => {
46
- if (route.dependency) {
47
- route = config.dependencyRoutes[route.dependency];
48
- }
49
- });
50
- delete config.dependencyRoutes;
51
- return config;
41
+ config.routes?.forEach((route) => {
42
+ if (route.dependency) {
43
+ route = config.dependencyRoutes[route.dependency];
44
+ }
45
+ });
46
+ delete config.dependencyRoutes;
47
+ return config;
52
48
  };
53
49
 
54
50
  /**
@@ -59,36 +55,36 @@ const applyDependencyConfig = (config) => {
59
55
  * @param {String} mountPath - the path the module was mounted to and the destination should point to.
60
56
  */
61
57
  const addDestination = (moduleId, port, mountPath) => {
62
- let destinations = [];
63
- if (process.env.destinations) {
64
- destinations = JSON.parse(process.env.destinations);
65
- }
58
+ let destinations = [];
59
+ if (process.env.destinations) {
60
+ destinations = JSON.parse(process.env.destinations);
61
+ }
66
62
 
67
- let url;
68
- if (mountPath) {
69
- url = `http://localhost:${process.env.PORT || 5000}${mountPath}`;
70
- } else {
71
- url = `http://localhost:${port}`;
72
- }
63
+ let url;
64
+ if (mountPath) {
65
+ url = `http://localhost:${process.env.PORT || 5000}${mountPath}`;
66
+ } else {
67
+ url = `http://localhost:${port}`;
68
+ }
73
69
 
74
- // only add new destination if it's not already provided
75
- const destinationAlreadyExists = destinations.some(destination => {
76
- const lowerCaseDestination = {};
77
- Object.keys(destination).forEach(key => {
78
- lowerCaseDestination[key.toLowerCase()] = destination[key];
79
- })
80
- return lowerCaseDestination.name === moduleId
81
- })
82
- if (!destinationAlreadyExists) {
83
- destinations.push({
84
- Name: moduleId,
85
- Authentication: "NoAuthentication",
86
- ProxyType: "Internet",
87
- Type: "HTTP",
88
- URL: url
89
- });
90
- process.env.destinations = JSON.stringify(destinations);
91
- }
70
+ // only add new destination if it's not already provided
71
+ const destinationAlreadyExists = destinations.some((destination) => {
72
+ const lowerCaseDestination = {};
73
+ Object.keys(destination).forEach((key) => {
74
+ lowerCaseDestination[key.toLowerCase()] = destination[key];
75
+ });
76
+ return lowerCaseDestination.name === moduleId;
77
+ });
78
+ if (!destinationAlreadyExists) {
79
+ destinations.push({
80
+ Name: moduleId,
81
+ Authentication: "NoAuthentication",
82
+ ProxyType: "Internet",
83
+ Type: "HTTP",
84
+ URL: url,
85
+ });
86
+ process.env.destinations = JSON.stringify(destinations);
87
+ }
92
88
  };
93
89
 
94
90
  /**
@@ -99,38 +95,46 @@ const addDestination = (moduleId, port, mountPath) => {
99
95
  * @returns {Object} the configured route.
100
96
  */
101
97
  const configureCAPRoute = (moduleId, servicesPaths, route) => {
102
- route.source = servicesPaths.map(path => { return `${path}(.*)` }).join("|");
103
- route.destination = moduleId;
104
- delete route.dependency;
98
+ if (!route) {
99
+ route = {};
100
+ route.authenticationType = "none";
101
+ }
102
+ route.source = servicesPaths
103
+ .map((path) => {
104
+ return `${path}(.*)`;
105
+ })
106
+ .join("|");
107
+ route.destination = moduleId;
108
+ delete route.dependency;
105
109
 
106
- return route;
110
+ return route;
107
111
  };
108
112
 
109
113
  /**
110
114
  * Configures the route for a given UI5 module.
111
- * @param {String} moduleId - the id of the module that the route should be configured for.
115
+ * @param {String} moduleId - the id of the module that the route should be configured for.
112
116
  * @param {String} sourcePath - the path the approuter should handle the module at.
113
117
  * @param {Object} route - the route that is to be configured.
114
118
  * @returns {Object} the configured route.
115
119
  */
116
120
  const configureUI5Route = (moduleId, sourcePath, route) => {
117
- if (sourcePath === "/") {
118
- // special regex to avoid endless loop
119
- route.source = `^(?!.*(/_${sourcePath}))`;
120
- } else {
121
- route.source = `^${sourcePath}(.*)$`;
122
- route.target = "$1";
123
- }
124
- route.destination = moduleId;
125
- delete route.dependency;
121
+ if (sourcePath === "/") {
122
+ // special regex to avoid endless loop
123
+ route.source = `^(?!.*(/_${sourcePath}))`;
124
+ } else {
125
+ route.source = `^${sourcePath}(.*)$`;
126
+ route.target = "$1";
127
+ }
128
+ route.destination = moduleId;
129
+ delete route.dependency;
126
130
 
127
- return route;
131
+ return route;
128
132
  };
129
133
 
130
134
  module.exports = {
131
- parseConfig,
132
- applyDependencyConfig,
133
- addDestination,
134
- configureCAPRoute,
135
- configureUI5Route
136
- };
135
+ parseConfig,
136
+ applyDependencyConfig,
137
+ addDestination,
138
+ configureCAPRoute,
139
+ configureUI5Route,
140
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev-approuter",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A dev time wrapper for the SAP Application Router that can serve UI5 and CAP modules added as dependencies.",
5
5
  "author": "Nico Schoenteich <nicolai.schoenteich@sap.com> (https://github.com/nicoschoenteich)",
6
6
  "license": "Apache-2.0",
@@ -17,5 +17,5 @@
17
17
  "path": "^0.12.7",
18
18
  "ui5-middleware-cap": "^3.1.0"
19
19
  },
20
- "gitHead": "4a6a4401cc13120cb5f91bba8c116dce78c3812b"
20
+ "gitHead": "3e8e6d7625a544cb6d1a329cc66a8d796e1c969f"
21
21
  }