-
Notifications
You must be signed in to change notification settings - Fork 369
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: $designer.id based URL (#4242)
* mark out contribution points * impl designer path encoder/decoder * use double quotation * apply encoder / decoder to navigation logic * fix tslint error * fix import order * remove comments * fix tslint error * encode breadcrumb path Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
- Loading branch information
Showing
5 changed files
with
213 additions
and
10 deletions.
There are no files selected for viewing
69 changes: 69 additions & 0 deletions
69
Composer/packages/client/__tests__/utils/convertUtils/designerPathEncoder.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { SDKKinds } from '@bfc/shared'; | ||
|
||
import { | ||
decodeDesignerPathToArrayPath, | ||
encodeArrayPathToDesignerPath, | ||
} from '../../../src/utils/convertUtils/designerPathEncoder'; | ||
|
||
const dialog = { | ||
triggers: [ | ||
{ | ||
$kind: SDKKinds.OnIntent, | ||
$designer: { id: '1234' }, | ||
actions: [ | ||
{ | ||
$kind: SDKKinds.SendActivity, | ||
$designer: { id: '5678' }, | ||
}, | ||
{ | ||
$kind: SDKKinds.SendActivity, | ||
}, | ||
], | ||
}, | ||
], | ||
}; | ||
|
||
describe('encodeArrayPathToDesignerPath()', () => { | ||
it('can handle empty input.', () => { | ||
expect(encodeArrayPathToDesignerPath(dialog, '')).toEqual(''); | ||
expect(encodeArrayPathToDesignerPath(undefined, '')).toEqual(''); | ||
}); | ||
|
||
it('should transform valid array path.', () => { | ||
expect(encodeArrayPathToDesignerPath(dialog, 'triggers[0].actions[0]')).toEqual(`triggers["1234"].actions["5678"]`); | ||
}); | ||
|
||
it('can handle subdata without $designer.id.', () => { | ||
expect(encodeArrayPathToDesignerPath(dialog, 'triggers[0].actions[1]')).toEqual(`triggers["1234"].actions[1]`); | ||
}); | ||
|
||
it('can recover from invalid array path.', () => { | ||
expect(encodeArrayPathToDesignerPath(dialog, 'triggers[0].actions[99]')).toEqual('triggers[0].actions[99]'); | ||
}); | ||
}); | ||
|
||
describe('decodeDesignerPathToArrayPath()', () => { | ||
it('should transform valid designer path.', () => { | ||
expect(decodeDesignerPathToArrayPath(dialog, `triggers["1234"].actions["5678"]`)).toEqual('triggers[0].actions[0]'); | ||
}); | ||
|
||
it('can handle valid designer path.', () => { | ||
expect(decodeDesignerPathToArrayPath(dialog, `triggers["1234"].actions["9999"]`)).toEqual( | ||
`triggers["1234"].actions["9999"]` | ||
); | ||
expect(decodeDesignerPathToArrayPath(dialog, `triggers["5678"].actions["1234"]`)).toEqual( | ||
`triggers["5678"].actions["1234"]` | ||
); | ||
expect(decodeDesignerPathToArrayPath(dialog, `dialogs["1234"].actions["5678"]`)).toEqual( | ||
`dialogs["1234"].actions["5678"]` | ||
); | ||
}); | ||
|
||
it('can handle empty input.', () => { | ||
expect(decodeDesignerPathToArrayPath(dialog, '')).toEqual(''); | ||
expect(decodeDesignerPathToArrayPath(undefined, '')).toEqual(''); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
115 changes: 115 additions & 0 deletions
115
Composer/packages/client/src/utils/convertUtils/designerPathEncoder.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import get from 'lodash/get'; | ||
|
||
const ArrayPathPattern = /^(\w+)\[(\d+)\]$/; | ||
|
||
const parseArrayPath = (subpath: string) => { | ||
const matchResults = ArrayPathPattern.exec(subpath); | ||
if (matchResults?.length !== 3) return null; | ||
|
||
const [, prefix, indexStr] = matchResults; | ||
return { | ||
prefix, | ||
index: parseInt(indexStr), | ||
}; | ||
}; | ||
|
||
/** | ||
* Inputs `triggers[0].actions[0]`, outputs `triggers['12345'].actions['67890']` | ||
* @param dialog Current active Adaptive dialog json | ||
* @param path Current focus path in index format | ||
*/ | ||
export const encodeArrayPathToDesignerPath = (dialog, path: string): string => { | ||
if (!path || !dialog) return path; | ||
|
||
const subpaths = path.split('.'); | ||
const transformedSubpaths: string[] = []; | ||
|
||
let rootData = dialog; | ||
for (const p of subpaths) { | ||
const pathInfo = parseArrayPath(p); | ||
if (!pathInfo) { | ||
// For invalid input, fallback to origin array path. | ||
return path; | ||
} | ||
|
||
const subData = get(rootData, p); | ||
if (!subData) { | ||
// For invalid data, fallback to origin array path. | ||
return path; | ||
} | ||
|
||
const { prefix, index } = pathInfo; | ||
// For subdata without designer.id, fallback to array index. | ||
const designerId: string | number = get(subData, '$designer.id', index); | ||
const designerIdStr = typeof designerId === 'string' ? `"${designerId}"` : designerId; | ||
|
||
const designerSubpath = `${prefix}[${designerIdStr}]`; | ||
transformedSubpaths.push(designerSubpath); | ||
|
||
// descent to subData | ||
rootData = subData; | ||
} | ||
|
||
const designerPath = transformedSubpaths.join('.'); | ||
return designerPath; | ||
}; | ||
|
||
const DesignerPathPattern = /^(\w+)\["(\w+)"\]$/; | ||
|
||
const parseDesignerPath = (subpath: string) => { | ||
const matchResults = DesignerPathPattern.exec(subpath); | ||
if (matchResults?.length !== 3) return null; | ||
|
||
const [, prefix, designerId] = matchResults; | ||
return { | ||
prefix, | ||
designerId, | ||
}; | ||
}; | ||
|
||
/** | ||
* Inputs `triggers['12345'].actions['67890']`, outputs `triggers[0].actions[0]` | ||
* @param dialog Current active Adaptive dialog json | ||
* @param path Current focus path in designer format | ||
*/ | ||
export const decodeDesignerPathToArrayPath = (dialog, path: string): string => { | ||
if (!path || !dialog) return path; | ||
|
||
const subpaths = path.split('.'); | ||
const transformedSubpaths: string[] = []; | ||
|
||
let rootData = dialog; | ||
for (const p of subpaths) { | ||
const pathInfo = parseDesignerPath(p); | ||
if (!pathInfo) { | ||
// For invalid input path, fallback to origin designer path | ||
return path; | ||
} | ||
|
||
const { prefix: arrayName, designerId } = pathInfo; | ||
|
||
const arrayData = get(rootData, arrayName); | ||
if (!Array.isArray(arrayData)) { | ||
// For invalid data, fallback to origin designer path | ||
return path; | ||
} | ||
|
||
const arrayIndex = arrayData.findIndex((x) => get(x, '$designer.id') === designerId); | ||
if (arrayIndex === -1) { | ||
// Can't find given designer id, fallback to input path. | ||
return path; | ||
} | ||
|
||
const arraySubpath = `${arrayName}[${arrayIndex}]`; | ||
transformedSubpaths.push(arraySubpath); | ||
|
||
// descent to subData | ||
rootData = arrayData[arrayIndex]; | ||
} | ||
|
||
const indexPath = transformedSubpaths.join('.'); | ||
return indexPath; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters