diff --git a/package.json b/package.json index 8cc998c..f02eec7 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ }, "dependencies": { "@vscode/extension-telemetry": "^0.8.4", - "dlt-logs-utils": "0.0.2", + "dlt-logs-utils": "^0.1.0", "jju": "github:mbehr1/jju#3aa4169df926e99083fdd511d7c20b5bd9ba789f", "js-yaml": "^4.1.0", "json5": "2.2.3", diff --git a/src/extension/fbaEditor.ts b/src/extension/fbaEditor.ts index df7e556..99d4590 100644 --- a/src/extension/fbaEditor.ts +++ b/src/extension/fbaEditor.ts @@ -966,13 +966,17 @@ export class FBAEditorProvider implements vscode.CustomTextEditorProvider, vscod */ static mergeAttributes(mainAttrs: any[], newAttrs: any[] | undefined) { console.warn(`FBAEditorProvider.mergeAttributes mainAttrs=${JSON.stringify(mainAttrs)} newAttrs=${JSON.stringify(newAttrs)}`) - // attributes are arrays of objects with a single key (the name) + // attributes are arrays of objects with a single key (the name) (and the fbUid as 2nd key) if (newAttrs === undefined) { return } - const mainKeys = mainAttrs.map((a) => Object.keys(a)[0]) + const mainKeys = mainAttrs.map((a) => { + const { fbUid: _a, ...aWoFbUid } = a + return Object.keys(aWoFbUid)[0] + }) for (const newKeyObj of newAttrs) { - const newKey = Object.keys(newKeyObj)[0] + const { fbUid: _a, ...newKeyObjWoFbUid } = newKeyObj + const newKey = Object.keys(newKeyObjWoFbUid)[0] if (!mainKeys.includes(newKey)) { mainAttrs.push(newKeyObj) } diff --git a/src/extension/fbaNBRQRenderer.ts b/src/extension/fbaNBRQRenderer.ts index 6b3542d..6bc4271 100644 --- a/src/extension/fbaNBRQRenderer.ts +++ b/src/extension/fbaNBRQRenderer.ts @@ -17,65 +17,9 @@ import { DocData, FBAEditorProvider } from './fbaEditor' import { FBBadge } from './fbaFormat' import { arrayEquals, getMemberParent, MemberPath } from './util' import * as uv0 from 'dlt-logs-utils' +import { RQ, rqUriDecode, rqUriEncode } from 'dlt-logs-utils/dist/restQuery' import { NotebookCellOutput } from 'vscode' -interface RQCmd { - cmd: string - param: any -} - -interface RQ { - path: string - commands: RQCmd[] -} - -const rqUriDecode = (rq: string): RQ => { - const res: RQ = { - path: '', - commands: [], - } - if (!rq || rq.length === 0) { - return res - } - - const indexOfQ = rq?.indexOf('?') - if (indexOfQ > 0) { - res.path = rq.slice(0, indexOfQ + 1) // + '\n' - - const options = rq.slice(indexOfQ + 1) - const optionArr = options.split('&') - for (const commandStr of optionArr) { - const eqIdx = commandStr.indexOf('=') - const command = commandStr.slice(0, eqIdx) - const commandParams = decodeURIComponent(commandStr.slice(eqIdx + 1)) - res.commands.push({ cmd: command, param: commandParams }) - } - } else { - res.path = rq - } - - return res -} - -const rqUriEncode = (rq: RQ): string => { - let toRet = rq.path - if (rq.commands.length > 0) { - if (!toRet.endsWith('?')) { - toRet += '?' - } - toRet += rq.commands.map((rqCmd) => rqCmd.cmd + '=' + encodeURIComponent(rqCmd.param)).join('&') - /* - for (const [idx, rqCmd] of rq.commands.entries()) { - const cmdStr = rqCmd.cmd + '=' + encodeURIComponent(rqCmd.param) - if (idx > 0) { - toRet += '&' - } - toRet += cmdStr - }*/ - } - return toRet -} - class FBANBRQCell extends vscode.NotebookCellData { constructor(kind: vscode.NotebookCellKind, value: string, languageId: string, metadata?: object) { super(kind, value, languageId) diff --git a/src/webview/package.json b/src/webview/package.json index 4b1882c..392f9b6 100644 --- a/src/webview/package.json +++ b/src/webview/package.json @@ -13,7 +13,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", - "dlt-logs-utils": "0.0.2", + "dlt-logs-utils": "^0.1.0", "dompurify": "^2.4.0", "jju": "github:mbehr1/jju#3aa4169df926e99083fdd511d7c20b5bd9ba789f", "js-yaml": "^4.1.0", diff --git a/src/webview/src/App.js b/src/webview/src/App.js index de72654..6beedd8 100644 --- a/src/webview/src/App.js +++ b/src/webview/src/App.js @@ -373,10 +373,10 @@ export default class App extends Component { } handleInputChange(object, event, propsField) { - console.warn(`handleInputChange this=`, this) - console.warn(`handleInputChange object=`, object) - console.warn(`handleInputChange event=`, event) - console.warn(`handleInputChange propsField=`, propsField) + console.log(`handleInputChange this=`, this) + console.log(`handleInputChange object=`, object) + console.log(`handleInputChange event=`, event) + console.log(`handleInputChange propsField=`, propsField) // if propsField is provided this determines the field to update (e.g. object.props[propsField]=...) const target = event.target @@ -429,9 +429,10 @@ export default class App extends Component { /*object.props[propsFieldName] = value; */ didUpdate = true } else { // for attributes object contains just one key: (the name) - if (Object.keys(object).length === 1) { + const { fbUid: _a, ...objectWoFbUid } = object + if (Object.keys(objectWoFbUid).length === 1) { console.log(`App.handleInputChange found attribute like object to update: ${JSON.stringify(object)}`) - const curValue = object[Object.keys(object)[0]] + const curValue = object[Object.keys(objectWoFbUid)[0]] if (typeof curValue === 'object') { const attrObj = curValue console.log(`App.handleInputChange found object inside attribute to update: ${JSON.stringify(attrObj)}`) @@ -453,7 +454,11 @@ export default class App extends Component { object[key] = value didUpdate = true } else { - console.warn(`handleInputChange didn't found key '${key}' in object to update to value '${value}'!`) + console.warn( + `handleInputChange didn't found key '${key}' in object to update to value '${JSON.stringify( + value, + )}' in object: '${JSON.stringify(object)}'!`, + ) } } } @@ -580,7 +585,7 @@ export default class App extends Component { // reset attributes: this.state.attributes.forEach((attribute) => { - const attrName = Object.keys(attribute)[0] + const attrName = Object.keys(attribute).find((key) => key !== 'fbUid') const attrObj = attribute[attrName] if ('value' in attrObj) { attrObj['value'] = null diff --git a/src/webview/src/components/dataProviderEditDialog.js b/src/webview/src/components/dataProviderEditDialog.js index ed2389d..eaab698 100644 --- a/src/webview/src/components/dataProviderEditDialog.js +++ b/src/webview/src/components/dataProviderEditDialog.js @@ -289,24 +289,24 @@ export default function DataProviderEditDialog(props) { dataSource?.startsWith('ext:mbehr1.dlt-logs') ? dataSource : props.applyMode - ? // eslint-disable-next-line no-template-curly-in-string - `ext:mbehr1.dlt-logs/get/docs/0/filters?delete=${encodeURIComponent('{"tmpFb":1}')}&disableAll=view${ - attributes.findIndex((attr) => attr.hasOwnProperty('lifecycles')) >= 0 - ? `&add=${encodeURIComponent( - // eslint-disable-next-line no-template-curly-in-string - '{"lifecycles":"${attributes.lifecycles.id}","name":"not selected lifecycles","not":true,"tmpFb":1,"type":1}', - )}` - : '' - }` - : // eslint-disable-next-line no-template-curly-in-string - `ext:mbehr1.dlt-logs/get/docs/0/filters?query=${encodeURIComponent( - `[{${ + ? // eslint-disable-next-line no-template-curly-in-string + `ext:mbehr1.dlt-logs/get/docs/0/filters?delete=${encodeURIComponent('{"tmpFb":1}')}&disableAll=view${ attributes.findIndex((attr) => attr.hasOwnProperty('lifecycles')) >= 0 - ? // eslint-disable-next-line no-template-curly-in-string - '"lifecycles":"${attributes.lifecycles.id}",' + ? `&add=${encodeURIComponent( + // eslint-disable-next-line no-template-curly-in-string + '{"lifecycles":"${attributes.lifecycles.id}","name":"not selected lifecycles","not":true,"tmpFb":1,"type":1}', + )}` : '' - }"name":"not selected lifecycles","not":true,"type":1}]`, - )}` + }` + : // eslint-disable-next-line no-template-curly-in-string + `ext:mbehr1.dlt-logs/get/docs/0/filters?query=${encodeURIComponent( + `[{${ + attributes.findIndex((attr) => attr.hasOwnProperty('lifecycles')) >= 0 + ? // eslint-disable-next-line no-template-curly-in-string + '"lifecycles":"${attributes.lifecycles.id}",' + : '' + }"name":"not selected lifecycles","not":true,"type":1}]`, + )}` } onChange={(newValue) => { setDataSource(newValue) @@ -325,24 +325,24 @@ export default function DataProviderEditDialog(props) { dataSource?.startsWith('ext:mbehr1.dlt-logs') ? dataSource : props.applyMode - ? // eslint-disable-next-line no-template-curly-in-string - `ext:mbehr1.dlt-logs/get/docs/0/filters?delete=${encodeURIComponent('{"tmpFb":1}')}&disableAll=view${ - attributes.findIndex((attr) => attr.hasOwnProperty('lifecycles')) >= 0 - ? `&add=${encodeURIComponent( - // eslint-disable-next-line no-template-curly-in-string - '{"lifecycles":"${attributes.lifecycles.id}","name":"not selected lifecycles","not":true,"tmpFb":1,"type":1}', - )}` - : '' - }` - : // eslint-disable-next-line no-template-curly-in-string - `ext:mbehr1.dlt-logs/get/docs/0/filters?query=${encodeURIComponent( - `[{${ + ? // eslint-disable-next-line no-template-curly-in-string + `ext:mbehr1.dlt-logs/get/docs/0/filters?delete=${encodeURIComponent('{"tmpFb":1}')}&disableAll=view${ attributes.findIndex((attr) => attr.hasOwnProperty('lifecycles')) >= 0 - ? // eslint-disable-next-line no-template-curly-in-string - '"lifecycles":"${attributes.lifecycles.id}",' + ? `&add=${encodeURIComponent( + // eslint-disable-next-line no-template-curly-in-string + '{"lifecycles":"${attributes.lifecycles.id}","name":"not selected lifecycles","not":true,"tmpFb":1,"type":1}', + )}` : '' - }"name":"not selected lifecycles","not":true,"type":1}]`, - )}` + }` + : // eslint-disable-next-line no-template-curly-in-string + `ext:mbehr1.dlt-logs/get/docs/0/filters?query=${encodeURIComponent( + `[{${ + attributes.findIndex((attr) => attr.hasOwnProperty('lifecycles')) >= 0 + ? // eslint-disable-next-line no-template-curly-in-string + '"lifecycles":"${attributes.lifecycles.id}",' + : '' + }"name":"not selected lifecycles","not":true,"type":1}]`, + )}` } open={manualEditOpen} onChange={(newValue) => { diff --git a/src/webview/src/util.js b/src/webview/src/util.js index 36a7d0c..2a4ddb6 100644 --- a/src/webview/src/util.js +++ b/src/webview/src/util.js @@ -5,6 +5,7 @@ import jp from 'jsonpath' // provide JSON5.parse for conversion functions: import JSON5 from 'json5' import * as uv0 from 'dlt-logs-utils' +import { rqUriDecode, rqUriEncode } from 'dlt-logs-utils/dist/restQuery' // eslint-disable-next-line no-undef if (!globalThis.JSON5) { @@ -137,17 +138,20 @@ export async function triggerRestQueryDetails(dataSourceObj, attributes) { attrKey = attrName.slice(dotPos + 1) attrName = attrName.slice(0, dotPos) } - console.log(`triggerRestQueryDetails attrName='${attrName}' attrKey='${attrKey}'`) - const attribute = attributes?.find((attr) => Object.keys(attr)[0] === attrName) + // console.log(`triggerRestQueryDetails attrName='${attrName}' attrKey='${attrKey}'`) + const attribute = attributes?.find((attr) => { + const name = Object.keys(attr).find((key) => key !== 'fbUid') + return name === attrName + }) if (attribute !== undefined) { const attrValue = attribute[attrName].value const attrKeyValue = Array.isArray(attrValue) ? attrValue.map((e) => (attrKey ? e[attrKey] : e)) : attrKey - ? attrValue && attrKey in attrValue - ? attrValue[attrKey] - : null - : attrValue + ? attrValue && attrKey in attrValue + ? attrValue[attrKey] + : null + : attrValue if (typeof attrKeyValue === 'string') { // console.log(`attrKeyValue='${attrKeyValue}'`, attribute); return wrapStringInQuotes ? `"${attrKeyValue}"` : attrKeyValue @@ -156,7 +160,7 @@ export async function triggerRestQueryDetails(dataSourceObj, attributes) { return JSON.stringify(attrKeyValue) } } - return `` + return wrapStringInQuotes ? `""` : `` } return wrapStringInQuotes ? `"${p1}"` : p1 } @@ -169,13 +173,64 @@ export async function triggerRestQueryDetails(dataSourceObj, attributes) { // -> JSON representation otherwise (e.g. for arrays [...]) // problem is that arrays should not be "" quoted. - requestStr = reqSource.replace(/"\$\{(.*?)\}"/g, (match, p1, offset) => replaceAttr(match, p1, offset, true)) - // replace the URI encoded ones as well, but uri encode them then: - requestStr = requestStr.replace(/%22%24%7B(.*?)%7D%22/g, (match, p1, offset) => - encodeURIComponent(replaceAttr(match, p1, offset, true)), - ) - // support uri encoded attrs like ${attributes.*} (w.o. being double enquoted) as well: - requestStr = requestStr.replace(/%24%7B(.*?)%7D/g, (match, p1, offset) => encodeURIComponent(replaceAttr(match, p1, offset, false))) + // to reduce risk we limit the new handling of replacing ${attribute..*} only to requests with "conversionFunction" inside: + if (!reqSource.includes('conversionFunction')) { + requestStr = reqSource.replace(/"\$\{(.*?)\}"/g, (match, p1, offset) => replaceAttr(match, p1, offset, true)) + // replace the URI encoded ones as well, but uri encode them then: + requestStr = requestStr.replace(/%22%24%7B(.*?)%7D%22/g, (match, p1, offset) => + encodeURIComponent(replaceAttr(match, p1, offset, true)), + ) + // support uri encoded attrs like ${attributes.*} (w.o. being double enquoted) as well: + requestStr = requestStr.replace(/%24%7B(.*?)%7D/g, (match, p1, offset) => encodeURIComponent(replaceAttr(match, p1, offset, false))) + } else { + // console.log(`triggerRestQueryDetails got conversionFunction. Replacing attributes in requestStr='${reqSource}'`) + // we only want attributes in the form of "${attributes.name}" to be replaced within filter expression (apid, ctid,... but not within reportOptions...) + try { + const rq = rqUriDecode(reqSource) + // console.log(`triggerRestQueryDetails got rq.path=${rq.path} rq.cmds.length=${rq.commands.length}`) + // scan all cmds for filter and replace attributes: + for (const cmd of rq.commands) { + if (cmd.param?.includes('${attributes.')) { + switch (cmd.cmd) { + case 'report': + case 'query': + // filters are the array + const filters = JSON5.parse(cmd.param) + if (Array.isArray(filters)) { + for (const filter of filters) { + if (typeof filter === 'object') { + for (const key of Object.keys(filter)) { + const val = filter[key] + if (typeof val === 'string' && val.includes('${attributes.')) { + console.log(`triggerRestQueryDetails replacing attributes in filter[${key}] from '${val}'`) + // todo optimize! need to replace to object/array/string and not a string representation of it... + filter[key] = JSON.parse( + JSON.stringify(val).replace(/"\$\{(.*?)\}"/g, (match, p1, offset) => replaceAttr(match, p1, offset, true)), + ) + console.log(`triggerRestQueryDetails replacing attributes in filter[${key}] to '${filter[key]}'`) + } + } + } else { + console.warn(`triggerRestQueryDetails got non object filter=${filter}`) + } + } + cmd.param = JSON.stringify(filters) + } else { + console.warn(`triggerRestQueryDetails got non array filters=${filters}`) + } + break + default: + // replace all attributes in the cmd.param: + cmd.param = cmd.param.replace(/"\$\{(.*?)\}"/g, (match, p1, offset) => replaceAttr(match, p1, offset, true)) + } + } + } + requestStr = rqUriEncode(rq) + } catch (e) { + console.warn(`triggerRestQueryDetails replacing attributes failed with ${e} using initial str`) + requestStr = reqSource + } + } } const res = await triggerRestQuery(requestStr) @@ -237,8 +292,8 @@ export async function triggerRestQueryDetails(dataSourceObj, attributes) { answer.convResult !== undefined ? answer.convResult : answer.jsonPathResult !== undefined - ? answer.jsonPathResult - : answer.restQueryResult + ? answer.jsonPathResult + : answer.restQueryResult // console.log(`triggerRestQueryDetails got result='${JSON.stringify(answer.result)}'`); } catch (e) { console.log(`triggerRestQueryDetails got error=`, e) diff --git a/src/webview/yarn.lock b/src/webview/yarn.lock index 78932a8..28dbadf 100644 --- a/src/webview/yarn.lock +++ b/src/webview/yarn.lock @@ -4259,10 +4259,10 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -dlt-logs-utils@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/dlt-logs-utils/-/dlt-logs-utils-0.0.2.tgz#b5d8a205c8eee876101fa00e76e6526bd809de8e" - integrity sha512-ApjoO5ng57axu6KEa7tHU8nv1dgpa4yYMmFRCshCW85+44Wzv+1iEIuoV0LiID+9Hay9wt0czwdDBOa1qn74ug== +dlt-logs-utils@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dlt-logs-utils/-/dlt-logs-utils-0.1.0.tgz#440cad79bf2d045d27e5c1b6210db7ff3bbba85a" + integrity sha512-XlAolmR4UfDGimvlhzwW0N2xF7g70MibyforFLC76c5sTbgYd49IUSQY67C1wJSEcX3KqxQ5rcSCX7wk8B2Mjg== dlv@^1.1.3: version "1.1.3" diff --git a/yarn.lock b/yarn.lock index edcbf37..3bc0343 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2285,10 +2285,10 @@ dir-glob@^3.0.0, dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -dlt-logs-utils@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/dlt-logs-utils/-/dlt-logs-utils-0.0.2.tgz#b5d8a205c8eee876101fa00e76e6526bd809de8e" - integrity sha512-ApjoO5ng57axu6KEa7tHU8nv1dgpa4yYMmFRCshCW85+44Wzv+1iEIuoV0LiID+9Hay9wt0czwdDBOa1qn74ug== +dlt-logs-utils@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dlt-logs-utils/-/dlt-logs-utils-0.1.0.tgz#440cad79bf2d045d27e5c1b6210db7ff3bbba85a" + integrity sha512-XlAolmR4UfDGimvlhzwW0N2xF7g70MibyforFLC76c5sTbgYd49IUSQY67C1wJSEcX3KqxQ5rcSCX7wk8B2Mjg== doctrine@^3.0.0: version "3.0.0"