From ab680aeaeaaf7fda9921abf5af73f49dc470b037 Mon Sep 17 00:00:00 2001 From: Roel Date: Thu, 11 Jul 2024 10:43:01 +0200 Subject: [PATCH 1/2] Improved aliased parameter parsing --- packages/sp/spqueryable.ts | 14 ++++--------- test/sp/alias.ts | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/packages/sp/spqueryable.ts b/packages/sp/spqueryable.ts index e246c3d35..26b30e4ba 100644 --- a/packages/sp/spqueryable.ts +++ b/packages/sp/spqueryable.ts @@ -81,17 +81,11 @@ export class _SPQueryable extends Queryable { const aliasedParams = new URLSearchParams(this.query); - // this regex is designed to locate aliased parameters within url paths. These may have the form: - // /something(!@p1::value) - // /something(!@p1::value, param=value) - // /something(param=value,!@p1::value) - // /something(param=value,!@p1::value,param=value) - // /something(param=!@p1::value) - // there could be spaces or not around the boundaries - let url = this.toUrl().replace(/([( *| *, *| *= *])'!(@.*?)::(.*?)'([ *)| *, *])/ig, (match, frontBoundary, labelName, value, endBoundary) => { + // this regex is designed to locate aliased parameters within url paths + let url = this.toUrl().replace(/'!(@.+?)::((?:[^']|'')+)'/ig, (match, labelName, value) => { this.log(`Rewriting aliased parameter from match ${match} to label: ${labelName} value: ${value}`, 0); - aliasedParams.set(labelName,`'${value}'`); - return `${frontBoundary}${labelName}${endBoundary}`; + aliasedParams.set(labelName, `'${value}'`); + return labelName; }); const query = aliasedParams.toString(); diff --git a/test/sp/alias.ts b/test/sp/alias.ts index b69a9a2d4..0268e36d0 100644 --- a/test/sp/alias.ts +++ b/test/sp/alias.ts @@ -6,6 +6,7 @@ import "@pnp/sp/files/web"; import "@pnp/sp/files/folder"; import "@pnp/sp/lists/web"; import { combine } from "@pnp/core"; +import { SPQueryable } from "@pnp/sp"; describe("Alias Parameters", function () { @@ -28,6 +29,48 @@ describe("Alias Parameters", function () { await ler.list.rootFolder.files.addUsingPath("text.txt", "Some file content!"); }); + it('Parameter parsing', function() { + /** Values to test */ + const values = [ + "value", + "value's", + "value with space", + "value with space' and apostrophe", + "ending with apostrophe'", + "'staring with apostrophe", + "'staring and ending with apostrophe'", + "with,' comma", + ]; + /** Aliased parameters to test */ + const tests = values.reduce>>((obj, value)=>{ + // Escape apostrophe in value + value = value.replace(/'/g, "''"); + + obj[`something('!@p1::${value}')`] = {'@p1': `'${value}'`}; + obj[`something('!@p1::${value}','!@p2::${value}2')`] = {'@p1': `'${value}'`, '@p2': `'${value}2'`}; + obj[`something('!@p1::${value}', param=value)`] = {'@p1': `'${value}'`}; + obj[`something('!@p1::${value}', param=value, '!@p2::${value}2')`] = {'@p1': `'${value}'`, '@p2': `'${value}2'`}; + obj[`something(param=value,'!@p1::${value}')`] = {'@p1': `'${value}'`}; + obj[`something(param=value,'!@p1::${value}','!@p2::${value}2')`] = {'@p1': `'${value}'`, '@p2': `'${value}2'`}; + obj[`something(param=value,'!@p1::${value}',param=value)`] = {'@p1': `'${value}'`}; + obj[`something(param=value,'!@p1::${value}',param=value,'!@p2::${value}2')`] = {'@p1': `'${value}'`, '@p2': `'${value}2'`}; + obj[`something(param='!@p1::${value}')`] = {'@p1': `'${value}'`}; + obj[`something(param='!@p1::${value}',param2='!@p2::${value}2')`] = {'@p1': `'${value}'`, '@p2': `'${value}2'`}; + return obj; + }, {}); + + // Test all aliased parameters + for(const [alias, params] of Object.entries(tests)) { + const requestUrl = SPQueryable(this.pnp.sp.web, alias).toRequestUrl(); + const searchParams = Object.fromEntries(new URL(requestUrl).searchParams.entries()); + + for(const param in params) { + expect(searchParams, `Failed to parse "${alias}"`).to.have.property(param); + expect(searchParams[param], `Failed to parse "${alias}"`).to.equal(params[param]); + } + } + }); + it("Folders", function () { return expect(this.pnp.sp.web.getFolderByServerRelativePath(`!@p1::/${combine(webRelativeUrl, "AliasTestLib/MyTestFolder")}`)()).to.eventually.be.fulfilled; From c28cebcece1501570e8987896ecd4b829b1bc1ab Mon Sep 17 00:00:00 2001 From: Roel Date: Thu, 11 Jul 2024 15:05:37 +0200 Subject: [PATCH 2/2] fix linting issues --- test/sp/alias.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/test/sp/alias.ts b/test/sp/alias.ts index 0268e36d0..098d6fb82 100644 --- a/test/sp/alias.ts +++ b/test/sp/alias.ts @@ -29,7 +29,7 @@ describe("Alias Parameters", function () { await ler.list.rootFolder.files.addUsingPath("text.txt", "Some file content!"); }); - it('Parameter parsing', function() { + it("Parameter parsing", function() { /** Values to test */ const values = [ "value", @@ -46,16 +46,16 @@ describe("Alias Parameters", function () { // Escape apostrophe in value value = value.replace(/'/g, "''"); - obj[`something('!@p1::${value}')`] = {'@p1': `'${value}'`}; - obj[`something('!@p1::${value}','!@p2::${value}2')`] = {'@p1': `'${value}'`, '@p2': `'${value}2'`}; - obj[`something('!@p1::${value}', param=value)`] = {'@p1': `'${value}'`}; - obj[`something('!@p1::${value}', param=value, '!@p2::${value}2')`] = {'@p1': `'${value}'`, '@p2': `'${value}2'`}; - obj[`something(param=value,'!@p1::${value}')`] = {'@p1': `'${value}'`}; - obj[`something(param=value,'!@p1::${value}','!@p2::${value}2')`] = {'@p1': `'${value}'`, '@p2': `'${value}2'`}; - obj[`something(param=value,'!@p1::${value}',param=value)`] = {'@p1': `'${value}'`}; - obj[`something(param=value,'!@p1::${value}',param=value,'!@p2::${value}2')`] = {'@p1': `'${value}'`, '@p2': `'${value}2'`}; - obj[`something(param='!@p1::${value}')`] = {'@p1': `'${value}'`}; - obj[`something(param='!@p1::${value}',param2='!@p2::${value}2')`] = {'@p1': `'${value}'`, '@p2': `'${value}2'`}; + obj[`something('!@p1::${value}')`] = {"@p1": `'${value}'`}; + obj[`something('!@p1::${value}','!@p2::${value}2')`] = {"@p1": `'${value}'`, "@p2": `'${value}2'`}; + obj[`something('!@p1::${value}', param=value)`] = {"@p1": `'${value}'`}; + obj[`something('!@p1::${value}', param=value, '!@p2::${value}2')`] = {"@p1": `'${value}'`, "@p2": `'${value}2'`}; + obj[`something(param=value,'!@p1::${value}')`] = {"@p1": `'${value}'`}; + obj[`something(param=value,'!@p1::${value}','!@p2::${value}2')`] = {"@p1": `'${value}'`, "@p2": `'${value}2'`}; + obj[`something(param=value,'!@p1::${value}',param=value)`] = {"@p1": `'${value}'`}; + obj[`something(param=value,'!@p1::${value}',param=value,'!@p2::${value}2')`] = {"@p1": `'${value}'`, "@p2": `'${value}2'`}; + obj[`something(param='!@p1::${value}')`] = {"@p1": `'${value}'`}; + obj[`something(param='!@p1::${value}',param2='!@p2::${value}2')`] = {"@p1": `'${value}'`, "@p2": `'${value}2'`}; return obj; }, {}); @@ -64,6 +64,7 @@ describe("Alias Parameters", function () { const requestUrl = SPQueryable(this.pnp.sp.web, alias).toRequestUrl(); const searchParams = Object.fromEntries(new URL(requestUrl).searchParams.entries()); + // eslint-disable-next-line guard-for-in for(const param in params) { expect(searchParams, `Failed to parse "${alias}"`).to.have.property(param); expect(searchParams[param], `Failed to parse "${alias}"`).to.equal(params[param]);