4
4
* @author Box
5
5
*/
6
6
7
+ import { TreeQueryInput } from '@box/combobox-with-api' ;
8
+ import cloneDeep from 'lodash/cloneDeep' ;
9
+ import lodashFilter from 'lodash/filter' ;
10
+ import flatMap from 'lodash/flatMap' ;
7
11
import getProp from 'lodash/get' ;
8
- import uniqueId from 'lodash/uniqueId' ;
9
12
import isEmpty from 'lodash/isEmpty' ;
13
+ import keyBy from 'lodash/keyBy' ;
14
+ import lodashMap from 'lodash/map' ;
15
+ import uniq from 'lodash/uniq' ;
16
+ import uniqueId from 'lodash/uniqueId' ;
10
17
import { getBadItemError , getBadPermissionsError , isUserCorrectableError } from '../utils/error' ;
11
18
import { getTypedFileId } from '../utils/file' ;
12
19
import { handleOnAbort , formatMetadataFieldValue } from './utils' ;
@@ -180,6 +187,110 @@ class Metadata extends File {
180
187
} ;
181
188
}
182
189
190
+ /**
191
+ * API URL for taxonomies levels for templates
192
+ *
193
+ * @param {string } taxonomyPath - taxonomy path
194
+ * @return {string } base url for files
195
+ */
196
+ getTaxonomyLevelsForTemplatesUrl ( taxonomyPath : string ) : string {
197
+ return `${this . getBaseApiUrl ( ) } / ${taxonomyPath } `;
198
+ }
199
+
200
+ /**
201
+ * Returns taxonomy path for API calls and level mapping
202
+ *
203
+ * @param {string} namespace
204
+ * @param {string} taxonomyKey
205
+ * @returns {string}
206
+ */
207
+ getTaxonomyPath(namespace?: string, taxonomyKey?: string): string | null {
208
+ if (!namespace || !taxonomyKey) {
209
+ return null;
210
+ }
211
+ return ` metadata_taxonomies / ${namespace } / ${taxonomyKey } `;
212
+ }
213
+
214
+ /**
215
+ *
216
+ * @param {Array<MetadataTemplate>} metadataTemplates
217
+ * @param {string} id
218
+ * @returns {Array<MetadataTemplate>}
219
+ */
220
+ async getTaxonomyLevelsForTemplates(
221
+ metadataTemplates: Array<MetadataTemplate>,
222
+ id: string,
223
+ ): Promise<Array<MetadataTemplate>> {
224
+ const templates = cloneDeep(metadataTemplates);
225
+
226
+ const taxonomyFields = flatMap(templates, template =>
227
+ lodashFilter(
228
+ template.fields,
229
+ field => field.type === 'taxonomy' && !field.levels && (field.taxonomyKey || field.taxonomy_key),
230
+ ),
231
+ );
232
+
233
+ if (isEmpty(taxonomyFields)) {
234
+ return templates;
235
+ }
236
+
237
+ const taxonomyPaths = uniq(
238
+ taxonomyFields
239
+ .map(field => this.getTaxonomyPath(field.namespace, field.taxonomyKey || field.taxonomy_key))
240
+ .filter(Boolean),
241
+ );
242
+
243
+ const fetchPromises = taxonomyPaths.map(async taxonomyPath => {
244
+ try {
245
+ const result = await this.xhr.get({
246
+ url: this.getTaxonomyLevelsForTemplatesUrl(taxonomyPath),
247
+ id: getTypedFileId(id),
248
+ });
249
+ return {
250
+ path: taxonomyPath,
251
+ levels: result.data.levels || [],
252
+ };
253
+ } catch (error) {
254
+ throw new Error(` Failed to fetch taxonomy for path : ${taxonomyPath } `);
255
+ }
256
+ });
257
+
258
+ const fetchResults = await Promise.all(fetchPromises);
259
+
260
+ const taxonomyInfo = keyBy(fetchResults, 'path');
261
+
262
+ return lodashMap(templates, template => {
263
+ if (!template.fields) return template;
264
+
265
+ const fieldsToUpdate = lodashFilter(template.fields, field => field.type === 'taxonomy' && !field.levels);
266
+
267
+ if (isEmpty(fieldsToUpdate)) return template;
268
+
269
+ const updatedFields = lodashMap(fieldsToUpdate, field => {
270
+ const taxonomyPath = this.getTaxonomyPath(field.namespace, field.taxonomyKey || field.taxonomy_key);
271
+ const levels = taxonomyInfo[taxonomyPath]?.levels || [];
272
+
273
+ const taxonomyKey = field.taxonomyKey || field.taxonomy_key;
274
+
275
+ delete field.taxonomy_key;
276
+
277
+ return {
278
+ ...field,
279
+ levels: lodashMap(levels, ({ displayName, display_name, ...rest }) => ({
280
+ ...rest,
281
+ displayName: displayName || display_name,
282
+ })),
283
+ taxonomyKey,
284
+ };
285
+ });
286
+
287
+ return {
288
+ ...template,
289
+ fields: updatedFields,
290
+ };
291
+ });
292
+ }
293
+
183
294
/**
184
295
* Gets metadata templates for enterprise
185
296
*
@@ -210,7 +321,10 @@ class Metadata extends File {
210
321
}
211
322
}
212
323
213
- return getProp ( templates , 'data.entries' , [ ] ) ;
324
+ templates = getProp(templates, 'data.entries', []);
325
+ const templatesWithTaxonomies = await this.getTaxonomyLevelsForTemplates(templates, id);
326
+
327
+ return templatesWithTaxonomies;
214
328
}
215
329
216
330
/**
@@ -1105,7 +1219,7 @@ class Metadata extends File {
1105
1219
templateKey: string,
1106
1220
fieldKey: string,
1107
1221
level: number,
1108
- options: { marker?: string, searchInput?: string, signal?: AbortSignal } ,
1222
+ options: TreeQueryInput ,
1109
1223
) {
1110
1224
this.errorCode = ERROR_CODE_FETCH_METADATA_OPTIONS;
1111
1225
@@ -1130,14 +1244,26 @@ class Metadata extends File {
1130
1244
throw new Error('Missing level');
1131
1245
}
1132
1246
1133
- const url = this.getMetadataOptionsUrl(scope, templateKey, fieldKey);
1134
- const { marker, searchInput: query_text, signal } = options;
1135
- const params = {
1247
+ const {
1248
+ marker,
1249
+ searchInput: query_text,
1250
+ onlySelectableOptions,
1251
+ ancestorId: ancestor_id,
1252
+ level: optionsLevel,
1253
+ signal,
1254
+ } = options;
1255
+
1256
+ const params: {} = {
1136
1257
...(marker ? { marker } : {}),
1137
1258
...(query_text ? { query_text } : {}),
1138
- ...(level || level === 0 ? { level } : {}),
1259
+ ...(optionsLevel ? { level: optionsLevel } : {}),
1260
+ ...(ancestor_id ? { ancestor_id } : {}),
1261
+ ...(onlySelectableOptions !== undefined ? { only_selectable_options: !!onlySelectableOptions } : {}),
1262
+ limit: 100,
1139
1263
};
1140
1264
1265
+ const url = this.getMetadataOptionsUrl(scope, templateKey, fieldKey);
1266
+
1141
1267
if (signal) {
1142
1268
signal.onabort = () => handleOnAbort(this.xhr);
1143
1269
}
@@ -1202,7 +1328,12 @@ class Metadata extends File {
1202
1328
* @param {boolean} includeAncestors
1203
1329
* @returns {` $ { string } / metadata_taxonomies / ${string } / ${string } / nodes / ${string } `}
1204
1330
*/
1205
- getMetadataTaxonomyNodeUrl(scope: string, taxonomyKey: string, nodeID: string, includeAncestors?: boolean): string {
1331
+ getMetadataTaxonomyNodeUrl(
1332
+ scope: string,
1333
+ taxonomyKey: string,
1334
+ nodeID: string,
1335
+ includeAncestors?: boolean = false,
1336
+ ): string {
1206
1337
const includeAncestorsParam = includeAncestors ? '?include-ancestors=true' : '';
1207
1338
1208
1339
return ` $ { this . getBaseApiUrl ( ) } / metadata_taxonomies / $ { scope} / $ { taxonomyKey} / nodes / $ { nodeID} $ { includeAncestorsParam } `;
0 commit comments