• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

microsoft / BotFramework-Composer / 8946411146

28 Mar 2024 01:12PM UTC coverage: 54.424% (-0.06%) from 54.482%
8946411146

push

github

web-flow
Merge pull request #9713 from OEvgeny/chore/update-electron-26

chore: update dependencies

7577 of 18338 branches covered (41.32%)

Branch coverage included in aggregate %.

939 of 1507 new or added lines in 170 files covered. (62.31%)

8 existing lines in 8 files now uncovered.

19745 of 31864 relevant lines covered (61.97%)

26.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.02
/Composer/packages/lib/code-editor/src/utils/structuredResponse.ts
1
// Copyright (c) Microsoft Corporation.
2
// Licensed under the MIT License.
3

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

6
import { activityTemplateType } from '../lg/constants';
4✔
7
import {
8
  acceptedAttachmentLayout,
9
  acceptedInputHintValues,
10
  ArrayBasedStructuredResponseItem,
11
  AttachmentLayoutStructuredResponseItem,
12
  AttachmentsStructuredResponseItem,
13
  InputHintStructuredResponseItem,
14
  PartialStructuredResponse,
15
  SpeechStructuredResponseItem,
16
  StructuredResponseItem,
17
  structuredResponseKeys,
18
  SuggestedActionsStructuredResponseItem,
19
  TextStructuredResponseItem,
20
} from '../lg/types';
4✔
21

22
const subTemplateNameRegex = /\${(.*)}/;
4✔
23
const defaultIndent = '    ';
4✔
24

25
const getStructuredResponseHelper = (value: unknown, kind: 'Text' | 'Speak' | 'Attachments') => {
4✔
26
  if (typeof value === 'string') {
5✔
27
    const valueAsString = value as string;
4✔
28
    const valueType = subTemplateNameRegex.test(valueAsString) ? 'template' : 'direct';
4✔
29

30
    return { kind, value: [valueAsString.trim()], valueType };
4✔
31
  }
32

33
  if (Array.isArray(value) && kind === 'Attachments') {
1✔
34
    const valueAsArray = (value as string[]).map((v) => v.trim());
3✔
35

36
    return { kind, value: valueAsArray, valueType: 'direct' };
1✔
37
  }
38

39
  return undefined;
×
40
};
41

42
const getStructuredResponseByKind = (
4✔
43
  template: LgTemplate,
44
  kind: StructuredResponseItem['kind'],
45
): StructuredResponseItem | undefined => {
46
  const value = template.properties?.[kind];
8!
47
  if (value === undefined) {
8!
48
    return undefined;
×
49
  }
50

51
  switch (kind) {
52
    case 'Text':
6!
53
      return getStructuredResponseHelper(value, 'Text') as TextStructuredResponseItem;
2✔
54
    case 'Speak':
55
      return getStructuredResponseHelper(value, 'Speak') as SpeechStructuredResponseItem;
2✔
56
    case 'Attachments':
57
      return getStructuredResponseHelper(value, 'Attachments') as AttachmentsStructuredResponseItem;
1✔
58
    case 'SuggestedActions': {
59
      const responseValue = Array.isArray(value) ? (value as string[]).map((v) => v.trim()) : [value];
3!
60
      return { kind: 'SuggestedActions', value: responseValue } as SuggestedActionsStructuredResponseItem;
1✔
61
    }
62
    case 'AttachmentLayout':
NEW
63
      if (acceptedAttachmentLayout.includes(value as (typeof acceptedAttachmentLayout)[number])) {
×
64
        return {
×
65
          kind: 'AttachmentLayout',
66
          value: value as (typeof acceptedAttachmentLayout)[number],
67
        } as AttachmentLayoutStructuredResponseItem;
68
      }
69
      break;
×
70
    case 'InputHint':
NEW
71
      if (acceptedInputHintValues.includes(value as (typeof acceptedInputHintValues)[number])) {
×
72
        return {
×
73
          kind: 'InputHint',
74
          value: value as (typeof acceptedInputHintValues)[number],
75
        } as InputHintStructuredResponseItem;
76
      }
77
      break;
×
78
  }
79

80
  return undefined;
2✔
81
};
82

83
/**
84
 * Converts template properties to structured response.
85
 * @param lgTemplate LgTemplate to convert.
86
 */
87
export const getStructuredResponseFromTemplate = (lgTemplate?: LgTemplate): PartialStructuredResponse | undefined => {
4✔
88
  if (!lgTemplate) {
5✔
89
    return undefined;
1✔
90
  }
91
  if (!lgTemplate.body) {
4✔
92
    return undefined;
1✔
93
  }
94

95
  if (lgTemplate.properties?.$type !== activityTemplateType) {
3!
96
    return undefined;
1✔
97
  }
98

99
  if (!Object.keys(lgTemplate.properties).length) {
2!
100
    return undefined;
×
101
  }
102

103
  const structuredResponse: PartialStructuredResponse = Object.keys(lgTemplate.properties).reduce((response, kind) => {
2✔
104
    const value = getStructuredResponseByKind(lgTemplate, kind as StructuredResponseItem['kind']);
8✔
105
    if (value !== undefined) {
8✔
106
      response[kind] = value;
6✔
107
    }
108

109
    return response;
8✔
110
  }, {} as PartialStructuredResponse);
111

112
  return Object.keys(structuredResponse).length ? structuredResponse : undefined;
2!
113
};
114

115
export const structuredResponseToString = (structuredResponse: PartialStructuredResponse): string => {
4✔
116
  const keys = Object.keys(structuredResponse);
3✔
117

118
  if (!keys.length) {
3✔
119
    return '';
1✔
120
  }
121

122
  const getValue = (kind: StructuredResponseItem['kind']): string | undefined => {
2✔
123
    switch (kind) {
124
      case 'Speak': {
8!
125
        const item = structuredResponse.Speak as SpeechStructuredResponseItem;
2✔
126
        return item.value[0];
2✔
127
      }
128
      case 'Text': {
129
        const item = structuredResponse.Text as TextStructuredResponseItem;
2✔
130
        return item.value[0];
2✔
131
      }
132
      case 'InputHint': {
133
        const item = structuredResponse.InputHint as InputHintStructuredResponseItem;
×
134
        return item?.value;
×
135
      }
136
      case 'AttachmentLayout': {
137
        const item = structuredResponse.AttachmentLayout as AttachmentLayoutStructuredResponseItem;
×
138
        return item?.value;
×
139
      }
140
      case 'SuggestedActions': {
141
        const item = structuredResponse.SuggestedActions as SuggestedActionsStructuredResponseItem;
2✔
142
        return item.value.join(' | ');
2✔
143
      }
144
      case 'Attachments': {
145
        const item = structuredResponse.Attachments as AttachmentsStructuredResponseItem;
2✔
146
        return item.value.join(' | ');
2✔
147
      }
148
    }
149
  };
150

151
  const body = structuredResponseKeys
152
    .filter((k) => keys.includes(k))
12✔
153
    .reduce((text, kind) => {
154
      const value = getValue(kind as StructuredResponseItem['kind']);
8✔
155

156
      if (value) {
8✔
157
        text += `\t${kind} = ${value}\n`;
4✔
158
      }
159
      return text;
8✔
160
    }, '');
161

162
  return body ? `[${activityTemplateType}\n${body}]\n`.replace(/\t/gm, defaultIndent) : '';
2✔
163
};
164

165
export const getTemplateId = <T extends ArrayBasedStructuredResponseItem>(response: T): string | undefined => {
4✔
166
  if (response?.value[0]) {
4!
167
    return extractTemplateNameFromExpression(response.value[0]);
4✔
168
  }
169
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc