From 2c7161eab4bc2d3f94d02f6683a67ef8ba4063dc Mon Sep 17 00:00:00 2001 From: anoopt Date: Thu, 3 Sep 2020 01:11:14 +0000 Subject: [PATCH] Error handling and logging improvments --- .../README.md | 3 +- .../package-lock.json | 6 +- .../package.json | 2 +- .../CascadingManagedMetadataWebPart.ts | 21 ++- .../components/CascadingManagedMetadata.tsx | 129 +++++++++++++----- .../services/MMDService.ts | 60 ++++---- 6 files changed, 155 insertions(+), 66 deletions(-) diff --git a/samples/react-graph-cascading-managed-metadata/README.md b/samples/react-graph-cascading-managed-metadata/README.md index b2d1ef2f2..0748ffcda 100644 --- a/samples/react-graph-cascading-managed-metadata/README.md +++ b/samples/react-graph-cascading-managed-metadata/README.md @@ -32,13 +32,14 @@ This web part shows how to use the Microsoft Graph APIs (beta) for Taxonomy to g Solution|Author(s) --------|--------- -react-graph-cascading-managed-metadata| Anoop Tatti ([@anooptells](https://twitter.com/anooptells)) +react-graph-cascading-managed-metadata| Anoop Tatti ([anoopt](https://github.com/anoopt), [@anooptells](https://twitter.com/anooptells)) ## Version history Version|Date|Comments -------|----|-------- 1.0.0|Aug 24, 2020|Initial release +1.0.1|Sep 03, 2020|Error handling and logging improvments ## Disclaimer diff --git a/samples/react-graph-cascading-managed-metadata/package-lock.json b/samples/react-graph-cascading-managed-metadata/package-lock.json index 4012bb010..789a3a0ae 100644 --- a/samples/react-graph-cascading-managed-metadata/package-lock.json +++ b/samples/react-graph-cascading-managed-metadata/package-lock.json @@ -3215,9 +3215,9 @@ } }, "@pnp/spfx-controls-react": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@pnp/spfx-controls-react/-/spfx-controls-react-1.19.0.tgz", - "integrity": "sha512-W3PS6I8NsdbOZjE9I9djloYutQW17QHd4nT7jFwPyJFoxnt1MDfWyN6nrPhaeGnnPde3t3TlUbWP4HKLXChFiw==", + "version": "1.20.0-beta.52e4e9f", + "resolved": "https://registry.npmjs.org/@pnp/spfx-controls-react/-/spfx-controls-react-1.20.0-beta.52e4e9f.tgz", + "integrity": "sha512-2lh6EI+0M6H/cgQyDq8av2/LvG/0hCgZG0/XfjWUIFGHcdqpbmcifJbVUeEX46nVt5gHDVeI3vcpqhbaAA5GrA==", "requires": { "@pnp/common": "1.0.1", "@pnp/logging": "1.0.1", diff --git a/samples/react-graph-cascading-managed-metadata/package.json b/samples/react-graph-cascading-managed-metadata/package.json index 3b872e138..2739cd3c3 100644 --- a/samples/react-graph-cascading-managed-metadata/package.json +++ b/samples/react-graph-cascading-managed-metadata/package.json @@ -18,7 +18,7 @@ "@microsoft/sp-office-ui-fabric-core": "1.11.0", "@microsoft/sp-property-pane": "1.11.0", "@microsoft/sp-webpart-base": "1.11.0", - "@pnp/spfx-controls-react": "1.19.0", + "@pnp/spfx-controls-react": "^1.20.0-beta.52e4e9f", "office-ui-fabric-react": "6.214.0", "react": "16.8.5", "react-dom": "16.8.5" diff --git a/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/CascadingManagedMetadataWebPart.ts b/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/CascadingManagedMetadataWebPart.ts index a04ce583a..9b4fa042c 100644 --- a/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/CascadingManagedMetadataWebPart.ts +++ b/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/CascadingManagedMetadataWebPart.ts @@ -1,6 +1,6 @@ import * as React from 'react'; import * as ReactDom from 'react-dom'; -import { Version, DisplayMode } from '@microsoft/sp-core-library'; +import { Version, DisplayMode, Guid } from '@microsoft/sp-core-library'; import { BaseClientSideWebPart, IPropertyPaneConfiguration, @@ -22,8 +22,7 @@ export default class CascadingManagedMetadataWebPart extends BaseClientSideWebPa public async render(): Promise { await MSGraph.Init(this.context); let renderElement = null; - //TODO: Use function to check if GUID? - if (this.properties.termSetId && this.properties.termSetId.length == 36) { + if (this.properties.termSetId) { renderElement = React.createElement( CascadingManagedMetadata, { @@ -69,6 +68,19 @@ export default class CascadingManagedMetadataWebPart extends BaseClientSideWebPa return Version.parse('1.0'); } + private validateTermSetId(value: string): string { + if (value === null || + value.trim().length === 0) { + return 'Provide a term set Id.'; + } + + if (!Guid.isValid(value.trim())) { + return 'Term set Id must be a valid GUID.'; + } + + return ''; + } + protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { return { pages: [ @@ -81,7 +93,8 @@ export default class CascadingManagedMetadataWebPart extends BaseClientSideWebPa groupName: strings.BasicGroupName, groupFields: [ PropertyPaneTextField('termSetId', { - label: strings.TermSetIdFieldLabel + label: strings.TermSetIdFieldLabel, + onGetErrorMessage: this.validateTermSetId.bind(this) }) ] } diff --git a/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/components/CascadingManagedMetadata.tsx b/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/components/CascadingManagedMetadata.tsx index 24477a3b0..2035299fc 100644 --- a/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/components/CascadingManagedMetadata.tsx +++ b/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/components/CascadingManagedMetadata.tsx @@ -16,31 +16,74 @@ const CascadingManagedMetadata: React.SFC = (pro const [citiesList, setCitiesList] = React.useState([]); const [selectedCityCoordinates, setSelectedCityCoordinates] = React.useState(null); const [selectedCity, setSelectedCity] = React.useState(null); + const [showMap, setShowMap] = React.useState(false); const [coordinates, setCoordinates] = React.useState({ latitude: null, longitude: null }); + const [messageBarStatus, setMessageBarStatus] = React.useState({ + type: MessageBarType.info, + message: , + show: false + }); + + const LOG_SOURCE: string = "Cascading MMD -"; React.useEffect(() => { _getCountries().then(countries => { - const options: IDropdownOption[] = countries.value.map(c => ({ key: c.id, text: c.labels[0].name })); - setCountriesList(options); + if (countries) { + const options: IDropdownOption[] = countries.value.map(c => ({ key: c.id, text: c.labels[0].name })); + setCountriesList(options); + } else { + setCountriesList([]); + clearData(); + } }); - }, []); + }, [props.termSetId]); //* Run this also when the property termSetId changes //* Get the country terms i.e. level 1 children using Graph const _getCountries = async (): Promise => { - let countries: ITerms = await MSGraph.Get(`/termStore/sets/${props.termSetId}/children`, "beta"); - return (countries); + try { + let countries: ITerms = await MSGraph.Get(`/termStore/sets/${props.termSetId}/children`, "beta"); + setMessageBarStatus(state => ({ ...state, show: false })); + console.debug("%s Retrieved countries. %o", LOG_SOURCE, countries); + return countries; + } + catch (error) { + console.error("%s Error retrieving countries. Details - %o", LOG_SOURCE, error); + setMessageBarStatus({ + type: MessageBarType.error, + message: Error in retrieving countries. Please contact admin., + show: true + }); + return null; + } }; //* Get the city terms under a country i.e. level 2 children using Graph const _onCountryChange = (event: React.FormEvent, item: IDropdownOption): void => { - setCoordinates({ latitude: null, longitude: null }); - setSelectedCityCoordinates(null); - setSelectedCity(null); + clearData(); let countryTermId: string = item.key.toString(); - MMDService.GetTermsAsDropdownOptions(`/termStore/sets/${props.termSetId}/terms/${countryTermId}/children`, countryTermId, true).then(options => { - setCitiesList(options); - }); + MMDService.GetTermsAsDropdownOptions(`/termStore/sets/${props.termSetId}/terms/${countryTermId}/children`, countryTermId, true) + .then(options => { + setCitiesList(options); + //setShowMap(false); + console.debug("%s Retrieved cities. %o", LOG_SOURCE, options); + setMessageBarStatus({ + type: MessageBarType.warning, + message: options.length > 0 ? + To see the map, please select a city. : + No city terms in the selected country., + show: true + }); + }) + .catch(error => { + console.error("%s Error retrieving cities. Details - %o", LOG_SOURCE, error); + setMessageBarStatus({ + type: MessageBarType.error, + message: Error in retrieving cities. Please contact admin., + show: true + }); + setCitiesList([]); + }); }; @@ -56,41 +99,65 @@ const CascadingManagedMetadata: React.SFC = (pro longitude: isNaN(Number(long)) ? null : Number(long) }; setCoordinates(coordinates); + + console.debug("%s Retrieved coordinates. %o", LOG_SOURCE, coordinates); + + if (coordinates.latitude && coordinates.longitude) { + setShowMap(true); + setMessageBarStatus(state => ({ ...state, show: false })); + } else { + setShowMap(false); + setMessageBarStatus({ + type: MessageBarType.error, + message: To see the map, please check if the coordinates have been configured correctly., + show: true + }); + } }; + //* Clear the data related to cities and maps + const clearData = () => { + setCoordinates({ latitude: null, longitude: null }); + setSelectedCityCoordinates(null); + setSelectedCity(null); + setCitiesList([]); + setShowMap(false); + } + return (
+ defaultSelectedKey="" + onChange={_onCountryChange} + disabled={!(countriesList.length > 0)} /> + onChange={_onCityChange} + disabled={!(citiesList.length > 0)} /> + { - coordinates.latitude && coordinates.longitude ? - ( - - - - ) : - ( -
- - {selectedCity ? "To see the map, please check if the coordinates have been configured correctly." - : "To see the map, please select a city."} - -
- ) + showMap && + + } + + { + messageBarStatus.show && +
+ + {messageBarStatus.message} + +
}
); diff --git a/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/services/MMDService.ts b/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/services/MMDService.ts index 3f0d77fa0..7c2c188e2 100644 --- a/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/services/MMDService.ts +++ b/samples/react-graph-cascading-managed-metadata/src/webparts/cascadingManagedMetadata/services/MMDService.ts @@ -5,9 +5,10 @@ import { IDropdownOption } from "office-ui-fabric-react/lib/Dropdown"; export class MMDService { private static _sessionStorageKey: string = "CMMD_Options"; + private static _logSource: string = "Cascading MMD Service -"; public static async GetTermsAsDropdownOptions(apiUrl: string, parent: string, tryFromCache: boolean): Promise { - + let options: IDropdownOption[] = []; if (tryFromCache) { let optionsFromCache: IOption[] = this._fetchFromSessionStorge(); @@ -16,35 +17,42 @@ export class MMDService { if (requiredOptionsFromCache.length) { options = requiredOptionsFromCache.map(r => ({ key: r.key, text: r.text })); return options; - } - } - } + } + } + } //Get data using Graph return await this._getTermsAsDropdownOptionsUsingGraph(apiUrl, parent); } private static async _getTermsAsDropdownOptionsUsingGraph(apiUrl: string, parent: string): Promise { - let terms: ITerms = await MSGraph.Get(apiUrl, "beta"); - if (terms.value) { - //* Set key as description of the term - //* Description will be of the format latitude;longitude - //* This will be used to render maps - const options: IDropdownOption[] = terms.value.map(t => ({ - key: t.descriptions[0] ? t.descriptions[0].description : t.id, - text: t.labels[0].name })); - let optionsToStoreInCache: IOption[] = options.map(o => ({ - key: o.key.toString(), - text: o.text, - parent - })); - let optionsFromCache: IOption[] = this._fetchFromSessionStorge(); - optionsToStoreInCache = [...optionsFromCache, ...optionsToStoreInCache]; - window.sessionStorage.setItem(this._sessionStorageKey, JSON.stringify(optionsToStoreInCache)); - console.log("Options added in cache"); - return options; - } else { + try { + let terms: ITerms = await MSGraph.Get(apiUrl, "beta"); + if (terms.value) { + //* Set key as description of the term + //* Description will be of the format latitude;longitude + //* This will be used to render maps + const options: IDropdownOption[] = terms.value.map(t => ({ + key: t.descriptions[0] ? t.descriptions[0].description : t.id, + text: t.labels[0].name + })); + let optionsToStoreInCache: IOption[] = options.map(o => ({ + key: o.key.toString(), + text: o.text, + parent + })); + let optionsFromCache: IOption[] = this._fetchFromSessionStorge(); + optionsToStoreInCache = [...optionsFromCache, ...optionsToStoreInCache]; + window.sessionStorage.setItem(this._sessionStorageKey, JSON.stringify(optionsToStoreInCache)); + console.debug("%s Data added in cache.", this._logSource); + return options; + } else { + return []; + } + } catch (error) { + console.error("%s Error getting data from Graph. Details - %o", this._logSource, error); return []; } + } private static _fetchFromSessionStorge(): IOption[] { @@ -53,11 +61,11 @@ export class MMDService { if (stringResult) { try { result = JSON.parse(stringResult); - if(result.length) { - console.log("Fetched options from cache"); + if (result.length) { + console.debug("%s Fetched data from cache", this._logSource); } } catch (error) { - console.error(error); + console.error("%s Error getting data from cache. Details - %o", this._logSource, error); } } return result;