From 96d7c66f46ceb3311b0c9198f7fc23cb3c5a977b Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 4 May 2026 17:55:19 -0400 Subject: [PATCH 1/6] MLE-28498 Permission fixes This contains permission fixes for testing as a result of the 11.3.5 changes. --- .../security/roles/rest-evaluator.json | 21 +++++++++++++++---- .../security/roles/rest-invoke-user.json | 11 ++++++++++ .../ml-config/security/users/rest-admin.json | 7 +++++-- .../ml-config/security/users/rest-reader.json | 6 ++---- .../security/users/rest-temporal-writer.json | 6 ++---- .../security/users/rest-transform-user.json | 4 ++++ .../ml-config/security/users/rest-writer.json | 3 ++- 7 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 test-app/src/main/ml-config/security/roles/rest-invoke-user.json create mode 100644 test-app/src/main/ml-config/security/users/rest-transform-user.json diff --git a/test-app/src/main/ml-config/security/roles/rest-evaluator.json b/test-app/src/main/ml-config/security/roles/rest-evaluator.json index 476c59cb..f7acb257 100644 --- a/test-app/src/main/ml-config/security/roles/rest-evaluator.json +++ b/test-app/src/main/ml-config/security/roles/rest-evaluator.json @@ -1,9 +1,7 @@ { "role-name": "rest-evaluator", "description": "REST writer who can eval, invoke, or set a dynamic databases", - "role": [ - "rest-writer" - ], + "role": ["rest-writer", "sparql-update-user"], "privilege": [ { "privilege-name": "xdmp-eval", @@ -49,6 +47,21 @@ "privilege-name": "xdmp-get-session-field", "action": "http://marklogic.com/xdmp/privileges/xdmp-get-session-field", "kind": "execute" + }, + { + "privilege-name": "xdmp-login", + "action": "http://marklogic.com/xdmp/privileges/xdmp-login", + "kind": "execute" + }, + { + "privilege-name": "unprotected-collections", + "action": "http://marklogic.com/xdmp/privileges/unprotected-collections", + "kind": "execute" + }, + { + "privilege-name": "xdmp-xslt-invoke", + "action": "http://marklogic.com/xdmp/privileges/xslt-invoke", + "kind": "execute" } ] -} \ No newline at end of file +} diff --git a/test-app/src/main/ml-config/security/roles/rest-invoke-user.json b/test-app/src/main/ml-config/security/roles/rest-invoke-user.json new file mode 100644 index 00000000..1c1d6bc4 --- /dev/null +++ b/test-app/src/main/ml-config/security/roles/rest-invoke-user.json @@ -0,0 +1,11 @@ +{ + "role-name": "rest-invoke-user", + "description": "Role granting xdmp:login privilege needed for REST transform invocations with different-transaction isolation", + "privilege": [ + { + "privilege-name": "xdmp-login", + "action": "http://marklogic.com/xdmp/privileges/xdmp-login", + "kind": "execute" + } + ] +} diff --git a/test-app/src/main/ml-config/security/users/rest-admin.json b/test-app/src/main/ml-config/security/users/rest-admin.json index 1d61dd6f..6936f7df 100644 --- a/test-app/src/main/ml-config/security/users/rest-admin.json +++ b/test-app/src/main/ml-config/security/users/rest-admin.json @@ -3,6 +3,9 @@ "description": "rest-admin user", "password": "x", "role": [ - "rest-admin" + "rest-admin", + "rest-evaluator", + "rest-extension-user", + "sparql-update-user" ] -} \ No newline at end of file +} diff --git a/test-app/src/main/ml-config/security/users/rest-reader.json b/test-app/src/main/ml-config/security/users/rest-reader.json index 7cdeed28..cf511ca6 100644 --- a/test-app/src/main/ml-config/security/users/rest-reader.json +++ b/test-app/src/main/ml-config/security/users/rest-reader.json @@ -2,7 +2,5 @@ "user-name": "rest-reader", "description": "rest-reader user", "password": "x", - "role": [ - "rest-reader" - ] -} \ No newline at end of file + "role": ["rest-reader", "rest-extension-user", "rest-invoke-user"] +} diff --git a/test-app/src/main/ml-config/security/users/rest-temporal-writer.json b/test-app/src/main/ml-config/security/users/rest-temporal-writer.json index aa163780..64528027 100644 --- a/test-app/src/main/ml-config/security/users/rest-temporal-writer.json +++ b/test-app/src/main/ml-config/security/users/rest-temporal-writer.json @@ -2,7 +2,5 @@ "user-name": "rest-temporal-writer", "description": "rest-writer user with temporal privileges", "password": "x", - "role": [ - "rest-temporal-writer" - ] -} \ No newline at end of file + "role": ["rest-temporal-writer", "rest-extension-user"] +} diff --git a/test-app/src/main/ml-config/security/users/rest-transform-user.json b/test-app/src/main/ml-config/security/users/rest-transform-user.json new file mode 100644 index 00000000..cbad1d76 --- /dev/null +++ b/test-app/src/main/ml-config/security/users/rest-transform-user.json @@ -0,0 +1,4 @@ +{ + "user-name": "rest-transform-user", + "role": ["rest-transform-internal", "rest-reader", "rest-invoke-user"] +} diff --git a/test-app/src/main/ml-config/security/users/rest-writer.json b/test-app/src/main/ml-config/security/users/rest-writer.json index 3dfe87db..47469e21 100644 --- a/test-app/src/main/ml-config/security/users/rest-writer.json +++ b/test-app/src/main/ml-config/security/users/rest-writer.json @@ -5,6 +5,7 @@ "role": [ "rest-writer", "rest-evaluator", - "temporal-admin" + "temporal-admin", + "rest-extension-user" ] } From 0df651fc8b6abde720147135dbe2a1e8141e05d2 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 4 May 2026 17:56:33 -0400 Subject: [PATCH 2/6] MLE-28498 transform test fixes Updated transforms tests to expect rest-transform-user as a result of 11.3.5 changes from ML-28684. --- test-basic/documents-transform.js | 379 ++++++++++--------- test-complete/nodejs-transform-javascript.js | 230 +++++------ 2 files changed, 324 insertions(+), 285 deletions(-) diff --git a/test-basic/documents-transform.js b/test-basic/documents-transform.js index 99ea76c3..797c7357 100644 --- a/test-basic/documents-transform.js +++ b/test-basic/documents-transform.js @@ -1,229 +1,268 @@ /* -* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. -*/ -var should = require('should'); + * Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. + */ +var should = require("should"); -var fs = require('fs'); -var valcheck = require('core-util-is'); +var fs = require("fs"); +var valcheck = require("core-util-is"); -var testconfig = require('../etc/test-config.js'); +var testconfig = require("../etc/test-config.js"); -var marklogic = require('../'); +var marklogic = require("../"); var q = marklogic.queryBuilder; var db = marklogic.createDatabaseClient(testconfig.restWriterConnection); -var restAdminDB = marklogic.createDatabaseClient(testconfig.restAdminConnection); +var restAdminDB = marklogic.createDatabaseClient( + testconfig.restAdminConnection, +); -describe('document transform', function(){ - var xqyTransformName = 'flagParam'; - var xqyTransformPath = './test-basic/data/flagTransform.xqy'; - describe('when configuring', function() { - it('should write the transform with positional parameters', function(done){ +describe("document transform", function () { + var xqyTransformName = "flagParam"; + var xqyTransformPath = "./test-basic/data/flagTransform.xqy"; + describe("when configuring", function () { + it("should write the transform with positional parameters", function (done) { this.timeout(3000); - restAdminDB.config.transforms.write(xqyTransformName, 'xquery', fs.createReadStream(xqyTransformPath)) - .result(function(response){ - done(); + restAdminDB.config.transforms + .write( + xqyTransformName, + "xquery", + fs.createReadStream(xqyTransformPath), + ) + .result(function (response) { + done(); }) - .catch(done); + .catch(done); }); - it('should write the transform with named parameters', function(done){ + it("should write the transform with named parameters", function (done) { this.timeout(3000); - restAdminDB.config.transforms.write({ - name: xqyTransformName, - title: 'The Flag Transform', - description: 'Raising the flag.', - provider: 'Banner Business', - version: 0.1, - format: 'xquery', - source: fs.createReadStream(xqyTransformPath), - transformParams: { - 'flag': 'xs:string?', - 'count': 'xs:integer*' - } - }) - .result(function(response){ - done(); - }) - .catch(done); + restAdminDB.config.transforms + .write({ + name: xqyTransformName, + title: "The Flag Transform", + description: "Raising the flag.", + provider: "Banner Business", + version: 0.1, + format: "xquery", + source: fs.createReadStream(xqyTransformPath), + transformParams: { + flag: "xs:string?", + count: "xs:integer*", + }, + }) + .result(function (response) { + done(); + }) + .catch(done); }); - it('should read the transform', function(done){ - restAdminDB.config.transforms.read(xqyTransformName) - .result(function(source){ - (!valcheck.isNullOrUndefined(source)).should.equal(true); - (typeof source).should.equal('string'); - done(); + it("should read the transform", function (done) { + restAdminDB.config.transforms + .read(xqyTransformName) + .result(function (source) { + (!valcheck.isNullOrUndefined(source)).should.equal(true); + (typeof source).should.equal("string"); + done(); }) - .catch(done); + .catch(done); }); - it('should list the transform', function(done){ - db.config.transforms.list() - .result(function(response){ - response.should.have.property('transforms'); - response.transforms.should.have.property('transform'); - response.transforms.transform.length.should.be.greaterThan(0); - - const flagTransform = response.transforms.transform.find(function(item){ - return item.name === xqyTransformName; - }); - should.exist(flagTransform); - flagTransform.should.have.property('transform-parameters'); - flagTransform['transform-parameters'].should.have.property('parameter'); - - const params = flagTransform['transform-parameters'].parameter; - params.should.be.an.Array(); - params.length.should.equal(2); - params.some(function(p){ - return p['parameter-name'] === 'flag' && p['parameter-type'] === 'xs:string?'; - }).should.equal(true); - params.some(function(p){ - return p['parameter-name'] === 'count' && p['parameter-type'] === 'xs:integer*'; - }).should.equal(true); - done(); - }) - .catch(done); + it("should list the transform", function (done) { + db.config.transforms + .list() + .result(function (response) { + response.should.have.property("transforms"); + response.transforms.should.have.property("transform"); + response.transforms.transform.length.should.be.greaterThan(0); + + const flagTransform = response.transforms.transform.find( + function (item) { + return item.name === xqyTransformName; + }, + ); + should.exist(flagTransform); + flagTransform.should.have.property("transform-parameters"); + flagTransform["transform-parameters"].should.have.property( + "parameter", + ); + + const params = flagTransform["transform-parameters"].parameter; + params.should.be.an.Array(); + params.length.should.equal(2); + params + .some(function (p) { + return ( + p["parameter-name"] === "flag" && + p["parameter-type"] === "xs:string?" + ); + }) + .should.equal(true); + params + .some(function (p) { + return ( + p["parameter-name"] === "count" && + p["parameter-type"] === "xs:integer*" + ); + }) + .should.equal(true); + done(); + }) + .catch(done); }); - it('should delete the transform', function(done){ - restAdminDB.config.transforms.remove(xqyTransformName) - .result(function(response){ - done(); + it("should delete the transform", function (done) { + restAdminDB.config.transforms + .remove(xqyTransformName) + .result(function (response) { + done(); }) - .catch(done); + .catch(done); }); // TODO: test streaming of source and list }); - describe('when using XQuery', function() { - var uri = '/test/write/transformContent1.json'; - before(function(done){ + describe("when using XQuery", function () { + var uri = "/test/write/transformContent1.json"; + before(function (done) { this.timeout(3000); - restAdminDB.config.transforms.write(xqyTransformName, 'xquery', fs.createReadStream(xqyTransformPath)) - .result(function(response){ - done(); + restAdminDB.config.transforms + .write( + xqyTransformName, + "xquery", + fs.createReadStream(xqyTransformPath), + ) + .result(function (response) { + done(); }) - .catch(done); + .catch(done); }); - it('should modify during write', function(done){ - db.documents.write({ - uri: uri, - contentType: 'application/json', - content: {key1: 'value 1'}, - transform: [xqyTransformName, {flag:'tested1'}] + it("should modify during write", function (done) { + db.documents + .write({ + uri: uri, + contentType: "application/json", + content: { key1: "value 1" }, + transform: [xqyTransformName, { flag: "tested1" }], }) - .result(function(response) { - return db.documents.read(uri).result(); + .result(function (response) { + return db.documents.read(uri).result(); }) - .then(function(documents) { - documents.length.should.equal(1); - documents[0].content.flagParam.should.equal('tested1'); - done(); + .then(function (documents) { + documents.length.should.equal(1); + documents[0].content.flagParam.should.equal("tested1"); + done(); }) - .catch(done); + .catch(done); }); - it('should modify during read', function(done){ - db.documents.read({ - uris: uri, - transform: [xqyTransformName, {flag:'tested2'}] + it("should modify during read", function (done) { + db.documents + .read({ + uris: uri, + transform: [xqyTransformName, { flag: "tested2" }], }) - .result(function(documents) { - documents.length.should.equal(1); - documents[0].content.flagParam.should.equal('tested2'); - done(); + .result(function (documents) { + documents.length.should.equal(1); + documents[0].content.flagParam.should.equal("tested2"); + done(); }) - .catch(done); + .catch(done); }); - it('should modify during query', function(done){ - db.documents.query( - q.where( - q.document(uri) - ). - slice(0, 10, - q.transform(xqyTransformName, {flag:'tested3'}) - ) - ) - .result(function(documents) { + it("should modify during query", function (done) { + db.documents + .query( + q + .where(q.document(uri)) + .slice(0, 10, q.transform(xqyTransformName, { flag: "tested3" })), + ) + .result(function (documents) { documents.length.should.equal(1); - documents[0].content.flagParam.should.equal('tested3'); + documents[0].content.flagParam.should.equal("tested3"); done(); - }) + }) .catch(done); }); }); - describe('when using JavaScript', function() { - var jsTransformName = 'timestamp'; - var jsTransformPath = './test-basic/data/timestampTransform.js'; - var readUri = '/test/write/readTransform.json'; - var writeUri = '/test/write/writeTransform.json'; - before(function(done){ + describe("when using JavaScript", function () { + var jsTransformName = "timestamp"; + var jsTransformPath = "./test-basic/data/timestampTransform.js"; + var readUri = "/test/write/readTransform.json"; + var writeUri = "/test/write/writeTransform.json"; + before(function (done) { this.timeout(3000); - restAdminDB.config.transforms.write(jsTransformName, 'javascript', fs.createReadStream(jsTransformPath)) - .result(function(response){ - return db.documents.write({ - uri: readUri, - contentType: 'application/json', - content: {readKey: 'read value'} - }).result(); - }) - .then(function(response){done();}) - .catch(done); + restAdminDB.config.transforms + .write( + jsTransformName, + "javascript", + fs.createReadStream(jsTransformPath), + ) + .result(function (response) { + return db.documents + .write({ + uri: readUri, + contentType: "application/json", + content: { readKey: "read value" }, + }) + .result(); + }) + .then(function (response) { + done(); + }) + .catch(done); }); - it('should modify during write', function(done){ - db.documents.write({ - uri: writeUri, - contentType: 'application/json', - content: {writeKey: 'write value'}, - transform: jsTransformName + it("should modify during write", function (done) { + db.documents + .write({ + uri: writeUri, + contentType: "application/json", + content: { writeKey: "write value" }, + transform: jsTransformName, }) - .result(function(response) { - return db.documents.read(writeUri).result(); + .result(function (response) { + return db.documents.read(writeUri).result(); }) - .then(function(documents) { - documents.length.should.equal(1); - documents[0].content.should.have.property('timestamp'); - documents[0].content.should.have.property('userName'); - documents[0].content.userName.should.eql('rest-writer'); - done(); + .then(function (documents) { + documents.length.should.equal(1); + documents[0].content.should.have.property("timestamp"); + documents[0].content.should.have.property("userName"); + documents[0].content.userName.should.eql("rest-transform-user"); // MLE-28684: transforms now run as rest-transform-user + done(); }) - .catch(done); + .catch(done); }); - it('should modify during read', function(done){ - db.documents.read({ - uris: readUri, - transform: jsTransformName + it("should modify during read", function (done) { + db.documents + .read({ + uris: readUri, + transform: jsTransformName, }) - .result(function(documents) { - documents.length.should.equal(1); - documents[0].content.should.have.property('timestamp'); - documents[0].content.should.have.property('userName'); - documents[0].content.userName.should.eql('rest-writer'); - done(); + .result(function (documents) { + documents.length.should.equal(1); + documents[0].content.should.have.property("timestamp"); + documents[0].content.should.have.property("userName"); + documents[0].content.userName.should.eql("rest-transform-user"); // MLE-28684: transforms now run as rest-transform-user + done(); }) - .catch(done); + .catch(done); }); - it('should modify during query', function(done){ - db.documents.query( - q.where( - q.document(readUri) - ). - slice(0, 10, - q.transform(jsTransformName) - ) - ) - .result(function(documents) { + it("should modify during query", function (done) { + db.documents + .query( + q + .where(q.document(readUri)) + .slice(0, 10, q.transform(jsTransformName)), + ) + .result(function (documents) { documents.length.should.equal(1); - documents[0].content.should.have.property('timestamp'); - documents[0].content.should.have.property('userName'); - documents[0].content.userName.should.eql('rest-writer'); + documents[0].content.should.have.property("timestamp"); + documents[0].content.should.have.property("userName"); + documents[0].content.userName.should.eql("rest-transform-user"); // MLE-28684: transforms now run as rest-transform-user done(); - }) + }) .catch(done); }); }); diff --git a/test-complete/nodejs-transform-javascript.js b/test-complete/nodejs-transform-javascript.js index 9f9e16ab..63554dc8 100644 --- a/test-complete/nodejs-transform-javascript.js +++ b/test-complete/nodejs-transform-javascript.js @@ -1,112 +1,113 @@ /* -* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. -*/ -var should = require('should'); -var fs = require('fs'); -var concatStream = require('concat-stream'); -var valcheck = require('core-util-is'); + * Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. + */ +var should = require("should"); +var fs = require("fs"); +var concatStream = require("concat-stream"); +var valcheck = require("core-util-is"); -var testconfig = require('../etc/test-config-qa.js'); +var testconfig = require("../etc/test-config-qa.js"); -var marklogic = require('../'); +var marklogic = require("../"); var q = marklogic.queryBuilder; var db = marklogic.createDatabaseClient(testconfig.restReaderConnection); var dbWriter = marklogic.createDatabaseClient(testconfig.restWriterConnection); var dbAdmin = marklogic.createDatabaseClient(testconfig.restAdminConnection); -describe('Transform test with javascript', function () { - before(function (done) { - this.timeout(10000); - dbWriter.documents.write({ - uri: '/test/transform/jstransform.json', - contentType: 'application/json', - content: { title: 'transform test with javascript' } +describe("Transform test with javascript", function () { + before(function (done) { + this.timeout(10000); + dbWriter.documents + .write( + { + uri: "/test/transform/jstransform.json", + contentType: "application/json", + content: { title: "transform test with javascript" }, }, { - uri: '/test/transform/jstransform2.json', - contentType: 'application/json', - content: { title: 'exapmle test with javascript' } - }). - result(function (response) { - done(); - }, done); - }); + uri: "/test/transform/jstransform2.json", + contentType: "application/json", + content: { title: "exapmle test with javascript" }, + }, + ) + .result(function (response) { + done(); + }, done); + }); - var transformName = 'timestamp'; - var transformPath = __dirname + '/data/timestampTransform.js'; + var transformName = "timestamp"; + var transformPath = __dirname + "/data/timestampTransform.js"; - it('should write the transform', function (done) { - this.timeout(10000); - fs.createReadStream(transformPath). - pipe(concatStream({ encoding: 'string' }, function (source) { - dbAdmin.config.transforms.write(transformName, 'javascript', source). - result(function (response) { - done(); - }, done); - })); - }); - - it('should read the transform', function (done) { - dbAdmin.config.transforms.read(transformName). - result(function (source) { - (!valcheck.isNullOrUndefined(source)).should.equal(true); - done(); - }, done); - }); + it("should write the transform", function (done) { + this.timeout(10000); + fs.createReadStream(transformPath).pipe( + concatStream({ encoding: "string" }, function (source) { + dbAdmin.config.transforms + .write(transformName, "javascript", source) + .result(function (response) { + done(); + }, done); + }), + ); + }); - it('should list the transform', function (done) { - dbAdmin.config.transforms.list(). - result(function (response) { - response.should.have.property('transforms'); - done(); - }, done); - }); + it("should read the transform", function (done) { + dbAdmin.config.transforms.read(transformName).result(function (source) { + (!valcheck.isNullOrUndefined(source)).should.equal(true); + done(); + }, done); + }); - var uri = '/test/transform/jstransform.json'; + it("should list the transform", function (done) { + dbAdmin.config.transforms.list().result(function (response) { + response.should.have.property("transforms"); + done(); + }, done); + }); - it('should modify during read', function (done) { - db.documents.read({ - uris: uri, - transform: transformName - }). - result(function (response) { - //console.log(JSON.stringify(response, null, 4)); - response[0].content.should.have.property('timestamp'); - response[0].content.userName.should.equal('rest-reader'); - done(); - }, done); - }); + var uri = "/test/transform/jstransform.json"; - it('should query', function (done) { - db.documents.query( - q.where( - q.word('title', 'test') - ) - ). - result(function (response) { - //console.log(JSON.stringify(response, null, 4)); - response.length.should.equal(2); - done(); - }, done); - }); + it("should modify during read", function (done) { + db.documents + .read({ + uris: uri, + transform: transformName, + }) + .result(function (response) { + //console.log(JSON.stringify(response, null, 4)); + response[0].content.should.have.property("timestamp"); + response[0].content.userName.should.equal("rest-transform-user"); // MLE-28684: transforms now run as rest-transform-user + done(); + }) + .catch(done); + }); + it("should query", function (done) { + db.documents.query(q.where(q.word("title", "test"))).result(function ( + response, + ) { + //console.log(JSON.stringify(response, null, 4)); + response.length.should.equal(2); + done(); + }, done); + }); - it('should modify during query', function () { - this.timeout(10000); - db.documents.query( - q.where( - q.word('title', 'transform') - ). - slice(0, 10, q.transform(transformName)) - ). - result(function (response) { - //console.log(JSON.stringify(response, null, 4)); - response[0].content.should.have.property('timestamp'); - response[0].content.userName.should.equal('rest-reader'); - }); - }); - /*it('should modify during query - synch', function(){ + it("should modify during query", function () { + this.timeout(10000); + db.documents + .query( + q + .where(q.word("title", "transform")) + .slice(0, 10, q.transform(transformName)), + ) + .result(function (response) { + //console.log(JSON.stringify(response, null, 4)); + response[0].content.should.have.property("timestamp"); + response[0].content.userName.should.equal("rest-reader"); + }); + }); + /*it('should modify during query - synch', function(){ this.timeout(10000); db.documents.query( q.where( @@ -120,21 +121,20 @@ describe('Transform test with javascript', function () { response[0].content.userName.should.equal('rest-reader'); }); });*/ - it('should modify during query , slice without paging parameters, Bug 111', function (done) { - db.documents.query( - q.where( - q.word('title', 'transform') - ). - slice(q.transform(transformName)) - ). - result(function (response) { - //console.log(JSON.stringify(response, null, 4)); - response[0].content.should.have.property('timestamp'); - response[0].content.userName.should.equal('rest-reader'); - done(); - }, done); - }); - /*it('should modify during write', function(done){ + it("should modify during query , slice without paging parameters, Bug 111", function (done) { + db.documents + .query( + q.where(q.word("title", "transform")).slice(q.transform(transformName)), + ) + .result(function (response) { + //console.log(JSON.stringify(response, null, 4)); + response[0].content.should.have.property("timestamp"); + response[0].content.userName.should.equal("rest-transform-user"); // MLE-28684: transforms now run as rest-transform-user + done(); + }) + .catch(done); + }); + /*it('should modify during write', function(done){ dbWriter.documents.write({ uri: '/test/transform/write/jstransform.json', contentType: 'application/json', @@ -152,14 +152,14 @@ describe('Transform test with javascript', function () { }, done); });*/ - it('should remove the documents', function (done) { - dbAdmin.documents.removeAll({ - directory: '/test/transform/' - }). - result(function (response) { - response.should.be.ok; - done(); - }, done); - }); - + it("should remove the documents", function (done) { + dbAdmin.documents + .removeAll({ + directory: "/test/transform/", + }) + .result(function (response) { + response.should.be.ok; + done(); + }, done); + }); }); From fc83da07ad68cee4b18e70d70dacd43a5d315599 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 4 May 2026 17:58:07 -0400 Subject: [PATCH 3/6] MLE-28498 dmsdk fixes Copilot fix for flakey dmsdk tests --- test-complete/nodejs-dmsdk-readall-1.js | 351 +++++++++++++----------- 1 file changed, 185 insertions(+), 166 deletions(-) diff --git a/test-complete/nodejs-dmsdk-readall-1.js b/test-complete/nodejs-dmsdk-readall-1.js index e2b2c0a1..ca9615ac 100644 --- a/test-complete/nodejs-dmsdk-readall-1.js +++ b/test-complete/nodejs-dmsdk-readall-1.js @@ -1,200 +1,219 @@ /* -* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. -*/ -var fs = require('fs'); -const path = require('path'); + * Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. + */ +var fs = require("fs"); +const path = require("path"); -var marklogic = require('../'); -var testconfig = require('../etc/test-config-qa.js'); +var marklogic = require("../"); +var testconfig = require("../etc/test-config-qa.js"); -const stream = require('stream'); -const { expect } = require('chai'); -const streamToArray = require('stream-to-array'); +const stream = require("stream"); +const { expect } = require("chai"); +const streamToArray = require("stream-to-array"); let resultUris = []; let resulContents = []; const selectFiles = []; var uriStream = new stream.Readable(); -var dbWriter = marklogic.createDatabaseClient(testconfig.dmsdkrestWriterConnection); +var dbWriter = marklogic.createDatabaseClient( + testconfig.dmsdkrestWriterConnection, +); let inputJsonUris = []; let inputContents = []; function verifyArrays(arr1, arr2) { - var isEqual = false; - if (arr1.length !== arr2.length) { - return false; + var isEqual = false; + if (arr1.length !== arr2.length) { + return false; + } + for (var i = 0; i < arr1.length; i++) { + for (const e of arr2) { + if (e === arr1[i]) { + isEqual = true; + break; + } } - for (var i = 0; i < arr1.length; i++) { - for (const e of arr2) { - if (e === arr1[i]) { - isEqual = true; - break; - } - } - } - return isEqual; + } + return isEqual; } function verifyCurrentContents(currResContent) { - var isContentEqual = false; - var currResValue = Object.values(currResContent).valueOf()[0]; - // For each object in resultsContents array - for (var i = 0; i < inputContents.length; i++) { - var c = inputContents[i]; - var cInputValue = Object.values(c).valueOf()[0]; - if (cInputValue === currResValue) { - isContentEqual = true; - break; - } + var isContentEqual = false; + var currResValue = Object.values(currResContent).valueOf()[0]; + // For each object in resultsContents array + for (var i = 0; i < inputContents.length; i++) { + var c = inputContents[i]; + var cInputValue = Object.values(c).valueOf()[0]; + if (cInputValue === currResValue) { + isContentEqual = true; + break; } - return isContentEqual; + } + return isContentEqual; } -describe('readAll-tests-one', function () { - before(function (done) { - this.timeout(20000); - //const selectFiles = []; - var jsonDocreadable = new stream.Readable({ objectMode: true }); - var multiDocreadable = new stream.Readable({ objectMode: true }); - // Handle only .json, .xml and .txt files - var allowedFiles = ['.json', '.xml', '.txt']; +describe("readAll-tests-one", function () { + before(function (done) { + this.timeout(20000); + //const selectFiles = []; + var jsonDocreadable = new stream.Readable({ objectMode: true }); + var multiDocreadable = new stream.Readable({ objectMode: true }); + // Handle only .json, .xml and .txt files + var allowedFiles = [".json", ".xml", ".txt"]; - const dirPath = path.join(__dirname, '/data/dmsdk/writeall/'); + const dirPath = path.join(__dirname, "/data/dmsdk/writeall/"); - function includeFile(fName) { - var fileExt = path.extname(fName); - return allowedFiles.includes(fileExt); - } + function includeFile(fName) { + var fileExt = path.extname(fName); + return allowedFiles.includes(fileExt); + } - var files = fs.readdirSync(dirPath).filter(includeFile); - files.forEach(file => { - let fileStat = fs.statSync(dirPath + '/' + file).isDirectory(); - if (!fileStat) { - selectFiles.push(file); - } - }); + var files = fs.readdirSync(dirPath).filter(includeFile); + files.forEach((file) => { + let fileStat = fs.statSync(dirPath + "/" + file).isDirectory(); + if (!fileStat) { + selectFiles.push(file); + } + }); - for (var file of selectFiles) { - var fileTobeRead = dirPath + file; - var data = fs.readFileSync(fileTobeRead, { encoding: 'utf8' }); - var findCType = path.extname(fileTobeRead); - var jsonFN1 = { - uri: file, - contentType: findCType === '.json' ? 'application/json' : findCType === '.xml' ? 'application/xml' : 'application/text', - collections: ['qatestsReadAll'], - content: data - }; - multiDocreadable.push(jsonFN1); - } - multiDocreadable.push(null); - dbWriter.documents.writeAll(multiDocreadable, { - onCompletion: ((summary) => { - summary.docsWrittenSuccessfully.should.be.greaterThanOrEqual(6); - }) - }); // End of pipe to writeAll - single byte + for (var file of selectFiles) { + var fileTobeRead = dirPath + file; + var data = fs.readFileSync(fileTobeRead, { encoding: "utf8" }); + var findCType = path.extname(fileTobeRead); + var jsonFN1 = { + uri: file, + contentType: + findCType === ".json" + ? "application/json" + : findCType === ".xml" + ? "application/xml" + : "application/text", + collections: ["qatestsReadAll"], + content: data, + }; + multiDocreadable.push(jsonFN1); + } + multiDocreadable.push(null); + dbWriter.documents.writeAll(multiDocreadable, { + onCompletion: (summary) => { + summary.docsWrittenSuccessfully.should.be.greaterThanOrEqual(6); + }, + }); // End of pipe to writeAll - single byte - for (let i = 0; i < 10; i++) { - const tempJson = { - uri: '/data/dmsdk/readAll-tests-one/' + i + '.json', - contentType: 'application/json', - content: { ['key ' + i]: 'value ' + i } - }; - jsonDocreadable.push(tempJson); - // To validate / use later in tests. - inputJsonUris.push(tempJson.uri); - inputContents.push(tempJson.content); - } - jsonDocreadable.push(null); - setTimeout(() => { - var i = 0; i++; - }, 5000); - dbWriter.documents.writeAll(jsonDocreadable, { - onCompletion: ((summary) => { - setTimeout(() => { - var i = 0; i++; - }, 1000); - summary.docsWrittenSuccessfully.should.be.greaterThanOrEqual(10); - }) - }); // End of pipe to writeAll - // Use uriStream as the input to readAll() - uriStream = new stream.PassThrough({ objectMode: true }); - inputJsonUris.forEach(uri => uriStream.push(uri)); - uriStream.push(null); + for (let i = 0; i < 10; i++) { + const tempJson = { + uri: "/data/dmsdk/readAll-tests-one/" + i + ".json", + contentType: "application/json", + content: { ["key " + i]: "value " + i }, + }; + jsonDocreadable.push(tempJson); + // To validate / use later in tests. + inputJsonUris.push(tempJson.uri); + inputContents.push(tempJson.content); + } + jsonDocreadable.push(null); + setTimeout(() => { + var i = 0; + i++; + }, 5000); + dbWriter.documents.writeAll(jsonDocreadable, { + onCompletion: (summary) => { setTimeout(() => { - done(); - }, 5000); - }); + var i = 0; + i++; + }, 1000); + summary.docsWrittenSuccessfully.should.be.greaterThanOrEqual(10); + }, + }); // End of pipe to writeAll + // Use uriStream as the input to readAll() + uriStream = new stream.PassThrough({ objectMode: true }); + inputJsonUris.forEach((uri) => uriStream.push(uri)); + uriStream.push(null); + setTimeout(() => { + done(); + }, 5000); + }); - after((function (done) { - this.timeout(5000); - dbWriter.documents.remove(inputJsonUris) - .result(function (response) { - done(); - }) - .catch(err => done(err)) - .catch(done); - })); + after(function (done) { + this.timeout(5000); + dbWriter.documents + .remove(inputJsonUris) + .result(function (response) { + done(); + }) + .catch((err) => done(err)) + .catch(done); + }); - it('readAll multiple documents with batch options', function (done) { - streamToArray(dbWriter.documents.readAll(uriStream, { - inputkind: 'Array', - batch: 5 - }), - function (err, arr) { - if (err) { - done(err); - } - arr.forEach(item => { - setTimeout(() => { - var i = 0; i++; - }, 3000); - resultUris.push(item.uri); - resulContents.push(item.content); - }); - setTimeout(() => { - var i = 0; i++; - }, 3000); - expect(verifyArrays(resultUris, inputJsonUris)).to.be.true; - for (var c of resulContents) { - expect(verifyCurrentContents(c)).to.be.true; - } + it("readAll multiple documents with batch options", function (done) { + streamToArray( + dbWriter.documents.readAll(uriStream, { + inputkind: "Array", + batch: 5, + }), + function (err, arr) { + if (err) { + done(err); + return; + } + arr.forEach((item) => { + setTimeout(() => { + var i = 0; + i++; + }, 3000); + resultUris.push(item.uri); + resulContents.push(item.content); }); + setTimeout(() => { + var i = 0; + i++; + }, 3000); + expect(verifyArrays(resultUris, inputJsonUris)).to.be.true; + for (var c of resulContents) { + expect(verifyCurrentContents(c)).to.be.true; + } done(); - }); + }, + ); + }); - it('readAll one document with batch options', function (done) { - uriStream = new stream.PassThrough({ objectMode: true }); - uriStream.push('dmsdk.txt'); - uriStream.push(null); - streamToArray(dbWriter.documents.readAll(uriStream, { - inputkind: 'Array', - batch: 5 - }), - function (err, arr) { - if (err) { - done(err); - } - arr.forEach(item => { - setTimeout(() => { - var i = 0; i++; - }, 3000); - expect(item.uri).to.equal('dmsdk.txt'); - }); + it("readAll one document with batch options", function (done) { + uriStream = new stream.PassThrough({ objectMode: true }); + uriStream.push("dmsdk.txt"); + uriStream.push(null); + streamToArray( + dbWriter.documents.readAll(uriStream, { + inputkind: "Array", + batch: 5, + }), + function (err, arr) { + if (err) { + done(err); + return; + } + arr.forEach((item) => { + setTimeout(() => { + var i = 0; + i++; + }, 3000); + expect(item.uri).to.equal("dmsdk.txt"); }); done(); - }); + }, + ); + }); - //Verify no errors when readAll has no Uris to read - it('readAll no document Uris', function (done) { - uriStream = new stream.PassThrough({ objectMode: true }); - uriStream.push(null); - var f = function noURIS() { - dbWriter.documents.readAll(uriStream, { - inputkind: 'Array', - batch: 5 - }); - }; - expect(f).to.not.throw(); - done(); - }); + //Verify no errors when readAll has no Uris to read + it("readAll no document Uris", function (done) { + uriStream = new stream.PassThrough({ objectMode: true }); + uriStream.push(null); + var f = function noURIS() { + dbWriter.documents.readAll(uriStream, { + inputkind: "Array", + batch: 5, + }); + }; + expect(f).to.not.throw(); + done(); + }); }); // End of readAll tests From 7d9d79027d5f728d7bd17af28505e545cb1ccffa Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 5 May 2026 10:16:43 -0400 Subject: [PATCH 4/6] MLE-28498 Fixing forced Prettier formatting --- test-basic/documents-transform.js | 379 +++++++++---------- test-complete/nodejs-dmsdk-readall-1.js | 353 ++++++++--------- test-complete/nodejs-transform-javascript.js | 232 ++++++------ 3 files changed, 455 insertions(+), 509 deletions(-) diff --git a/test-basic/documents-transform.js b/test-basic/documents-transform.js index 797c7357..12f93c9c 100644 --- a/test-basic/documents-transform.js +++ b/test-basic/documents-transform.js @@ -1,268 +1,229 @@ /* - * Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. - */ -var should = require("should"); +* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +*/ +var should = require('should'); -var fs = require("fs"); -var valcheck = require("core-util-is"); +var fs = require('fs'); +var valcheck = require('core-util-is'); -var testconfig = require("../etc/test-config.js"); +var testconfig = require('../etc/test-config.js'); -var marklogic = require("../"); +var marklogic = require('../'); var q = marklogic.queryBuilder; var db = marklogic.createDatabaseClient(testconfig.restWriterConnection); -var restAdminDB = marklogic.createDatabaseClient( - testconfig.restAdminConnection, -); +var restAdminDB = marklogic.createDatabaseClient(testconfig.restAdminConnection); -describe("document transform", function () { - var xqyTransformName = "flagParam"; - var xqyTransformPath = "./test-basic/data/flagTransform.xqy"; - describe("when configuring", function () { - it("should write the transform with positional parameters", function (done) { +describe('document transform', function(){ + var xqyTransformName = 'flagParam'; + var xqyTransformPath = './test-basic/data/flagTransform.xqy'; + describe('when configuring', function() { + it('should write the transform with positional parameters', function(done){ this.timeout(3000); - restAdminDB.config.transforms - .write( - xqyTransformName, - "xquery", - fs.createReadStream(xqyTransformPath), - ) - .result(function (response) { - done(); + restAdminDB.config.transforms.write(xqyTransformName, 'xquery', fs.createReadStream(xqyTransformPath)) + .result(function(response){ + done(); }) - .catch(done); + .catch(done); }); - it("should write the transform with named parameters", function (done) { + it('should write the transform with named parameters', function(done){ this.timeout(3000); - restAdminDB.config.transforms - .write({ - name: xqyTransformName, - title: "The Flag Transform", - description: "Raising the flag.", - provider: "Banner Business", - version: 0.1, - format: "xquery", - source: fs.createReadStream(xqyTransformPath), - transformParams: { - flag: "xs:string?", - count: "xs:integer*", - }, - }) - .result(function (response) { - done(); - }) - .catch(done); + restAdminDB.config.transforms.write({ + name: xqyTransformName, + title: 'The Flag Transform', + description: 'Raising the flag.', + provider: 'Banner Business', + version: 0.1, + format: 'xquery', + source: fs.createReadStream(xqyTransformPath), + transformParams: { + 'flag': 'xs:string?', + 'count': 'xs:integer*' + } + }) + .result(function(response){ + done(); + }) + .catch(done); }); - it("should read the transform", function (done) { - restAdminDB.config.transforms - .read(xqyTransformName) - .result(function (source) { - (!valcheck.isNullOrUndefined(source)).should.equal(true); - (typeof source).should.equal("string"); - done(); + it('should read the transform', function(done){ + restAdminDB.config.transforms.read(xqyTransformName) + .result(function(source){ + (!valcheck.isNullOrUndefined(source)).should.equal(true); + (typeof source).should.equal('string'); + done(); }) - .catch(done); + .catch(done); }); - it("should list the transform", function (done) { - db.config.transforms - .list() - .result(function (response) { - response.should.have.property("transforms"); - response.transforms.should.have.property("transform"); - response.transforms.transform.length.should.be.greaterThan(0); - - const flagTransform = response.transforms.transform.find( - function (item) { - return item.name === xqyTransformName; - }, - ); - should.exist(flagTransform); - flagTransform.should.have.property("transform-parameters"); - flagTransform["transform-parameters"].should.have.property( - "parameter", - ); - - const params = flagTransform["transform-parameters"].parameter; - params.should.be.an.Array(); - params.length.should.equal(2); - params - .some(function (p) { - return ( - p["parameter-name"] === "flag" && - p["parameter-type"] === "xs:string?" - ); - }) - .should.equal(true); - params - .some(function (p) { - return ( - p["parameter-name"] === "count" && - p["parameter-type"] === "xs:integer*" - ); - }) - .should.equal(true); - done(); - }) - .catch(done); + it('should list the transform', function(done){ + db.config.transforms.list() + .result(function(response){ + response.should.have.property('transforms'); + response.transforms.should.have.property('transform'); + response.transforms.transform.length.should.be.greaterThan(0); + + const flagTransform = response.transforms.transform.find(function(item){ + return item.name === xqyTransformName; + }); + should.exist(flagTransform); + flagTransform.should.have.property('transform-parameters'); + flagTransform['transform-parameters'].should.have.property('parameter'); + + const params = flagTransform['transform-parameters'].parameter; + params.should.be.an.Array(); + params.length.should.equal(2); + params.some(function(p){ + return p['parameter-name'] === 'flag' && p['parameter-type'] === 'xs:string?'; + }).should.equal(true); + params.some(function(p){ + return p['parameter-name'] === 'count' && p['parameter-type'] === 'xs:integer*'; + }).should.equal(true); + done(); + }) + .catch(done); }); - it("should delete the transform", function (done) { - restAdminDB.config.transforms - .remove(xqyTransformName) - .result(function (response) { - done(); + it('should delete the transform', function(done){ + restAdminDB.config.transforms.remove(xqyTransformName) + .result(function(response){ + done(); }) - .catch(done); + .catch(done); }); // TODO: test streaming of source and list }); - describe("when using XQuery", function () { - var uri = "/test/write/transformContent1.json"; - before(function (done) { + describe('when using XQuery', function() { + var uri = '/test/write/transformContent1.json'; + before(function(done){ this.timeout(3000); - restAdminDB.config.transforms - .write( - xqyTransformName, - "xquery", - fs.createReadStream(xqyTransformPath), - ) - .result(function (response) { - done(); + restAdminDB.config.transforms.write(xqyTransformName, 'xquery', fs.createReadStream(xqyTransformPath)) + .result(function(response){ + done(); }) - .catch(done); + .catch(done); }); - it("should modify during write", function (done) { - db.documents - .write({ - uri: uri, - contentType: "application/json", - content: { key1: "value 1" }, - transform: [xqyTransformName, { flag: "tested1" }], + it('should modify during write', function(done){ + db.documents.write({ + uri: uri, + contentType: 'application/json', + content: {key1: 'value 1'}, + transform: [xqyTransformName, {flag:'tested1'}] }) - .result(function (response) { - return db.documents.read(uri).result(); + .result(function(response) { + return db.documents.read(uri).result(); }) - .then(function (documents) { - documents.length.should.equal(1); - documents[0].content.flagParam.should.equal("tested1"); - done(); + .then(function(documents) { + documents.length.should.equal(1); + documents[0].content.flagParam.should.equal('tested1'); + done(); }) - .catch(done); + .catch(done); }); - it("should modify during read", function (done) { - db.documents - .read({ - uris: uri, - transform: [xqyTransformName, { flag: "tested2" }], + it('should modify during read', function(done){ + db.documents.read({ + uris: uri, + transform: [xqyTransformName, {flag:'tested2'}] }) - .result(function (documents) { - documents.length.should.equal(1); - documents[0].content.flagParam.should.equal("tested2"); - done(); + .result(function(documents) { + documents.length.should.equal(1); + documents[0].content.flagParam.should.equal('tested2'); + done(); }) - .catch(done); + .catch(done); }); - it("should modify during query", function (done) { - db.documents - .query( - q - .where(q.document(uri)) - .slice(0, 10, q.transform(xqyTransformName, { flag: "tested3" })), - ) - .result(function (documents) { + it('should modify during query', function(done){ + db.documents.query( + q.where( + q.document(uri) + ). + slice(0, 10, + q.transform(xqyTransformName, {flag:'tested3'}) + ) + ) + .result(function(documents) { documents.length.should.equal(1); - documents[0].content.flagParam.should.equal("tested3"); + documents[0].content.flagParam.should.equal('tested3'); done(); - }) + }) .catch(done); }); }); - describe("when using JavaScript", function () { - var jsTransformName = "timestamp"; - var jsTransformPath = "./test-basic/data/timestampTransform.js"; - var readUri = "/test/write/readTransform.json"; - var writeUri = "/test/write/writeTransform.json"; - before(function (done) { + describe('when using JavaScript', function() { + var jsTransformName = 'timestamp'; + var jsTransformPath = './test-basic/data/timestampTransform.js'; + var readUri = '/test/write/readTransform.json'; + var writeUri = '/test/write/writeTransform.json'; + before(function(done){ this.timeout(3000); - restAdminDB.config.transforms - .write( - jsTransformName, - "javascript", - fs.createReadStream(jsTransformPath), - ) - .result(function (response) { - return db.documents - .write({ - uri: readUri, - contentType: "application/json", - content: { readKey: "read value" }, - }) - .result(); - }) - .then(function (response) { - done(); - }) - .catch(done); + restAdminDB.config.transforms.write(jsTransformName, 'javascript', fs.createReadStream(jsTransformPath)) + .result(function(response){ + return db.documents.write({ + uri: readUri, + contentType: 'application/json', + content: {readKey: 'read value'} + }).result(); + }) + .then(function(response){done();}) + .catch(done); }); - it("should modify during write", function (done) { - db.documents - .write({ - uri: writeUri, - contentType: "application/json", - content: { writeKey: "write value" }, - transform: jsTransformName, + it('should modify during write', function(done){ + db.documents.write({ + uri: writeUri, + contentType: 'application/json', + content: {writeKey: 'write value'}, + transform: jsTransformName }) - .result(function (response) { - return db.documents.read(writeUri).result(); + .result(function(response) { + return db.documents.read(writeUri).result(); }) - .then(function (documents) { - documents.length.should.equal(1); - documents[0].content.should.have.property("timestamp"); - documents[0].content.should.have.property("userName"); - documents[0].content.userName.should.eql("rest-transform-user"); // MLE-28684: transforms now run as rest-transform-user - done(); + .then(function(documents) { + documents.length.should.equal(1); + documents[0].content.should.have.property('timestamp'); + documents[0].content.should.have.property('userName'); + documents[0].content.userName.should.eql('rest-transform-user'); // MLE-28684: transforms now run as rest-transform-user + done(); }) - .catch(done); + .catch(done); }); - it("should modify during read", function (done) { - db.documents - .read({ - uris: readUri, - transform: jsTransformName, + it('should modify during read', function(done){ + db.documents.read({ + uris: readUri, + transform: jsTransformName }) - .result(function (documents) { - documents.length.should.equal(1); - documents[0].content.should.have.property("timestamp"); - documents[0].content.should.have.property("userName"); - documents[0].content.userName.should.eql("rest-transform-user"); // MLE-28684: transforms now run as rest-transform-user - done(); + .result(function(documents) { + documents.length.should.equal(1); + documents[0].content.should.have.property('timestamp'); + documents[0].content.should.have.property('userName'); + documents[0].content.userName.should.eql('rest-transform-user'); // MLE-28684: transforms now run as rest-transform-user + done(); }) - .catch(done); + .catch(done); }); - it("should modify during query", function (done) { - db.documents - .query( - q - .where(q.document(readUri)) - .slice(0, 10, q.transform(jsTransformName)), - ) - .result(function (documents) { + it('should modify during query', function(done){ + db.documents.query( + q.where( + q.document(readUri) + ). + slice(0, 10, + q.transform(jsTransformName) + ) + ) + .result(function(documents) { documents.length.should.equal(1); - documents[0].content.should.have.property("timestamp"); - documents[0].content.should.have.property("userName"); - documents[0].content.userName.should.eql("rest-transform-user"); // MLE-28684: transforms now run as rest-transform-user + documents[0].content.should.have.property('timestamp'); + documents[0].content.should.have.property('userName'); + documents[0].content.userName.should.eql('rest-transform-user'); // MLE-28684: transforms now run as rest-transform-user done(); - }) + }) .catch(done); }); }); diff --git a/test-complete/nodejs-dmsdk-readall-1.js b/test-complete/nodejs-dmsdk-readall-1.js index ca9615ac..d94917c1 100644 --- a/test-complete/nodejs-dmsdk-readall-1.js +++ b/test-complete/nodejs-dmsdk-readall-1.js @@ -1,219 +1,202 @@ /* * Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. */ -var fs = require("fs"); -const path = require("path"); +var fs = require('fs'); +const path = require('path'); -var marklogic = require("../"); -var testconfig = require("../etc/test-config-qa.js"); +var marklogic = require('../'); +var testconfig = require('../etc/test-config-qa.js'); -const stream = require("stream"); -const { expect } = require("chai"); -const streamToArray = require("stream-to-array"); +const stream = require('stream'); +const { expect } = require('chai'); +const streamToArray = require('stream-to-array'); let resultUris = []; let resulContents = []; const selectFiles = []; var uriStream = new stream.Readable(); -var dbWriter = marklogic.createDatabaseClient( - testconfig.dmsdkrestWriterConnection, -); +var dbWriter = marklogic.createDatabaseClient(testconfig.dmsdkrestWriterConnection); let inputJsonUris = []; let inputContents = []; function verifyArrays(arr1, arr2) { - var isEqual = false; - if (arr1.length !== arr2.length) { - return false; - } - for (var i = 0; i < arr1.length; i++) { - for (const e of arr2) { - if (e === arr1[i]) { - isEqual = true; - break; - } + var isEqual = false; + if (arr1.length !== arr2.length) { + return false; } - } - return isEqual; + for (var i = 0; i < arr1.length; i++) { + for (const e of arr2) { + if (e === arr1[i]) { + isEqual = true; + break; + } + } + } + return isEqual; } function verifyCurrentContents(currResContent) { - var isContentEqual = false; - var currResValue = Object.values(currResContent).valueOf()[0]; - // For each object in resultsContents array - for (var i = 0; i < inputContents.length; i++) { - var c = inputContents[i]; - var cInputValue = Object.values(c).valueOf()[0]; - if (cInputValue === currResValue) { - isContentEqual = true; - break; + var isContentEqual = false; + var currResValue = Object.values(currResContent).valueOf()[0]; + // For each object in resultsContents array + for (var i = 0; i < inputContents.length; i++) { + var c = inputContents[i]; + var cInputValue = Object.values(c).valueOf()[0]; + if (cInputValue === currResValue) { + isContentEqual = true; + break; + } } - } - return isContentEqual; + return isContentEqual; } -describe("readAll-tests-one", function () { - before(function (done) { - this.timeout(20000); - //const selectFiles = []; - var jsonDocreadable = new stream.Readable({ objectMode: true }); - var multiDocreadable = new stream.Readable({ objectMode: true }); - // Handle only .json, .xml and .txt files - var allowedFiles = [".json", ".xml", ".txt"]; +describe('readAll-tests-one', function () { + before(function (done) { + this.timeout(20000); + //const selectFiles = []; + var jsonDocreadable = new stream.Readable({ objectMode: true }); + var multiDocreadable = new stream.Readable({ objectMode: true }); + // Handle only .json, .xml and .txt files + var allowedFiles = ['.json', '.xml', '.txt']; - const dirPath = path.join(__dirname, "/data/dmsdk/writeall/"); + const dirPath = path.join(__dirname, '/data/dmsdk/writeall/'); - function includeFile(fName) { - var fileExt = path.extname(fName); - return allowedFiles.includes(fileExt); - } + function includeFile(fName) { + var fileExt = path.extname(fName); + return allowedFiles.includes(fileExt); + } - var files = fs.readdirSync(dirPath).filter(includeFile); - files.forEach((file) => { - let fileStat = fs.statSync(dirPath + "/" + file).isDirectory(); - if (!fileStat) { - selectFiles.push(file); - } - }); + var files = fs.readdirSync(dirPath).filter(includeFile); + files.forEach(file => { + let fileStat = fs.statSync(dirPath + '/' + file).isDirectory(); + if (!fileStat) { + selectFiles.push(file); + } + }); - for (var file of selectFiles) { - var fileTobeRead = dirPath + file; - var data = fs.readFileSync(fileTobeRead, { encoding: "utf8" }); - var findCType = path.extname(fileTobeRead); - var jsonFN1 = { - uri: file, - contentType: - findCType === ".json" - ? "application/json" - : findCType === ".xml" - ? "application/xml" - : "application/text", - collections: ["qatestsReadAll"], - content: data, - }; - multiDocreadable.push(jsonFN1); - } - multiDocreadable.push(null); - dbWriter.documents.writeAll(multiDocreadable, { - onCompletion: (summary) => { - summary.docsWrittenSuccessfully.should.be.greaterThanOrEqual(6); - }, - }); // End of pipe to writeAll - single byte + for (var file of selectFiles) { + var fileTobeRead = dirPath + file; + var data = fs.readFileSync(fileTobeRead, { encoding: 'utf8' }); + var findCType = path.extname(fileTobeRead); + var jsonFN1 = { + uri: file, + contentType: findCType === '.json' ? 'application/json' : findCType === '.xml' ? 'application/xml' : 'application/text', + collections: ['qatestsReadAll'], + content: data + }; + multiDocreadable.push(jsonFN1); + } + multiDocreadable.push(null); + dbWriter.documents.writeAll(multiDocreadable, { + onCompletion: ((summary) => { + summary.docsWrittenSuccessfully.should.be.greaterThanOrEqual(6); + }) + }); // End of pipe to writeAll - single byte - for (let i = 0; i < 10; i++) { - const tempJson = { - uri: "/data/dmsdk/readAll-tests-one/" + i + ".json", - contentType: "application/json", - content: { ["key " + i]: "value " + i }, - }; - jsonDocreadable.push(tempJson); - // To validate / use later in tests. - inputJsonUris.push(tempJson.uri); - inputContents.push(tempJson.content); - } - jsonDocreadable.push(null); - setTimeout(() => { - var i = 0; - i++; - }, 5000); - dbWriter.documents.writeAll(jsonDocreadable, { - onCompletion: (summary) => { + for (let i = 0; i < 10; i++) { + const tempJson = { + uri: '/data/dmsdk/readAll-tests-one/' + i + '.json', + contentType: 'application/json', + content: { ['key ' + i]: 'value ' + i } + }; + jsonDocreadable.push(tempJson); + // To validate / use later in tests. + inputJsonUris.push(tempJson.uri); + inputContents.push(tempJson.content); + } + jsonDocreadable.push(null); + setTimeout(() => { + var i = 0; i++; + }, 5000); + dbWriter.documents.writeAll(jsonDocreadable, { + onCompletion: ((summary) => { + setTimeout(() => { + var i = 0; i++; + }, 1000); + summary.docsWrittenSuccessfully.should.be.greaterThanOrEqual(10); + }) + }); // End of pipe to writeAll + // Use uriStream as the input to readAll() + uriStream = new stream.PassThrough({ objectMode: true }); + inputJsonUris.forEach(uri => uriStream.push(uri)); + uriStream.push(null); setTimeout(() => { - var i = 0; - i++; - }, 1000); - summary.docsWrittenSuccessfully.should.be.greaterThanOrEqual(10); - }, - }); // End of pipe to writeAll - // Use uriStream as the input to readAll() - uriStream = new stream.PassThrough({ objectMode: true }); - inputJsonUris.forEach((uri) => uriStream.push(uri)); - uriStream.push(null); - setTimeout(() => { - done(); - }, 5000); - }); + done(); + }, 5000); + }); - after(function (done) { - this.timeout(5000); - dbWriter.documents - .remove(inputJsonUris) - .result(function (response) { - done(); - }) - .catch((err) => done(err)) - .catch(done); - }); + after((function (done) { + this.timeout(5000); + dbWriter.documents.remove(inputJsonUris) + .result(function (response) { + done(); + }) + .catch(err => done(err)) + .catch(done); + })); - it("readAll multiple documents with batch options", function (done) { - streamToArray( - dbWriter.documents.readAll(uriStream, { - inputkind: "Array", - batch: 5, - }), - function (err, arr) { - if (err) { - done(err); - return; - } - arr.forEach((item) => { - setTimeout(() => { - var i = 0; - i++; - }, 3000); - resultUris.push(item.uri); - resulContents.push(item.content); + it('readAll multiple documents with batch options', function (done) { + streamToArray(dbWriter.documents.readAll(uriStream, { + inputkind: 'Array', + batch: 5 + }), + function (err, arr) { + if (err) { + done(err); + return; + } + arr.forEach(item => { + setTimeout(() => { + var i = 0; i++; + }, 3000); + resultUris.push(item.uri); + resulContents.push(item.content); + }); + setTimeout(() => { + var i = 0; i++; + }, 3000); + expect(verifyArrays(resultUris, inputJsonUris)).to.be.true; + for (var c of resulContents) { + expect(verifyCurrentContents(c)).to.be.true; + } + done(); }); - setTimeout(() => { - var i = 0; - i++; - }, 3000); - expect(verifyArrays(resultUris, inputJsonUris)).to.be.true; - for (var c of resulContents) { - expect(verifyCurrentContents(c)).to.be.true; - } - done(); - }, - ); - }); + }); - it("readAll one document with batch options", function (done) { - uriStream = new stream.PassThrough({ objectMode: true }); - uriStream.push("dmsdk.txt"); - uriStream.push(null); - streamToArray( - dbWriter.documents.readAll(uriStream, { - inputkind: "Array", - batch: 5, - }), - function (err, arr) { - if (err) { - done(err); - return; - } - arr.forEach((item) => { - setTimeout(() => { - var i = 0; - i++; - }, 3000); - expect(item.uri).to.equal("dmsdk.txt"); + it('readAll one document with batch options', function (done) { + uriStream = new stream.PassThrough({ objectMode: true }); + uriStream.push('dmsdk.txt'); + uriStream.push(null); + streamToArray(dbWriter.documents.readAll(uriStream, { + inputkind: 'Array', + batch: 5 + }), + function (err, arr) { + if (err) { + done(err); + return; + } + arr.forEach(item => { + setTimeout(() => { + var i = 0; i++; + }, 3000); + expect(item.uri).to.equal('dmsdk.txt'); + }); + done(); }); - done(); - }, - ); - }); + }); - //Verify no errors when readAll has no Uris to read - it("readAll no document Uris", function (done) { - uriStream = new stream.PassThrough({ objectMode: true }); - uriStream.push(null); - var f = function noURIS() { - dbWriter.documents.readAll(uriStream, { - inputkind: "Array", - batch: 5, - }); - }; - expect(f).to.not.throw(); - done(); - }); + //Verify no errors when readAll has no Uris to read + it('readAll no document Uris', function (done) { + uriStream = new stream.PassThrough({ objectMode: true }); + uriStream.push(null); + var f = function noURIS() { + dbWriter.documents.readAll(uriStream, { + inputkind: 'Array', + batch: 5 + }); + }; + expect(f).to.not.throw(); + done(); + }); }); // End of readAll tests diff --git a/test-complete/nodejs-transform-javascript.js b/test-complete/nodejs-transform-javascript.js index 63554dc8..a9e22c3f 100644 --- a/test-complete/nodejs-transform-javascript.js +++ b/test-complete/nodejs-transform-javascript.js @@ -1,113 +1,113 @@ /* - * Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. - */ -var should = require("should"); -var fs = require("fs"); -var concatStream = require("concat-stream"); -var valcheck = require("core-util-is"); +* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +*/ +var should = require('should'); +var fs = require('fs'); +var concatStream = require('concat-stream'); +var valcheck = require('core-util-is'); -var testconfig = require("../etc/test-config-qa.js"); +var testconfig = require('../etc/test-config-qa.js'); -var marklogic = require("../"); +var marklogic = require('../'); var q = marklogic.queryBuilder; var db = marklogic.createDatabaseClient(testconfig.restReaderConnection); var dbWriter = marklogic.createDatabaseClient(testconfig.restWriterConnection); var dbAdmin = marklogic.createDatabaseClient(testconfig.restAdminConnection); -describe("Transform test with javascript", function () { - before(function (done) { - this.timeout(10000); - dbWriter.documents - .write( - { - uri: "/test/transform/jstransform.json", - contentType: "application/json", - content: { title: "transform test with javascript" }, +describe('Transform test with javascript', function () { + before(function (done) { + this.timeout(10000); + dbWriter.documents.write({ + uri: '/test/transform/jstransform.json', + contentType: 'application/json', + content: { title: 'transform test with javascript' } }, { - uri: "/test/transform/jstransform2.json", - contentType: "application/json", - content: { title: "exapmle test with javascript" }, - }, - ) - .result(function (response) { - done(); - }, done); - }); + uri: '/test/transform/jstransform2.json', + contentType: 'application/json', + content: { title: 'exapmle test with javascript' } + }). + result(function (response) { + done(); + }, done); + }); - var transformName = "timestamp"; - var transformPath = __dirname + "/data/timestampTransform.js"; + var transformName = 'timestamp'; + var transformPath = __dirname + '/data/timestampTransform.js'; - it("should write the transform", function (done) { - this.timeout(10000); - fs.createReadStream(transformPath).pipe( - concatStream({ encoding: "string" }, function (source) { - dbAdmin.config.transforms - .write(transformName, "javascript", source) - .result(function (response) { - done(); - }, done); - }), - ); - }); + it('should write the transform', function (done) { + this.timeout(10000); + fs.createReadStream(transformPath). + pipe(concatStream({ encoding: 'string' }, function (source) { + dbAdmin.config.transforms.write(transformName, 'javascript', source). + result(function (response) { + done(); + }, done); + })); + }); - it("should read the transform", function (done) { - dbAdmin.config.transforms.read(transformName).result(function (source) { - (!valcheck.isNullOrUndefined(source)).should.equal(true); - done(); - }, done); - }); + it('should read the transform', function (done) { + dbAdmin.config.transforms.read(transformName). + result(function (source) { + (!valcheck.isNullOrUndefined(source)).should.equal(true); + done(); + }, done); + }); - it("should list the transform", function (done) { - dbAdmin.config.transforms.list().result(function (response) { - response.should.have.property("transforms"); - done(); - }, done); - }); + it('should list the transform', function (done) { + dbAdmin.config.transforms.list(). + result(function (response) { + response.should.have.property('transforms'); + done(); + }, done); + }); - var uri = "/test/transform/jstransform.json"; + var uri = '/test/transform/jstransform.json'; - it("should modify during read", function (done) { - db.documents - .read({ - uris: uri, - transform: transformName, - }) - .result(function (response) { - //console.log(JSON.stringify(response, null, 4)); - response[0].content.should.have.property("timestamp"); - response[0].content.userName.should.equal("rest-transform-user"); // MLE-28684: transforms now run as rest-transform-user - done(); - }) - .catch(done); - }); + it('should modify during read', function (done) { + db.documents.read({ + uris: uri, + transform: transformName + }). + result(function (response) { + //console.log(JSON.stringify(response, null, 4)); + response[0].content.should.have.property('timestamp'); + response[0].content.userName.should.equal('rest-transform-user'); // MLE-28684: transforms now run as rest-transform-user + done(); + }) + .catch(done); + }); - it("should query", function (done) { - db.documents.query(q.where(q.word("title", "test"))).result(function ( - response, - ) { - //console.log(JSON.stringify(response, null, 4)); - response.length.should.equal(2); - done(); - }, done); - }); + it('should query', function (done) { + db.documents.query( + q.where( + q.word('title', 'test') + ) + ). + result(function (response) { + //console.log(JSON.stringify(response, null, 4)); + response.length.should.equal(2); + done(); + }, done); + }); - it("should modify during query", function () { - this.timeout(10000); - db.documents - .query( - q - .where(q.word("title", "transform")) - .slice(0, 10, q.transform(transformName)), - ) - .result(function (response) { - //console.log(JSON.stringify(response, null, 4)); - response[0].content.should.have.property("timestamp"); - response[0].content.userName.should.equal("rest-reader"); - }); - }); - /*it('should modify during query - synch', function(){ + + it('should modify during query', function () { + this.timeout(10000); + db.documents.query( + q.where( + q.word('title', 'transform') + ). + slice(0, 10, q.transform(transformName)) + ). + result(function (response) { + //console.log(JSON.stringify(response, null, 4)); + response[0].content.should.have.property('timestamp'); + response[0].content.userName.should.equal('rest-reader'); + }); + }); + /*it('should modify during query - synch', function(){ this.timeout(10000); db.documents.query( q.where( @@ -121,20 +121,22 @@ describe("Transform test with javascript", function () { response[0].content.userName.should.equal('rest-reader'); }); });*/ - it("should modify during query , slice without paging parameters, Bug 111", function (done) { - db.documents - .query( - q.where(q.word("title", "transform")).slice(q.transform(transformName)), - ) - .result(function (response) { - //console.log(JSON.stringify(response, null, 4)); - response[0].content.should.have.property("timestamp"); - response[0].content.userName.should.equal("rest-transform-user"); // MLE-28684: transforms now run as rest-transform-user - done(); - }) - .catch(done); - }); - /*it('should modify during write', function(done){ + it('should modify during query , slice without paging parameters, Bug 111', function (done) { + db.documents.query( + q.where( + q.word('title', 'transform') + ). + slice(q.transform(transformName)) + ). + result(function (response) { + //console.log(JSON.stringify(response, null, 4)); + response[0].content.should.have.property('timestamp'); + response[0].content.userName.should.equal('rest-transform-user'); // MLE-28684: transforms now run as rest-transform-user + done(); + }) + .catch(done); + }); + /*it('should modify during write', function(done){ dbWriter.documents.write({ uri: '/test/transform/write/jstransform.json', contentType: 'application/json', @@ -152,14 +154,14 @@ describe("Transform test with javascript", function () { }, done); });*/ - it("should remove the documents", function (done) { - dbAdmin.documents - .removeAll({ - directory: "/test/transform/", - }) - .result(function (response) { - response.should.be.ok; - done(); - }, done); - }); + it('should remove the documents', function (done) { + dbAdmin.documents.removeAll({ + directory: '/test/transform/' + }). + result(function (response) { + response.should.be.ok; + done(); + }, done); + }); + }); From c9f3216f0b65974a9cdf1e01961d12475ae010bc Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 7 May 2026 15:52:54 -0400 Subject: [PATCH 5/6] MLE-28498 Requested changes Renamed rest-invoke-user.json -> rest-login.json Added message to asserts with transform tests checking userName equal to rest-transform-user --- .../main/ml-config/security/roles/rest-evaluator.json | 5 ++++- .../roles/{rest-invoke-user.json => rest-login.json} | 2 +- .../src/main/ml-config/security/users/rest-reader.json | 6 +++++- .../ml-config/security/users/rest-temporal-writer.json | 5 ++++- .../ml-config/security/users/rest-transform-user.json | 8 +++++++- test-basic/documents-transform.js | 9 ++++++--- test-complete/nodejs-transform-javascript.js | 6 ++++-- 7 files changed, 31 insertions(+), 10 deletions(-) rename test-app/src/main/ml-config/security/roles/{rest-invoke-user.json => rest-login.json} (89%) diff --git a/test-app/src/main/ml-config/security/roles/rest-evaluator.json b/test-app/src/main/ml-config/security/roles/rest-evaluator.json index f7acb257..9d315236 100644 --- a/test-app/src/main/ml-config/security/roles/rest-evaluator.json +++ b/test-app/src/main/ml-config/security/roles/rest-evaluator.json @@ -1,7 +1,10 @@ { "role-name": "rest-evaluator", "description": "REST writer who can eval, invoke, or set a dynamic databases", - "role": ["rest-writer", "sparql-update-user"], + "role": [ + "rest-writer", + "sparql-update-user" + ], "privilege": [ { "privilege-name": "xdmp-eval", diff --git a/test-app/src/main/ml-config/security/roles/rest-invoke-user.json b/test-app/src/main/ml-config/security/roles/rest-login.json similarity index 89% rename from test-app/src/main/ml-config/security/roles/rest-invoke-user.json rename to test-app/src/main/ml-config/security/roles/rest-login.json index 1c1d6bc4..1e6308d0 100644 --- a/test-app/src/main/ml-config/security/roles/rest-invoke-user.json +++ b/test-app/src/main/ml-config/security/roles/rest-login.json @@ -1,5 +1,5 @@ { - "role-name": "rest-invoke-user", + "role-name": "rest-login", "description": "Role granting xdmp:login privilege needed for REST transform invocations with different-transaction isolation", "privilege": [ { diff --git a/test-app/src/main/ml-config/security/users/rest-reader.json b/test-app/src/main/ml-config/security/users/rest-reader.json index cf511ca6..7ca1398e 100644 --- a/test-app/src/main/ml-config/security/users/rest-reader.json +++ b/test-app/src/main/ml-config/security/users/rest-reader.json @@ -2,5 +2,9 @@ "user-name": "rest-reader", "description": "rest-reader user", "password": "x", - "role": ["rest-reader", "rest-extension-user", "rest-invoke-user"] + "role": [ + "rest-reader", + "rest-extension-user", + "rest-login" + ] } diff --git a/test-app/src/main/ml-config/security/users/rest-temporal-writer.json b/test-app/src/main/ml-config/security/users/rest-temporal-writer.json index 64528027..4d57bd5d 100644 --- a/test-app/src/main/ml-config/security/users/rest-temporal-writer.json +++ b/test-app/src/main/ml-config/security/users/rest-temporal-writer.json @@ -2,5 +2,8 @@ "user-name": "rest-temporal-writer", "description": "rest-writer user with temporal privileges", "password": "x", - "role": ["rest-temporal-writer", "rest-extension-user"] + "role": [ + "rest-temporal-writer", + "rest-extension-user" + ] } diff --git a/test-app/src/main/ml-config/security/users/rest-transform-user.json b/test-app/src/main/ml-config/security/users/rest-transform-user.json index cbad1d76..029dd5c7 100644 --- a/test-app/src/main/ml-config/security/users/rest-transform-user.json +++ b/test-app/src/main/ml-config/security/users/rest-transform-user.json @@ -1,4 +1,10 @@ { "user-name": "rest-transform-user", - "role": ["rest-transform-internal", "rest-reader", "rest-invoke-user"] + "description": "rest-transform-user user", + "password": "x", + "role": [ + "rest-transform-internal", + "rest-reader", + "rest-login" + ] } diff --git a/test-basic/documents-transform.js b/test-basic/documents-transform.js index 12f93c9c..de4b1986 100644 --- a/test-basic/documents-transform.js +++ b/test-basic/documents-transform.js @@ -187,7 +187,8 @@ describe('document transform', function(){ documents.length.should.equal(1); documents[0].content.should.have.property('timestamp'); documents[0].content.should.have.property('userName'); - documents[0].content.userName.should.eql('rest-transform-user'); // MLE-28684: transforms now run as rest-transform-user + documents[0].content.userName.should.eql('rest-transform-user', + 'As of MarkLogic 11.3.5 and 12.0.2 JavaScript transforms execute as the dedicated rest-transform-user rather than the calling user\'s identity, preventing privilege escalation via malicious transforms'); done(); }) .catch(done); @@ -202,7 +203,8 @@ describe('document transform', function(){ documents.length.should.equal(1); documents[0].content.should.have.property('timestamp'); documents[0].content.should.have.property('userName'); - documents[0].content.userName.should.eql('rest-transform-user'); // MLE-28684: transforms now run as rest-transform-user + documents[0].content.userName.should.eql('rest-transform-user', + 'As of MarkLogic 11.3.5 and 12.0.2 JavaScript transforms execute as the dedicated rest-transform-user rather than the calling user\'s identity, preventing privilege escalation via malicious transforms'); done(); }) .catch(done); @@ -221,7 +223,8 @@ describe('document transform', function(){ documents.length.should.equal(1); documents[0].content.should.have.property('timestamp'); documents[0].content.should.have.property('userName'); - documents[0].content.userName.should.eql('rest-transform-user'); // MLE-28684: transforms now run as rest-transform-user + documents[0].content.userName.should.eql('rest-transform-user', + 'As of MarkLogic 11.3.5 and 12.0.2 JavaScript transforms execute as the dedicated rest-transform-user rather than the calling user\'s identity, preventing privilege escalation via malicious transforms'); done(); }) .catch(done); diff --git a/test-complete/nodejs-transform-javascript.js b/test-complete/nodejs-transform-javascript.js index a9e22c3f..0291e762 100644 --- a/test-complete/nodejs-transform-javascript.js +++ b/test-complete/nodejs-transform-javascript.js @@ -73,7 +73,8 @@ describe('Transform test with javascript', function () { result(function (response) { //console.log(JSON.stringify(response, null, 4)); response[0].content.should.have.property('timestamp'); - response[0].content.userName.should.equal('rest-transform-user'); // MLE-28684: transforms now run as rest-transform-user + response[0].content.userName.should.equal('rest-transform-user', + 'As of MarkLogic 11.3.5 and 12.0.2 JavaScript transforms execute as the dedicated rest-transform-user rather than the calling user\'s identity, preventing privilege escalation via malicious transforms'); done(); }) .catch(done); @@ -131,7 +132,8 @@ describe('Transform test with javascript', function () { result(function (response) { //console.log(JSON.stringify(response, null, 4)); response[0].content.should.have.property('timestamp'); - response[0].content.userName.should.equal('rest-transform-user'); // MLE-28684: transforms now run as rest-transform-user + response[0].content.userName.should.equal('rest-transform-user', + 'As of MarkLogic 11.3.5 and 12.0.2 JavaScript transforms execute as the dedicated rest-transform-user rather than the calling user\'s identity, preventing privilege escalation via malicious transforms'); done(); }) .catch(done); From f95f2fdbb96d0301087ce4937d9bf1ac78d9165c Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Fri, 8 May 2026 13:56:04 -0400 Subject: [PATCH 6/6] MLE-28498 Skip optic-fromDocs tests for < 12.1 After confirming with the MarkLogic Server team, op:from-docs is only supported in MLS 12.1 since it is a new feature. I have added a skip for anything lower than MLS 12.1 --- test-basic/optic-fromDocs.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test-basic/optic-fromDocs.js b/test-basic/optic-fromDocs.js index b8b78206..039f9ee8 100644 --- a/test-basic/optic-fromDocs.js +++ b/test-basic/optic-fromDocs.js @@ -20,6 +20,8 @@ let uris = []; let serverConfiguration = {}; describe('optic-update fromDocs tests', function() { + // NOTE: op.fromDocs() with op.columnBuilder() is only supported in MarkLogic 12.1.0 and later. + // Tests in this suite are skipped automatically on earlier versions. this.timeout(15000); before(function (done) { @@ -34,6 +36,10 @@ describe('optic-update fromDocs tests', function() { describe('fromDocs', function () { before(function (done) { + if (serverConfiguration.serverVersion < 12.1) { + this.skip(); + return; + } // Insert test documents const testDocs = [ { @@ -73,7 +79,7 @@ describe('optic-update fromDocs tests', function() { } }, { - // we already have a geospatial element index for 'point' in wgs84 + // we already have a geospatial element index for 'point' in wgs84 // in the test-app ml-gradle project. Use that. Use 'point' to indicate location. uri: '/test/fromDocs/location-portland.json', contentType: 'application/json', @@ -127,14 +133,14 @@ describe('optic-update fromDocs tests', function() { } } ]; - + let readable = new Stream.Readable({objectMode: true}); testDocs.forEach(doc => { readable.push(doc); uris.push(doc.uri); }); readable.push(null); - + db.documents.writeAll(readable, { onCompletion: () => done() }); @@ -252,7 +258,7 @@ describe('optic-update fromDocs tests', function() { const portlandPoint = op.cts.point(45.52, -122.68); const searchRadius = 650; // miles - // geospatial element index is defined for 'point' in wgs84 + // geospatial element index is defined for 'point' in wgs84 const plan = op.fromDocs( op.cts.collectionQuery('fromDocs'), '/location', @@ -270,7 +276,7 @@ describe('optic-update fromDocs tests', function() { ['coordinate-system=wgs84'] ) ); - + execPlan(plan).then(function (response) { const output = getResults(response); output.length.should.be.equal(3); @@ -340,4 +346,4 @@ describe('optic-update fromDocs tests', function() { }); -}); \ No newline at end of file +});