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..929376cc8 100644 --- a/test/sp/alias.ts +++ b/test/sp/alias.ts @@ -6,6 +6,8 @@ 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"; +import { URL } from 'url'; // To prevent typing error in the "parameter parsing" test describe("Alias Parameters", function () { @@ -28,6 +30,49 @@ 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()); + + // 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]); + } + } + }); + it("Folders", function () { return expect(this.pnp.sp.web.getFolderByServerRelativePath(`!@p1::/${combine(webRelativeUrl, "AliasTestLib/MyTestFolder")}`)()).to.eventually.be.fulfilled;