Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Composer/packages/client/src/shell/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { LgMetaData, LgTemplateRef, LgType, extractTemplateNameFromExpression } from '@bfc/shared';
import { LgMetaData, LgTemplateRef, LgType } from '@bfc/shared';
import { LgTemplate, MicrosoftIDialog, ShellApi } from '@botframework-composer/types';

type SerializableLg = {
Expand Down Expand Up @@ -30,6 +30,7 @@ export const serializeLgTemplate = (
return '';
}

const exprRegex = /^\${(.*)\(\)}$/;
const serializableLg: SerializableLg = {
originalId: fromId,
mainTemplateBody: lgTemplate?.body,
Expand All @@ -43,8 +44,9 @@ export const serializeLgTemplate = (
? (lgTemplate.properties[responseType] as string[])
: ([lgTemplate.properties[responseType]] as string[]);
for (const subTemplateItem of subTemplateItems) {
const subTemplateId = extractTemplateNameFromExpression(subTemplateItem.trim());
if (subTemplateId) {
const matched = subTemplateItem.trim().match(exprRegex);
if (matched && matched.length > 1) {
const subTemplateId = matched[1];
const subTemplate = lgTemplates.find((x) => x.name === subTemplateId);
if (subTemplate) {
if (!serializableLg.relatedLgTemplateBodies) {
Expand Down
8 changes: 6 additions & 2 deletions Composer/packages/lib/code-editor/src/lg/ModalityPivot.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { CodeEditorSettings, LgTemplate, TelemetryClient, extractTemplateNameFromExpression } from '@bfc/shared';
import { CodeEditorSettings, LgTemplate, TelemetryClient } from '@bfc/shared';
import { FluentTheme, FontSizes } from '@uifabric/fluent-theme';
import formatMessage from 'format-message';
import { IconButton } from 'office-ui-fabric-react/lib/Button';
Expand All @@ -20,7 +20,11 @@ import mergeWith from 'lodash/mergeWith';

import { LGOption } from '../utils';
import { ItemWithTooltip } from '../components/ItemWithTooltip';
import { getTemplateId, structuredResponseToString } from '../utils/structuredResponse';
import {
extractTemplateNameFromExpression,
getTemplateId,
structuredResponseToString,
} from '../utils/structuredResponse';

import { AttachmentModalityEditor } from './modalityEditors/AttachmentModalityEditor';
import { SpeechModalityEditor } from './modalityEditors/SpeechModalityEditor';
Expand Down
4 changes: 2 additions & 2 deletions Composer/packages/lib/code-editor/src/lg/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type LgCardTemplateType = typeof lgCardAttachmentTemplates[number];
export const cardTemplates: Record<LgCardTemplateType, string> = {
adaptive: `> To learn more Adaptive Cards format, read the documentation at
> https://docs.microsoft.com/en-us/adaptive-cards/getting-started/bots
- \${{
- \`\`\`\${json({
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.2",
"type": "AdaptiveCard",
Expand All @@ -34,7 +34,7 @@ export const cardTemplates: Record<LgCardTemplateType, string> = {
"isSubtle": false
}
]
}}`,
})}\`\`\``,
hero: `[HeroCard
title =
subtitle =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import formatMessage from 'format-message';
import React from 'react';
import { IDropdownOption, DropdownMenuItemType } from 'office-ui-fabric-react/lib/Dropdown';
import { CodeEditorSettings, extractTemplateNameFromExpression } from '@bfc/shared';
import { CodeEditorSettings } from '@bfc/shared';

import {
AttachmentsStructuredResponseItem,
AttachmentLayoutStructuredResponseItem,
CommonModalityEditorProps,
} from '../types';
import { extractTemplateNameFromExpression } from '../../utils/structuredResponse';

import { ModalityEditorContainer } from './ModalityEditorContainer';
import { AttachmentArrayEditor } from './AttachmentArrayEditor';
Expand Down Expand Up @@ -45,11 +46,7 @@ const AttachmentModalityEditor = React.memo(
(newItems: string[]) => {
setItems(newItems);
onUpdateResponseTemplate({
Attachments: {
kind: 'Attachments',
value: newItems.map((item) => `\${json(${item}())}`),
valueType: 'direct',
},
Attachments: { kind: 'Attachments', value: newItems.map((item) => `\${${item}()}`), valueType: 'direct' },
});
},
[setItems, onUpdateResponseTemplate]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { LgTemplate, extractTemplateNameFromExpression } from '@bfc/shared';
import { LgTemplate } from '@bfc/shared';

import { activityTemplateType } from '../lg/constants';
import {
Expand All @@ -20,6 +20,7 @@ import {
} from '../lg/types';

const subTemplateNameRegex = /\${(.*)}/;
const templateNameExtractRegex = /\${(.*)(\(.*\))\s*}/;
const defaultIndent = ' ';

const getStructuredResponseHelper = (value: unknown, kind: 'Text' | 'Speak' | 'Attachments') => {
Expand Down Expand Up @@ -112,6 +113,14 @@ export const getStructuredResponseFromTemplate = (lgTemplate?: LgTemplate): Part
return Object.keys(structuredResponse).length ? structuredResponse : undefined;
};

/**
* Extracts template name from an LG expression
* ${templateName(params)} => templateName
* @param expression Expression to extract template name from.
*/
export const extractTemplateNameFromExpression = (expression: string): string | undefined =>
expression.match(templateNameExtractRegex)?.[1]?.trim();

export const structuredResponseToString = (structuredResponse: PartialStructuredResponse): string => {
const keys = Object.keys(structuredResponse);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { parseTemplateBody, extractTemplateNameFromExpression } from '../../../src';
import { parseTemplateBody } from '../../../src';

const normalTemplateBody = `- variation0
- variation1
Expand Down Expand Up @@ -87,14 +87,4 @@ describe('lgStringUtils', () => {
expect(items.length).toBe(0);
expect(items).toEqual([]);
});

it('extractTemplateNameFromExpression: Extracts template name from an LG expression', () => {
const templateName = extractTemplateNameFromExpression('${templateName()}');
expect(templateName).toEqual('templateName');
});

it('extractTemplateNameFromExpression: Extracts template name from an LG expression in json method', () => {
const templateName = extractTemplateNameFromExpression('${json(templateName())}');
expect(templateName).toEqual('templateName');
});
});
7 changes: 1 addition & 6 deletions Composer/packages/lib/shared/src/lgUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,4 @@ export { default as LgTemplateRef } from './models/LgTemplateRef';
export { default as LgType } from './models/LgType';
export { extractLgTemplateRefs } from './parsers/parseLgTemplateRef';
export { LgNamePattern } from './parsers/lgPatterns';
export {
TemplateBodyItem,
parseTemplateBody,
templateBodyItemsToString,
extractTemplateNameFromExpression,
} from './stringBuilders/lgStringUtils';
export { TemplateBodyItem, parseTemplateBody, templateBodyItemsToString } from './stringBuilders/lgStringUtils';
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable security/detect-unsafe-regex */
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/* eslint-disable security/detect-unsafe-regex */

export type TemplateBodyItem = {
kind: 'newline' | 'variation' | 'comment';
Expand Down Expand Up @@ -106,17 +106,3 @@ export const templateBodyItemsToString = (items: TemplateBodyItem[]) =>
}
})
.join('\n');

const templateNameExtractRegex = /\${(?<templateName>.*)(\(.*\))\s*}/;
const jsonTemplateNameExtractRegex = /\${json\((?<templateName>.*)(\(.*\))\s*}/;

/**
* Extracts template name from an LG expression
* ${templateName(params)} => templateName
* ${json(templateName(params))} => templateName
* @param expression Expression to extract template name from.
*/
export const extractTemplateNameFromExpression = (expression: string): string | undefined =>
expression.startsWith('${json(')
? expression.match(jsonTemplateNameExtractRegex)?.groups?.templateName?.trim()
: expression.match(templateNameExtractRegex)?.groups?.templateName?.trim();
1 change: 0 additions & 1 deletion Composer/packages/test-utils/src/react/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const babelConfig = {
require.resolve('@babel/plugin-transform-runtime'),
require.resolve('@babel/plugin-proposal-optional-chaining'),
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'),
require.resolve('@babel/plugin-transform-named-capturing-groups-regex'),
],
};

Expand Down
16 changes: 10 additions & 6 deletions Composer/packages/ui-plugins/lg/src/LgWidget/useLgTemplate.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { LgTemplateRef, parseTemplateBody, extractTemplateNameFromExpression } from '@bfc/shared';
import { LgTemplateRef, parseTemplateBody } from '@bfc/shared';
import { LgTemplate, useShellApi } from '@bfc/extension-client';

import { locateLgTemplatePosition } from '../locateLgTemplatePosition';

const exprRegex = /^\${(.*)\(\)}$/;

const getAttachmentDisplayText = (template: LgTemplate) => {
const cardType = template.properties?.$type as string;
const title = (template.properties?.title as string) ?? '';
Expand All @@ -29,9 +31,10 @@ const getLgTemplateTextData = (
if (responseType === 'Text' || responseType === 'Speak') {
const subTemplateItem = template.properties[responseType];
if (subTemplateItem && typeof subTemplateItem === 'string') {
const subTemplateId = extractTemplateNameFromExpression(subTemplateItem.trim());
const matched = subTemplateItem.trim().match(exprRegex);
//If it's a template
if (subTemplateId) {
if (matched && matched.length > 1) {
const subTemplateId = matched[1];
const subTemplate = templates.find((x) => x.name === subTemplateId);
// If found template and it matches auto-generated names
if (subTemplate) {
Expand All @@ -54,9 +57,10 @@ const getLgTemplateTextData = (
: [template.properties[responseType]]) as string[];
const subTemplateItem = subTemplateItems[0];
if (subTemplateItem && typeof subTemplateItem === 'string') {
const subTemplateId = extractTemplateNameFromExpression(subTemplateItem.trim());
// If it's a template
if (subTemplateId) {
const matched = subTemplateItem.trim().match(exprRegex);
//If it's a template
if (matched && matched.length > 1) {
const subTemplateId = matched[1];
const subTemplate = templates.find((x) => x.name === subTemplateId);
if (subTemplate) {
acc[responseType] = {
Expand Down