@servicetitan/titan-chatbot-api 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/api-client/__mocks__/chatbot-api-client.mock.d.ts +11 -0
- package/dist/api-client/__mocks__/chatbot-api-client.mock.d.ts.map +1 -0
- package/dist/api-client/__mocks__/chatbot-api-client.mock.js +47 -0
- package/dist/api-client/__mocks__/chatbot-api-client.mock.js.map +1 -0
- package/dist/api-client/base/chatbot-api-client.d.ts +27 -0
- package/dist/api-client/base/chatbot-api-client.d.ts.map +1 -0
- package/dist/api-client/base/chatbot-api-client.js +10 -0
- package/dist/api-client/base/chatbot-api-client.js.map +1 -0
- package/dist/api-client/help-center/__tests__/converter-from-models.test.d.ts +2 -0
- package/dist/api-client/help-center/__tests__/converter-from-models.test.d.ts.map +1 -0
- package/dist/api-client/help-center/__tests__/converter-from-models.test.js +34 -0
- package/dist/api-client/help-center/__tests__/converter-from-models.test.js.map +1 -0
- package/dist/api-client/help-center/__tests__/converter-to-models.test.d.ts +2 -0
- package/dist/api-client/help-center/__tests__/converter-to-models.test.d.ts.map +1 -0
- package/dist/api-client/help-center/__tests__/converter-to-models.test.js +82 -0
- package/dist/api-client/help-center/__tests__/converter-to-models.test.js.map +1 -0
- package/dist/api-client/help-center/chatbot-api-client.d.ts +31 -0
- package/dist/api-client/help-center/chatbot-api-client.d.ts.map +1 -0
- package/dist/api-client/help-center/chatbot-api-client.js +90 -0
- package/dist/api-client/help-center/chatbot-api-client.js.map +1 -0
- package/dist/api-client/help-center/converter-from-models.d.ts +13 -0
- package/dist/api-client/help-center/converter-from-models.d.ts.map +1 -0
- package/dist/api-client/help-center/converter-from-models.js +113 -0
- package/dist/api-client/help-center/converter-from-models.js.map +1 -0
- package/dist/api-client/help-center/converter-to-models.d.ts +13 -0
- package/dist/api-client/help-center/converter-to-models.d.ts.map +1 -0
- package/dist/api-client/help-center/converter-to-models.js +95 -0
- package/dist/api-client/help-center/converter-to-models.js.map +1 -0
- package/dist/api-client/help-center/index.d.ts +2 -0
- package/dist/api-client/help-center/index.d.ts.map +1 -0
- package/dist/api-client/help-center/index.js +2 -0
- package/dist/api-client/help-center/index.js.map +1 -0
- package/dist/api-client/help-center/native-client.d.ts +1260 -0
- package/dist/api-client/help-center/native-client.d.ts.map +1 -0
- package/dist/api-client/help-center/native-client.js +6169 -0
- package/dist/api-client/help-center/native-client.js.map +1 -0
- package/dist/api-client/index.d.ts +8 -0
- package/dist/api-client/index.d.ts.map +1 -0
- package/dist/api-client/index.js +8 -0
- package/dist/api-client/index.js.map +1 -0
- package/dist/api-client/models/__mocks__/models.mock.d.ts +13 -0
- package/dist/api-client/models/__mocks__/models.mock.d.ts.map +1 -0
- package/dist/api-client/models/__mocks__/models.mock.js +114 -0
- package/dist/api-client/models/__mocks__/models.mock.js.map +1 -0
- package/dist/api-client/models/index.d.ts +22 -0
- package/dist/api-client/models/index.d.ts.map +1 -0
- package/dist/api-client/models/index.js +15 -0
- package/dist/api-client/models/index.js.map +1 -0
- package/dist/api-client/titan-chat/chatbot-api-client.d.ts +34 -0
- package/dist/api-client/titan-chat/chatbot-api-client.d.ts.map +1 -0
- package/dist/api-client/titan-chat/chatbot-api-client.js +72 -0
- package/dist/api-client/titan-chat/chatbot-api-client.js.map +1 -0
- package/dist/api-client/titan-chat/index.d.ts +2 -0
- package/dist/api-client/titan-chat/index.d.ts.map +1 -0
- package/dist/api-client/titan-chat/index.js +2 -0
- package/dist/api-client/titan-chat/index.js.map +1 -0
- package/dist/api-client/titan-chat/native-client.d.ts +225 -0
- package/dist/api-client/titan-chat/native-client.d.ts.map +1 -0
- package/dist/api-client/titan-chat/native-client.js +931 -0
- package/dist/api-client/titan-chat/native-client.js.map +1 -0
- package/dist/api-client/utils/model-utils.d.ts +4 -0
- package/dist/api-client/utils/model-utils.d.ts.map +1 -0
- package/dist/api-client/utils/model-utils.js +58 -0
- package/dist/api-client/utils/model-utils.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/models/chatbot-customizations.d.ts +15 -0
- package/dist/models/chatbot-customizations.d.ts.map +1 -0
- package/dist/models/chatbot-customizations.js +2 -0
- package/dist/models/chatbot-customizations.js.map +1 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +2 -0
- package/dist/models/index.js.map +1 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.test.d.ts +2 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.test.d.ts.map +1 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.test.js +341 -0
- package/dist/stores/__tests__/chatbot-ui-backend.store.test.js.map +1 -0
- package/dist/stores/__tests__/chatbot-ui.store.test.d.ts +2 -0
- package/dist/stores/__tests__/chatbot-ui.store.test.d.ts.map +1 -0
- package/dist/stores/__tests__/chatbot-ui.store.test.js +166 -0
- package/dist/stores/__tests__/chatbot-ui.store.test.js.map +1 -0
- package/dist/stores/__tests__/filter.store.test.d.ts +2 -0
- package/dist/stores/__tests__/filter.store.test.d.ts.map +1 -0
- package/dist/stores/__tests__/filter.store.test.js +316 -0
- package/dist/stores/__tests__/filter.store.test.js.map +1 -0
- package/dist/stores/__tests__/initialize.store.test.d.ts +2 -0
- package/dist/stores/__tests__/initialize.store.test.d.ts.map +1 -0
- package/dist/stores/__tests__/initialize.store.test.js +54 -0
- package/dist/stores/__tests__/initialize.store.test.js.map +1 -0
- package/dist/stores/chatbot-ui-backend.store.d.ts +61 -0
- package/dist/stores/chatbot-ui-backend.store.d.ts.map +1 -0
- package/dist/stores/chatbot-ui-backend.store.js +396 -0
- package/dist/stores/chatbot-ui-backend.store.js.map +1 -0
- package/dist/stores/chatbot-ui.store.d.ts +25 -0
- package/dist/stores/chatbot-ui.store.d.ts.map +1 -0
- package/dist/stores/chatbot-ui.store.js +87 -0
- package/dist/stores/chatbot-ui.store.js.map +1 -0
- package/dist/stores/filter.store.d.ts +30 -0
- package/dist/stores/filter.store.d.ts.map +1 -0
- package/dist/stores/filter.store.js +334 -0
- package/dist/stores/filter.store.js.map +1 -0
- package/dist/stores/index.d.ts +4 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/index.js +4 -0
- package/dist/stores/index.js.map +1 -0
- package/dist/stores/initialize.store.d.ts +17 -0
- package/dist/stores/initialize.store.d.ts.map +1 -0
- package/dist/stores/initialize.store.js +98 -0
- package/dist/stores/initialize.store.js.map +1 -0
- package/dist/utils/__tests__/axios-utils.test.d.ts +2 -0
- package/dist/utils/__tests__/axios-utils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/axios-utils.test.js +33 -0
- package/dist/utils/__tests__/axios-utils.test.js.map +1 -0
- package/dist/utils/axios-utils.d.ts +5 -0
- package/dist/utils/axios-utils.d.ts.map +1 -0
- package/dist/utils/axios-utils.js +23 -0
- package/dist/utils/axios-utils.js.map +1 -0
- package/dist/utils/test-utils.d.ts +5 -0
- package/dist/utils/test-utils.d.ts.map +1 -0
- package/dist/utils/test-utils.js +17 -0
- package/dist/utils/test-utils.js.map +1 -0
- package/package.json +45 -0
- package/src/api-client/__mocks__/chatbot-api-client.mock.ts +11 -0
- package/src/api-client/base/chatbot-api-client.ts +33 -0
- package/src/api-client/help-center/__tests__/converter-from-models.test.ts +41 -0
- package/src/api-client/help-center/__tests__/converter-to-models.test.ts +89 -0
- package/src/api-client/help-center/chatbot-api-client.ts +107 -0
- package/src/api-client/help-center/converter-from-models.ts +132 -0
- package/src/api-client/help-center/converter-to-models.ts +124 -0
- package/src/api-client/help-center/index.ts +1 -0
- package/src/api-client/help-center/native-client.ts +5662 -0
- package/src/api-client/index.ts +12 -0
- package/src/api-client/models/__mocks__/models.mock.ts +141 -0
- package/src/api-client/models/index.ts +48 -0
- package/src/api-client/titan-chat/chatbot-api-client.ts +77 -0
- package/src/api-client/titan-chat/index.ts +1 -0
- package/src/api-client/titan-chat/native-client.ts +826 -0
- package/src/api-client/utils/model-utils.ts +68 -0
- package/src/cypress.d.ts +10 -0
- package/src/index.ts +6 -0
- package/src/models/chatbot-customizations.ts +16 -0
- package/src/models/index.ts +1 -0
- package/src/stores/__tests__/chatbot-ui-backend.store.test.ts +426 -0
- package/src/stores/__tests__/chatbot-ui.store.test.ts +196 -0
- package/src/stores/__tests__/filter.store.test.ts +363 -0
- package/src/stores/__tests__/initialize.store.test.ts +73 -0
- package/src/stores/chatbot-ui-backend.store.ts +401 -0
- package/src/stores/chatbot-ui.store.ts +82 -0
- package/src/stores/filter.store.ts +250 -0
- package/src/stores/index.ts +12 -0
- package/src/stores/initialize.store.ts +62 -0
- package/src/utils/__tests__/axios-utils.test.ts +40 -0
- package/src/utils/axios-utils.ts +25 -0
- package/src/utils/test-utils.ts +22 -0
- package/tsconfig.json +19 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
2
|
+
import { action, makeObservable, observable } from 'mobx';
|
|
3
|
+
import { nanoid } from 'nanoid';
|
|
4
|
+
import { Models, ModelsUtils } from '../api-client';
|
|
5
|
+
|
|
6
|
+
interface IOption extends Models.IOption {
|
|
7
|
+
uid?: string;
|
|
8
|
+
parentKeys?: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class FilterStore {
|
|
12
|
+
@observable selectedOptions: Record<string, string[]> = {};
|
|
13
|
+
@observable filters: IOption[] = [];
|
|
14
|
+
|
|
15
|
+
private model?: Models.IFrontendModel;
|
|
16
|
+
private originalFilters: IOption[] = [];
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
makeObservable(this);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@action
|
|
23
|
+
initFilters(model: Models.IFrontendModel) {
|
|
24
|
+
const setUids = (o?: IOption[]) => {
|
|
25
|
+
o?.forEach(x => {
|
|
26
|
+
x.uid = nanoid();
|
|
27
|
+
setUids(x.subOptions as IOption[]);
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
this.model = model;
|
|
32
|
+
this.originalFilters = cloneDeep(
|
|
33
|
+
model!.options.subOptions?.filter(x => x.type === Models.OptionType.Group) ?? []
|
|
34
|
+
);
|
|
35
|
+
setUids(this.originalFilters);
|
|
36
|
+
this.filters = this.originalFilters;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@action
|
|
40
|
+
selectOption = (filterKey: string, optionKey: string) => {
|
|
41
|
+
const allFilterOptions = this.getExistingFilterOptions(filterKey);
|
|
42
|
+
const option = allFilterOptions.find(o => o.key === optionKey);
|
|
43
|
+
if (!option) {
|
|
44
|
+
throw new Error(`Option "${optionKey}" does not exist in the filter "${filterKey}".`);
|
|
45
|
+
}
|
|
46
|
+
if (!this.selectedOptions[filterKey]) {
|
|
47
|
+
this.selectedOptions[filterKey] = [];
|
|
48
|
+
}
|
|
49
|
+
this.selectedOptions[filterKey] = [...this.selectedOptions[filterKey], option.key];
|
|
50
|
+
this.addSubSequentFilters(filterKey);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
@action
|
|
54
|
+
selectAll = (filterKey: string) => {
|
|
55
|
+
this.selectedOptions[filterKey] = this.getExistingFilterOptions(filterKey).map(o => o.key);
|
|
56
|
+
this.addSubSequentFilters(filterKey);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
@action
|
|
60
|
+
deselectOption = (filterKey: string, optionKey: string) => {
|
|
61
|
+
this.selectedOptions[filterKey] = this.selectedOptions[filterKey].filter(
|
|
62
|
+
o => o !== optionKey
|
|
63
|
+
);
|
|
64
|
+
this.cleanUpFilterOptions(filterKey, optionKey);
|
|
65
|
+
this.removeSubSequentFilters(filterKey);
|
|
66
|
+
this.addSubSequentFilters(filterKey);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
@action
|
|
70
|
+
deselectAll = (filterKey: string) => {
|
|
71
|
+
for (const selectedKey of this.selectedOptions[filterKey]) {
|
|
72
|
+
this.deselectOption(filterKey, selectedKey);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
getFilterLabel = (key: string) => {
|
|
77
|
+
// transform camel case key to human-readable label
|
|
78
|
+
return key.replace(/([A-Z])/g, ' $1').replace(/^./, function (str) {
|
|
79
|
+
return str.toUpperCase();
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export(): Models.Selections | undefined {
|
|
84
|
+
return ModelsUtils.createSelectionsModel(this.originalFilters, this.selectedOptions);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@action
|
|
88
|
+
private removeSubSequentFilters = (filterKey: string) => {
|
|
89
|
+
const filter = this.getExistingFilterByKey(filterKey);
|
|
90
|
+
const subFilters = this.getSubFilters(filter);
|
|
91
|
+
this.filters = this.filters.filter(x => !subFilters.some(f => f.key === x.key));
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
@action
|
|
95
|
+
private addSubSequentFilters = (filterKey: string) => {
|
|
96
|
+
const filter = this.getExistingFilterByKey(filterKey);
|
|
97
|
+
if (!filter.subOptions) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Get selected items for the filter
|
|
102
|
+
const filterSelectedKeys = this.selectedOptions[filterKey] ?? [];
|
|
103
|
+
const filterSelectedOptions: IOption[] = filter.subOptions.filter(
|
|
104
|
+
x => x.type === Models.OptionType.Selectable && filterSelectedKeys.includes(x.key!)
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// Find all sub-filters for the selected options
|
|
108
|
+
const allSubFilters: IOption[] =
|
|
109
|
+
filterSelectedOptions.flatMap<IOption>(selectedOption => {
|
|
110
|
+
// For each selected option, collect its sub-filters
|
|
111
|
+
const subFilters: IOption[] = [];
|
|
112
|
+
for (const subFilter of selectedOption.subOptions ?? []) {
|
|
113
|
+
const originalFilter = cloneDeep(
|
|
114
|
+
this.getOriginalFilter((subFilter as IOption).uid)
|
|
115
|
+
);
|
|
116
|
+
originalFilter.parentKeys = [selectedOption.uid!];
|
|
117
|
+
subFilters.push(originalFilter);
|
|
118
|
+
}
|
|
119
|
+
return subFilters;
|
|
120
|
+
}) ?? [];
|
|
121
|
+
|
|
122
|
+
// Merge sub-filters with the same key and their options (with parent keys merging)
|
|
123
|
+
const mergedSubFilters = allSubFilters.reduce<IOption[]>((acc, subFilter) => {
|
|
124
|
+
if (!subFilter.subOptions) {
|
|
125
|
+
return acc;
|
|
126
|
+
}
|
|
127
|
+
const subfilterOptions = (subFilter.subOptions ?? []) as IOption[];
|
|
128
|
+
const subfilterParentKeys = subFilter.parentKeys;
|
|
129
|
+
|
|
130
|
+
// Merge filters with the same key and their options if they already exist
|
|
131
|
+
subfilterOptions.forEach(subfilterOption => {
|
|
132
|
+
subfilterOption.parentKeys = this.mergeParentKeys(
|
|
133
|
+
subfilterOption.parentKeys,
|
|
134
|
+
subfilterParentKeys
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Check if the filter already exists in the accumulator
|
|
139
|
+
const existingFilter = acc.find(x => x.key === subFilter.key);
|
|
140
|
+
if (!existingFilter) {
|
|
141
|
+
return [...acc, subFilter];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Merge options for subfilters by key
|
|
145
|
+
existingFilter.subOptions = subfilterOptions.reduce(
|
|
146
|
+
(acc2, subfilterOption) => {
|
|
147
|
+
const existing = acc2.find(o => o.key === subfilterOption.key) as
|
|
148
|
+
| IOption
|
|
149
|
+
| undefined;
|
|
150
|
+
if (!existing) {
|
|
151
|
+
return [...acc2, subfilterOption];
|
|
152
|
+
}
|
|
153
|
+
existing.parentKeys = this.mergeParentKeys(
|
|
154
|
+
existing.parentKeys,
|
|
155
|
+
subfilterOption.parentKeys
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
return acc2;
|
|
159
|
+
},
|
|
160
|
+
[...(existingFilter.subOptions ?? [])]
|
|
161
|
+
) as Models.Option[];
|
|
162
|
+
return acc;
|
|
163
|
+
}, []);
|
|
164
|
+
|
|
165
|
+
// Update the filters with the merged sub-filters
|
|
166
|
+
for (const newFilter of mergedSubFilters) {
|
|
167
|
+
const existingFilterIdx = this.filters.findIndex(f => f.key === newFilter.key);
|
|
168
|
+
if (existingFilterIdx < 0) {
|
|
169
|
+
this.filters.push(newFilter);
|
|
170
|
+
} else {
|
|
171
|
+
this.filters[existingFilterIdx] = newFilter;
|
|
172
|
+
}
|
|
173
|
+
this.addSubSequentFilters(newFilter.key);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
private mergeParentKeys = (keys1?: string[], keys2?: string[]): string[] => {
|
|
178
|
+
if (!keys1 && !keys2) {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
if (!keys1) {
|
|
182
|
+
return keys2 ?? [];
|
|
183
|
+
}
|
|
184
|
+
if (!keys2) {
|
|
185
|
+
return keys1;
|
|
186
|
+
}
|
|
187
|
+
const mergedKeys = new Set<string>([...keys1, ...keys2]);
|
|
188
|
+
return Array.from(mergedKeys);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
private cleanUpFilterOptions = (filterKey: string, parentKeyToRemove: string) => {
|
|
192
|
+
const filter = this.getExistingFilterByKey(filterKey);
|
|
193
|
+
const filterSubOptionToRemove = filter.subOptions?.find(x => x.key === parentKeyToRemove);
|
|
194
|
+
const filterSubOptionToRemoveUid = (filterSubOptionToRemove as IOption)?.uid;
|
|
195
|
+
const filterIdx = this.filters.findIndex(f => f.key === filterKey);
|
|
196
|
+
for (let i = filterIdx + 1; i < this.filters.length; i++) {
|
|
197
|
+
const subFilter = this.filters[i];
|
|
198
|
+
subFilter.subOptions?.forEach((o: IOption) => {
|
|
199
|
+
o.parentKeys = (o.parentKeys ?? []).filter(p => p !== filterSubOptionToRemoveUid);
|
|
200
|
+
// Uncheck sub-filter options if they no longer have parent keys
|
|
201
|
+
if (!o.parentKeys?.length) {
|
|
202
|
+
this.selectedOptions[subFilter.key] = this.selectedOptions[
|
|
203
|
+
subFilter.key
|
|
204
|
+
]?.filter(x => x !== o.key);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
subFilter.subOptions = subFilter.subOptions?.filter((o: IOption) =>
|
|
208
|
+
Boolean(o.parentKeys?.length)
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
private getSubFilters = (filter: IOption): IOption[] => {
|
|
214
|
+
const result: IOption[] = [];
|
|
215
|
+
filter.subOptions?.forEach(filterSelectable => {
|
|
216
|
+
filterSelectable.subOptions?.forEach(subFilter => {
|
|
217
|
+
result.push(subFilter, ...this.getSubFilters(subFilter));
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
return result;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
private getOriginalFilters = (): IOption[] => {
|
|
224
|
+
return this.originalFilters.reduce<IOption[]>((acc, next) => {
|
|
225
|
+
return [...acc, next, ...this.getSubFilters(next)];
|
|
226
|
+
}, []);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
private getOriginalFilter = (uid?: string): IOption => {
|
|
230
|
+
const originalFilters = this.getOriginalFilters();
|
|
231
|
+
const filter = originalFilters.find(f => f.uid === uid);
|
|
232
|
+
if (!filter) {
|
|
233
|
+
throw new Error(`Filter with key "${uid}" does not exist.`);
|
|
234
|
+
}
|
|
235
|
+
return filter;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
private getExistingFilterOptions = (filterKey: string): IOption[] => {
|
|
239
|
+
const filter = this.getExistingFilterByKey(filterKey);
|
|
240
|
+
return filter.subOptions?.filter(x => x.type === Models.OptionType.Selectable) ?? [];
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
private getExistingFilterByKey = (filterKey: string): IOption => {
|
|
244
|
+
const filter = this.filters.find(f => f.key === filterKey);
|
|
245
|
+
if (!filter) {
|
|
246
|
+
throw new Error(`Filter with key "${filterKey}" does not exist.`);
|
|
247
|
+
}
|
|
248
|
+
return filter;
|
|
249
|
+
};
|
|
250
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export {
|
|
2
|
+
CHATBOT_UI_BACKEND_STORE_TOKEN,
|
|
3
|
+
IChatbotUiBackendStore,
|
|
4
|
+
ChatbotUiBackendStore,
|
|
5
|
+
} from './chatbot-ui-backend.store';
|
|
6
|
+
export {
|
|
7
|
+
CHATBOT_UI_STORE_TOKEN,
|
|
8
|
+
IChatbotUiStore,
|
|
9
|
+
ChatbotUiStore,
|
|
10
|
+
ChatbotUiEvent,
|
|
11
|
+
} from './chatbot-ui.store';
|
|
12
|
+
export { InitializeStore, InitializeStoreStatus } from './initialize.store';
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { injectable } from '@servicetitan/react-ioc';
|
|
2
|
+
import { action, computed, makeObservable, observable } from 'mobx';
|
|
3
|
+
|
|
4
|
+
export enum InitializeStoreStatus {
|
|
5
|
+
None = 'None',
|
|
6
|
+
Pending = 'Pending',
|
|
7
|
+
Success = 'Success',
|
|
8
|
+
Error = 'Error',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@injectable()
|
|
12
|
+
export class InitializeStore {
|
|
13
|
+
@observable
|
|
14
|
+
initializeStatus = InitializeStoreStatus.None;
|
|
15
|
+
|
|
16
|
+
@observable.ref
|
|
17
|
+
private initializePromiseInner?: Promise<void>;
|
|
18
|
+
|
|
19
|
+
@computed
|
|
20
|
+
get isInitialized() {
|
|
21
|
+
return this.initializeStatus === InitializeStoreStatus.Success;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
constructor() {
|
|
25
|
+
makeObservable(this);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async initialize(force = false): Promise<void> {
|
|
29
|
+
if (force) {
|
|
30
|
+
this.resetInitialization();
|
|
31
|
+
}
|
|
32
|
+
if (this.initializePromiseInner) {
|
|
33
|
+
return this.initializePromiseInner;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
this.setInitializeStatus(InitializeStoreStatus.Pending);
|
|
38
|
+
this.initializePromiseInner = this.initializeInternal();
|
|
39
|
+
await this.initializePromiseInner;
|
|
40
|
+
this.setInitializeStatus(InitializeStoreStatus.Success);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
this.setInitializeStatus(InitializeStoreStatus.Error);
|
|
43
|
+
throw e;
|
|
44
|
+
}
|
|
45
|
+
return this.initializePromiseInner;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected initializeInternal(): Promise<void> {
|
|
49
|
+
return Promise.resolve();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@action
|
|
53
|
+
private setInitializeStatus(initializeStatus: InitializeStoreStatus) {
|
|
54
|
+
this.initializeStatus = initializeStatus;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@action
|
|
58
|
+
private resetInitialization() {
|
|
59
|
+
this.initializeStatus = InitializeStoreStatus.None;
|
|
60
|
+
this.initializePromiseInner = undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { expect } from '@jest/globals';
|
|
2
|
+
import { withTimeout } from '../axios-utils';
|
|
3
|
+
|
|
4
|
+
describe('axios-utils', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
jest.useFakeTimers();
|
|
7
|
+
jest.setSystemTime(new Date('2000-01-01T00:00:00.000Z'));
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
jest.useRealTimers();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('should withTimeout (timeout first)', async () => {
|
|
16
|
+
const abortController = new AbortController();
|
|
17
|
+
const spyAbort = jest.spyOn(abortController, 'abort');
|
|
18
|
+
const action = new Promise(resolve => setTimeout(() => resolve('done'), 100));
|
|
19
|
+
const timeoutMs = 50;
|
|
20
|
+
|
|
21
|
+
const p = expect(withTimeout(action, timeoutMs, abortController)).rejects.toThrow(
|
|
22
|
+
'The request is timed out'
|
|
23
|
+
);
|
|
24
|
+
await jest.advanceTimersByTimeAsync(50);
|
|
25
|
+
await p;
|
|
26
|
+
expect(spyAbort).toHaveBeenCalledWith('The request is timed out');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('should withTimeout (resolves first)', async () => {
|
|
30
|
+
const abortController = new AbortController();
|
|
31
|
+
const spyAbort = jest.spyOn(abortController, 'abort');
|
|
32
|
+
const action = new Promise(resolve => setTimeout(() => resolve('done'), 100));
|
|
33
|
+
const timeoutMs = 150;
|
|
34
|
+
|
|
35
|
+
const p = expect(withTimeout(action, timeoutMs, abortController)).resolves.toEqual('done');
|
|
36
|
+
await jest.advanceTimersByTimeAsync(100);
|
|
37
|
+
await p;
|
|
38
|
+
expect(spyAbort).not.toHaveBeenCalled();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aborts the action if it takes longer than the specified timeout.
|
|
3
|
+
*/
|
|
4
|
+
export async function withTimeout<T>(
|
|
5
|
+
action: Promise<T>,
|
|
6
|
+
timeoutMs: number,
|
|
7
|
+
abortController: AbortController
|
|
8
|
+
): Promise<T> {
|
|
9
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined = undefined;
|
|
10
|
+
const timeoutPromise = new Promise<T>((_, reject) => {
|
|
11
|
+
timeoutId = setTimeout(() => {
|
|
12
|
+
abortController.abort('The request is timed out');
|
|
13
|
+
reject(new Error('The request is timed out'));
|
|
14
|
+
}, timeoutMs);
|
|
15
|
+
});
|
|
16
|
+
const promise = Promise.race([action, timeoutPromise]);
|
|
17
|
+
try {
|
|
18
|
+
return await promise;
|
|
19
|
+
} finally {
|
|
20
|
+
if (timeoutId) {
|
|
21
|
+
clearTimeout(timeoutId);
|
|
22
|
+
timeoutId = undefined;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Container } from '@servicetitan/react-ioc';
|
|
2
|
+
|
|
3
|
+
type Newable<T> = new (...args: never[]) => T;
|
|
4
|
+
|
|
5
|
+
export const initTestContainer = (
|
|
6
|
+
serviceIdentifier: Newable<unknown> | Newable<unknown>[],
|
|
7
|
+
initDependenciesFn: (container: Container) => void
|
|
8
|
+
) => {
|
|
9
|
+
const rootContainer = new Container();
|
|
10
|
+
if (Array.isArray(serviceIdentifier)) {
|
|
11
|
+
serviceIdentifier.forEach(identifier => rootContainer.bind(identifier).toSelf());
|
|
12
|
+
} else {
|
|
13
|
+
rootContainer.bind(serviceIdentifier).toSelf();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return () => {
|
|
17
|
+
const container = new Container();
|
|
18
|
+
container.parent = rootContainer;
|
|
19
|
+
initDependenciesFn(container);
|
|
20
|
+
return container;
|
|
21
|
+
};
|
|
22
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "@servicetitan/startup/tsconfig/base",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"composite": true,
|
|
5
|
+
"outDir": "dist",
|
|
6
|
+
"rootDir": "src",
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"moduleResolution": "bundler"
|
|
9
|
+
},
|
|
10
|
+
"include": ["src/**/*"],
|
|
11
|
+
"exclude": [
|
|
12
|
+
"node_modules"
|
|
13
|
+
],
|
|
14
|
+
"references": [
|
|
15
|
+
{
|
|
16
|
+
"path": "../titan-chat-ui-common"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|