Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Closed
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
2 changes: 1 addition & 1 deletion Composer/packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"babel-plugin-named-asset-import": "^0.3.1",
"babel-preset-react-app": "^7.0.1",
"bfj": "6.1.1",
"botbuilder-lg": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview0.tgz",
"botbuilder-lg": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz",
"case-sensitive-paths-webpack-plugin": "2.2.0",
"codemirror": "^5.45.0",
"css-loader": "1.0.0",
Expand Down
11 changes: 5 additions & 6 deletions Composer/packages/client/src/ShellApi.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/*eslint @typescript-eslint/no-use-before-define: ["error", { "functions": false }]*/
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React, { useEffect, useContext, useMemo, useState } from 'react';
import React, { useEffect, useContext, useMemo } from 'react';
import { ShellData } from '@bfc/shared';
import isEqual from 'lodash.isequal';
import get from 'lodash.get';

import { parseLgTemplate, checkLgContent, updateTemplateInContent } from '../src/store/action/lg';

import { isExpression } from './utils';
import * as lgUtil from './utils/lgUtil';
import { StoreContext } from './store';
Expand Down Expand Up @@ -245,16 +244,16 @@ export const ShellApi: React.FC = () => {
if (!file) throw new Error(`lg file ${id} not found`);
if (!templateName) throw new Error(`templateName is missing or empty`);

parseLgTemplate(template);
lgUtil.parseLgTemplate(template);

await updateLgTemplate({
file,
templateName,
template,
});

const content = updateTemplateInContent({ content: file.content, templateName, template });
return checkLgContent(content);
const content = lgUtil.updateTemplate(file.content, templateName, template);
return lgUtil.checkLgContent(content);
}

