Skip to content

Commit 8a794ad

Browse files
authored
feat(metadata-sidebar): Multilevel taxonomy integration (#4044)
* feat(metadata-sidebar): Add multi level taxonomy * feat(metadata-sidebar): Update integration * feat(metadata-sidebar): Add useCallback * feat(metadata-sidebar): update tests for metadataTaxonomyFetcher * feat(metadata-sidebar): fix metadataTaxonomyNodeAncestorsFetcher * feat(metadata-sidebar): Add tests for Metadata API * feat(metadata-sidebar): Fix flow errors * feat(metadata-sidebar): Accommodate new API response * feat(metadata-sidebar): Update types * feat(metadata-sidebar): visual regression tests * feat(metadata-sidebar): Fix flow errors * feat(metadata-sidebar): Add descriptions * feat(metadata-sidebar): Fix lint * feat(metadata-sidebar): Limit to options request * feat(metadata-sidebar): Update test * feat(metadata-sidebar): Handle old and new API * feat(metadata-sidebar): Use array instead of map * feat(metadata-sidebar): Remove default values * feat(metadata-sidebar): add useCallback * feat(metadata-sidebar): simplify params * feat(metadata-sidebar): Refactor with lodash * feat(metadata-editor): redundant comments * feat(metadata-sidebar): fix types * feat(metadata-sidebar): linting * feat(metadata-sidebar): redundant arg * feat(metadata-sidebar): types * feat(metadata-editor): flow fixes * feat(metadata-editor): update dependencies * feat(metadata-sidebar): Linting
1 parent ca41b1a commit 8a794ad

File tree

10 files changed

+958
-61
lines changed

10 files changed

+958
-61
lines changed

package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@
129129
"@box/cldr-data": "^34.2.0",
130130
"@box/combobox-with-api": "^0.28.4",
131131
"@box/frontend": "^10.0.1",
132-
"@box/item-icon": "^0.9.58",
132+
"@box/item-icon": "^0.9.83",
133133
"@box/languages": "^1.0.0",
134-
"@box/metadata-editor": "^0.96.1",
134+
"@box/metadata-editor": "^0.97.3",
135135
"@box/react-virtualized": "9.22.3-rc-box.9",
136136
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
137137
"@chromatic-com/storybook": "^1.6.1",
@@ -303,8 +303,8 @@
303303
"@box/box-ai-content-answers": "^0.113.0",
304304
"@box/cldr-data": ">=34.2.0",
305305
"@box/combobox-with-api": "^0.28.4",
306-
"@box/item-icon": "^0.9.58",
307-
"@box/metadata-editor": "^0.96.1",
306+
"@box/item-icon": "^0.9.83",
307+
"@box/metadata-editor": "^0.97.3",
308308
"@box/react-virtualized": "9.22.3-rc-box.9",
309309
"@hapi/address": "^2.1.4",
310310
"axios": "^0.30.0",

src/api/Metadata.js

+139-8
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,16 @@
44
* @author Box
55
*/
66

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';
711
import getProp from 'lodash/get';
8-
import uniqueId from 'lodash/uniqueId';
912
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';
1017
import { getBadItemError, getBadPermissionsError, isUserCorrectableError } from '../utils/error';
1118
import { getTypedFileId } from '../utils/file';
1219
import { handleOnAbort, formatMetadataFieldValue } from './utils';
@@ -180,6 +187,110 @@ class Metadata extends File {
180187
};
181188
}
182189

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+
183294
/**
184295
* Gets metadata templates for enterprise
185296
*
@@ -210,7 +321,10 @@ class Metadata extends File {
210321
}
211322
}
212323
213-
return getProp(templates, 'data.entries', []);
324+
templates = getProp(templates, 'data.entries', []);
325+
const templatesWithTaxonomies = await this.getTaxonomyLevelsForTemplates(templates, id);
326+
327+
return templatesWithTaxonomies;
214328
}
215329
216330
/**
@@ -1105,7 +1219,7 @@ class Metadata extends File {
11051219
templateKey: string,
11061220
fieldKey: string,
11071221
level: number,
1108-
options: { marker?: string, searchInput?: string, signal?: AbortSignal },
1222+
options: TreeQueryInput,
11091223
) {
11101224
this.errorCode = ERROR_CODE_FETCH_METADATA_OPTIONS;
11111225
@@ -1130,14 +1244,26 @@ class Metadata extends File {
11301244
throw new Error('Missing level');
11311245
}
11321246
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: {} = {
11361257
...(marker ? { marker } : {}),
11371258
...(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,
11391263
};
11401264
1265+
const url = this.getMetadataOptionsUrl(scope, templateKey, fieldKey);
1266+
11411267
if (signal) {
11421268
signal.onabort = () => handleOnAbort(this.xhr);
11431269
}
@@ -1202,7 +1328,12 @@ class Metadata extends File {
12021328
* @param {boolean} includeAncestors
12031329
* @returns {`${string}/metadata_taxonomies/${string}/${string}/nodes/${string}`}
12041330
*/
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 {
12061337
const includeAncestorsParam = includeAncestors ? '?include-ancestors=true' : '';
12071338
12081339
return `${this.getBaseApiUrl()}/metadata_taxonomies/${scope}/${taxonomyKey}/nodes/${nodeID}${includeAncestorsParam}`;

0 commit comments

Comments
 (0)