refactor(mocks): load the webout guard as a module, not by eval
Build / Build-and-ng-test (pull_request) Canceled after 3m3s
Build / Build-and-test-development (pull_request) Canceled after 0s
Lighthouse Checks / lighthouse (pull_request) Canceled after 0s

The guard is the tree's one shared rule, but each of the three services that
needs it repeated the path derivation and then eval'd a file read out of the
SASjs Drive at run time - which is what the file's own header said this tree does
not need.  weboutGuard.js now exports the function and the services require it by
absolute path: one copy of the function, no eval, and a load failure is a
require error naming the file rather than a silent no-op.

Being a module also means the guard has to carry its own dependencies: as an
eval'd snippet `fs` came from the program's scope, and the first run as a
required module failed with "fs is not defined".  It now requires fs itself.

dcMockUtils.requireMemberMetadata remains the source of truth for the rule and
both files say so, since a site may run both trees and an operator reads
whichever one threw.
This commit is contained in:
dc committed 2026-11-04 16:48:07 +00:00
1 parent 744c89b919
commit 331460ad25
5 files changed
+37 -40

No files matched your search

@@ -3,17 +3,14 @@ const nodePath = require('path')
const appLoc = nodePath.join(..._program.split('services')[0].split('/'))
const sasjsRoot = nodePath.resolve(weboutPath, '..', '..', '..')
eval(
fs.readFileSync(
nodePath.resolve(
sasjsRoot,
'drive',
'files',
appLoc,
'services',
'weboutGuard.js'
),
'utf8'
const requireMemberMetadata = require(
nodePath.resolve(
sasjsRoot,
'drive',
'files',
appLoc,
'services',
'weboutGuard.js'
)
)
@@ -3,17 +3,14 @@ const nodePath = require('path')
const appLoc = nodePath.join(..._program.split('services')[0].split('/'))
const sasjsRoot = nodePath.resolve(weboutPath, '..', '..', '..')
eval(
fs.readFileSync(
nodePath.resolve(
sasjsRoot,
'drive',
'files',
appLoc,
'services',
'weboutGuard.js'
),
'utf8'
const requireMemberMetadata = require(
nodePath.resolve(
sasjsRoot,
'drive',
'files',
appLoc,
'services',
'weboutGuard.js'
)
)
@@ -3,17 +3,14 @@ const nodePath = require('path')
const appLoc = nodePath.join(..._program.split('services')[0].split('/'))
const sasjsRoot = nodePath.resolve(weboutPath, '..', '..', '..')
eval(
fs.readFileSync(
nodePath.resolve(
sasjsRoot,
'drive',
'files',
appLoc,
'services',
'weboutGuard.js'
),
'utf8'
const requireMemberMetadata = require(
nodePath.resolve(
sasjsRoot,
'drive',
'files',
appLoc,
'services',
'weboutGuard.js'
)
)
+10 -5
View File
@@ -2,11 +2,12 @@
* @file weboutGuard.js
* @brief Refuse a webout() member whose column metadata did not reach the response.
*
* The sasjs tree keeps this beside its other shared utilities in
* `sas/mocks/sasjs/services/dcMockUtils.js`; this tree is one file per service
* with no shared module, so the services that pass `{ showmeta }` - getdata,
* getcolvals and viewdata - load this file instead, to keep the rule in one
* place rather than copied into each of them.
* This is the sas9 tree's copy of the rule, and `requireMemberMetadata` in
* `sas/mocks/sasjs/services/dcMockUtils.js` is the source of truth for it: the
* two must say the same thing, because an operator reads whichever one their
* tree throws and a site may run both. The services that pass `{ showmeta }` -
* getdata, getcolvals and viewdata - require this file rather than repeating
* the check.
*
* Call it immediately after the `webout('OBJ', ...)` that carried the option,
* and before `webout('CLOSE')`: a throw after CLOSE leaves a complete body
@@ -24,6 +25,8 @@
* @param {string} bodyPath the response body the program is writing
* @param {string} member the member the caller wrote
*/
const fs = require('fs')
function requireMemberMetadata(bodyPath, member) {
let body
try {
@@ -57,3 +60,5 @@ function requireMemberMetadata(bodyPath, member) {
'later, on the host running this program.'
)
}
module.exports = requireMemberMetadata
+3 -2
View File
@@ -46,8 +46,9 @@ const MONTHS_NUM = {JAN:'01',FEB:'02',MAR:'03',APR:'04',MAY:'05',JUN:'06',JUL:'0
* `@sasjs/server`'s `^4.4.0` range can resolve on releases before 3.12.3 -
* writes the rows and drops the metadata silently. The response still looks
* complete, so the estate quietly tests less than it claims. Refuse it instead,
* and name the cause. The sas9 tree carries the same function in
* `sas/mocks/sas9/sasjs/services/weboutGuard.js`; keep the two saying one thing.
* and name the cause. This function is the source of truth for the rule; the
* sas9 tree's `sas/mocks/sas9/sasjs/services/weboutGuard.js` is a copy of it and
* the two must say one thing, because an operator reads whichever tree threw.
*
* Testing the output rather than the function is deliberate: an arity check
* (`webout.length`) stops counting at the first default or rest parameter, so a