function removeLgTemplateHandler({ id, templateName }, event) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export const LGPage = props => {
onText={formatMessage('Edit mode')}
offText={formatMessage('Edit mode')}
checked={editMode}
disabled={(!isRoot && editMode === false) || fileValid === false}
disabled={(!isRoot && editMode === false) || (codeRange === null && fileValid === false)}
onChange={onToggleEditMode}
/>
</div>
Expand Down
90 changes: 5 additions & 85 deletions Composer/packages/client/src/store/action/lg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,74 +7,6 @@ import { ActionCreator } from '../types';
import { ActionTypes } from './../../constants';
import httpClient from './../../utils/httpUtil';

export function textFromTemplates(templates) {
let text = '';

templates.forEach(template => {
if (template.Name && (template.Body !== null && template.Body !== undefined)) {
text += `# ${template.Name.trim()}`;
if (template.Parameters && template.Parameters.length > 0) {
text += '(' + template.Parameters.join(', ') + ')';
}
text += '\n';
text += `${template.Body.trim()}`;
}
});

return text;
}

// update & remove template & create if not exist
export function updateTemplateInContent({ content, templateName, template }) {
const oldTemplates = lgUtil.parse(content);
if (Array.isArray(oldTemplates) === false) throw new Error('origin lg file is not valid');

const originalTemplate = oldTemplates.find(x => x.Name === templateName);
let newContent = content.replace(/\s+$/, '');

if (originalTemplate === undefined) {
newContent = `${content}${content ? '\n\n' : ''}${textFromTemplates([template])}\n`;
} else {
const startLineNumber = originalTemplate.ParseTree._start.line;
const endLineNumber = originalTemplate.ParseTree._stop && originalTemplate.ParseTree._stop.line;

const lines = content.split('\n');
const contentBefore = lines.slice(0, startLineNumber - 1).join('\n');
const contentAfter = lines.slice(endLineNumber || lines.length).join('\n');
const newTemplateContent = textFromTemplates([template]);

newContent = [contentBefore, newTemplateContent, contentAfter].join('\n');
}

return newContent;
}

/**
*
* @param {Name: string, ?Parameters: string[], Body: string} template
*/
export function parseLgTemplate(template) {
const content = textFromTemplates([template]);

if (lgUtil.parse(content).length !== 1) {
throw new Error('Not a single template');
}
}

/**
*
* @param string, content
*/
export function checkLgContent(content) {
// check lg content, make up error message

const diagnostics = lgUtil.check(content);
if (lgUtil.isValid(diagnostics) === false) {
const errorMsg = lgUtil.combineMessage(diagnostics);
throw new Error(errorMsg);
}
}

export const updateLgFile: ActionCreator = async ({ dispatch }, { id, content }) => {
try {
const response = await httpClient.put(`/projects/opened/lgFiles/${id}`, { id, content });
Expand Down Expand Up @@ -124,28 +56,16 @@ export const removeLgFile: ActionCreator = async ({ dispatch }, { id }) => {
};

export const updateLgTemplate: ActionCreator = async (store, { file, templateName, template }) => {
parseLgTemplate(template);
const newContent = updateTemplateInContent({ content: file.content, templateName, template });
const newContent = lgUtil.updateTemplate(file.content, templateName, template);
return await updateLgFile(store, { id: file.id, content: newContent });
};

export const createLgTemplate: ActionCreator = async (store, { file, template, position }) => {
parseLgTemplate(template);

let content = file.content;
if (position === 0) {
content = textFromTemplates([template]) + '\n\n' + content;
} else {
content = content.replace(/\s+$/, '') + '\n\n' + textFromTemplates([template]) + '\n';
}

checkLgContent(content);
return await updateLgFile(store, { id: file.id, content });
export const createLgTemplate: ActionCreator = async (store, { file, template }) => {
const newContent = lgUtil.addTemplate(file.content, template);
return await updateLgFile(store, { id: file.id, content: newContent });
};

export const removeLgTemplate: ActionCreator = async (store, { file, templateName }) => {
const newContent = updateTemplateInContent({ content: file.content, templateName, template: {} });
checkLgContent(newContent);

const newContent = lgUtil.removeTemplate(file.content, templateName);
return await updateLgFile(store, { id: file.id, content: newContent });
};
66 changes: 64 additions & 2 deletions Composer/packages/client/src/utils/lgUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@ import { get } from 'lodash';

const lgStaticChecker = new StaticChecker();

const lgImportResolver = new ImportResolver();
const lgImportResolver = ImportResolver.fileResolver;

interface Template {
Name: string;
Parameters: string[];
Body: string;
}

export function isValid(diagnostics: Diagnostic[]): boolean {
return diagnostics.every(d => d.Severity !== DiagnosticSeverity.Error);
}

export function check(content: string, id = ''): Diagnostic[] {
// @ts-ignore
return lgStaticChecker.checkText(content, id, lgImportResolver);
}

Expand All @@ -31,3 +36,60 @@ export function combineMessage(diagnostics: Diagnostic[]): string {
return msg;
}, '');
}

export function checkLgContent(content: string) {
// check lg content, make up error message
const diagnostics = check(content);
if (isValid(diagnostics) === false) {
const errorMsg = combineMessage(diagnostics);
throw new Error(errorMsg);
}
}

export function updateTemplate(content: string, templateName: string, { Name, Parameters, Body }: Template): string {
const resource = LGParser.parse(content);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the store should hold a bunch of resources? so we don't have to parse it every time?

Copy link
Contributor Author

@zhixzhan zhixzhan Oct 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgUtil do not have state, or we pass a parsed content to it ?

content: string ---> content: LGResource

Copy link
Contributor Author

@zhixzhan zhixzhan Oct 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought again, this way would increase the cost of caller.
how about cache it at lgUtil, then calculate hash diff determine re-parse it or not.

const localParsedLG = { id, contentHash, LGResource };

export function updateTemplate(content: string, templateName: string, { Name, Parameters, Body }: Template): string {
  const contentHash = hash(content);
  let resource;
  if( contentHash in localParsedLG ) {
      resource =  localParsedLG.get(contentHash)
  } else {
      resource = LGParser.parse(content);
     //update localParsedLG
  }
  ...
}

maybe this method should implmented withing lg parser

// add if not exist
if (resource.Templates.findIndex(t => t.Name === templateName) === -1) {
return resource.addTemplate(Name, Parameters, Body).toString();
} else {
return resource.updateTemplate(templateName, Name, Parameters, Body).toString();
}
}

export function addTemplate(content: string, { Name, Parameters, Body }: Template): string {
const resource = LGParser.parse(content);
return resource.addTemplate(Name, Parameters, Body).toString();
}

export function removeTemplate(content: string, templateName: string): string {
const resource = LGParser.parse(content);
return resource.deleteTemplate(templateName).toString();
}

export function textFromTemplates(templates) {
let text = '';

templates.forEach(template => {
if (template.Name && (template.Body !== null && template.Body !== undefined)) {
text += `# ${template.Name.trim()}`;
if (template.Parameters && template.Parameters.length > 0) {
text += '(' + template.Parameters.join(', ') + ')';
}
text += '\n';
text += `${template.Body.trim()}`;
}
});

return text;
}
/**
*
* @param {Name: string, ?Parameters: string[], Body: string} template
*/
export function parseLgTemplate(template) {
const content = textFromTemplates([template]);

if (parse(content).length !== 1) {
throw new Error('Not a single template');
}
}
2 changes: 1 addition & 1 deletion Composer/packages/lib/indexers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
},
"dependencies": {
"botbuilder-expression-parser": "^4.5.11",
"botbuilder-lg": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview0.tgz",
"botbuilder-lg": "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz",
"lodash.get": "^4.4.2",
"lodash.has": "^4.5.2",
"lodash.uniq": "^4.5.0",
Expand Down
15 changes: 12 additions & 3 deletions Composer/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3964,9 +3964,18 @@ botbuilder-expression-parser@^4.5.9:
xmldom "^0.1.27"
xpath "0.0.27"

"botbuilder-lg@https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview0.tgz":
version "4.7.0-preview0"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview0.tgz#46ac6e53e44ba64ae56971ab8404c3f2f9ab239d"
"botbuilder-lg@https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz":
version "4.7.0-preview2"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz#2a3dd7d7991e0727fb88630501d2fb14d475537b"
dependencies:
antlr4ts "0.5.0-alpha.1"
botframework-expressions "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botframework-expressions/-/4.7.0-preview0.tgz"
lodash "^4.17.11"
uuid "^3.3.3"

"botbuilder-lg@https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz":
version "4.7.0-preview2"
resolved "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botbuilder-lg/-/4.7.0-preview2.tgz#2a3dd7d7991e0727fb88630501d2fb14d475537b"
dependencies:
antlr4ts "0.5.0-alpha.1"
botframework-expressions "https://botbuilder.myget.org/F/botbuilder-declarative/npm/botframework-expressions/-/4.7.0-preview0.tgz"
Expand Down