Error handling and logging improvments
This commit is contained in:
parent
df87dd9cfc
commit
2c7161eab4
|
@ -32,13 +32,14 @@ This web part shows how to use the Microsoft Graph APIs (beta) for Taxonomy to g
|
||||||
|
|
||||||
Solution|Author(s)
|
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 history
|
||||||
|
|
||||||
Version|Date|Comments
|
Version|Date|Comments
|
||||||
-------|----|--------
|
-------|----|--------
|
||||||
1.0.0|Aug 24, 2020|Initial release
|
1.0.0|Aug 24, 2020|Initial release
|
||||||
|
1.0.1|Sep 03, 2020|Error handling and logging improvments
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
|
|
|
@ -3215,9 +3215,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@pnp/spfx-controls-react": {
|
"@pnp/spfx-controls-react": {
|
||||||
"version": "1.19.0",
|
"version": "1.20.0-beta.52e4e9f",
|
||||||
"resolved": "https://registry.npmjs.org/@pnp/spfx-controls-react/-/spfx-controls-react-1.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pnp/spfx-controls-react/-/spfx-controls-react-1.20.0-beta.52e4e9f.tgz",
|
||||||
"integrity": "sha512-W3PS6I8NsdbOZjE9I9djloYutQW17QHd4nT7jFwPyJFoxnt1MDfWyN6nrPhaeGnnPde3t3TlUbWP4HKLXChFiw==",
|
"integrity": "sha512-2lh6EI+0M6H/cgQyDq8av2/LvG/0hCgZG0/XfjWUIFGHcdqpbmcifJbVUeEX46nVt5gHDVeI3vcpqhbaAA5GrA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@pnp/common": "1.0.1",
|
"@pnp/common": "1.0.1",
|
||||||
"@pnp/logging": "1.0.1",
|
"@pnp/logging": "1.0.1",
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
"@microsoft/sp-office-ui-fabric-core": "1.11.0",
|
"@microsoft/sp-office-ui-fabric-core": "1.11.0",
|
||||||
"@microsoft/sp-property-pane": "1.11.0",
|
"@microsoft/sp-property-pane": "1.11.0",
|
||||||
"@microsoft/sp-webpart-base": "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",
|
"office-ui-fabric-react": "6.214.0",
|
||||||
"react": "16.8.5",
|
"react": "16.8.5",
|
||||||
"react-dom": "16.8.5"
|
"react-dom": "16.8.5"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ReactDom from 'react-dom';
|
import * as ReactDom from 'react-dom';
|
||||||
import { Version, DisplayMode } from '@microsoft/sp-core-library';
|
import { Version, DisplayMode, Guid } from '@microsoft/sp-core-library';
|
||||||
import {
|
import {
|
||||||
BaseClientSideWebPart,
|
BaseClientSideWebPart,
|
||||||
IPropertyPaneConfiguration,
|
IPropertyPaneConfiguration,
|
||||||
|
@ -22,8 +22,7 @@ export default class CascadingManagedMetadataWebPart extends BaseClientSideWebPa
|
||||||
public async render(): Promise<void> {
|
public async render(): Promise<void> {
|
||||||
await MSGraph.Init(this.context);
|
await MSGraph.Init(this.context);
|
||||||
let renderElement = null;
|
let renderElement = null;
|
||||||
//TODO: Use function to check if GUID?
|
if (this.properties.termSetId) {
|
||||||
if (this.properties.termSetId && this.properties.termSetId.length == 36) {
|
|
||||||
renderElement = React.createElement(
|
renderElement = React.createElement(
|
||||||
CascadingManagedMetadata,
|
CascadingManagedMetadata,
|
||||||
{
|
{
|
||||||
|
@ -69,6 +68,19 @@ export default class CascadingManagedMetadataWebPart extends BaseClientSideWebPa
|
||||||
return Version.parse('1.0');
|
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 {
|
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||||
return {
|
return {
|
||||||
pages: [
|
pages: [
|
||||||
|
@ -81,7 +93,8 @@ export default class CascadingManagedMetadataWebPart extends BaseClientSideWebPa
|
||||||
groupName: strings.BasicGroupName,
|
groupName: strings.BasicGroupName,
|
||||||
groupFields: [
|
groupFields: [
|
||||||
PropertyPaneTextField('termSetId', {
|
PropertyPaneTextField('termSetId', {
|
||||||
label: strings.TermSetIdFieldLabel
|
label: strings.TermSetIdFieldLabel,
|
||||||
|
onGetErrorMessage: this.validateTermSetId.bind(this)
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,31 +16,74 @@ const CascadingManagedMetadata: React.SFC<ICascadingManagedMetadataProps> = (pro
|
||||||
const [citiesList, setCitiesList] = React.useState<IDropdownOption[]>([]);
|
const [citiesList, setCitiesList] = React.useState<IDropdownOption[]>([]);
|
||||||
const [selectedCityCoordinates, setSelectedCityCoordinates] = React.useState<string>(null);
|
const [selectedCityCoordinates, setSelectedCityCoordinates] = React.useState<string>(null);
|
||||||
const [selectedCity, setSelectedCity] = React.useState<string>(null);
|
const [selectedCity, setSelectedCity] = React.useState<string>(null);
|
||||||
|
const [showMap, setShowMap] = React.useState<boolean>(false);
|
||||||
const [coordinates, setCoordinates] = React.useState<ICoordinates>({ latitude: null, longitude: null });
|
const [coordinates, setCoordinates] = React.useState<ICoordinates>({ latitude: null, longitude: null });
|
||||||
|
const [messageBarStatus, setMessageBarStatus] = React.useState({
|
||||||
|
type: MessageBarType.info,
|
||||||
|
message: <span></span>,
|
||||||
|
show: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const LOG_SOURCE: string = "Cascading MMD -";
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
_getCountries().then(countries => {
|
_getCountries().then(countries => {
|
||||||
const options: IDropdownOption[] = countries.value.map(c => ({ key: c.id, text: c.labels[0].name }));
|
if (countries) {
|
||||||
setCountriesList(options);
|
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
|
//* Get the country terms i.e. level 1 children using Graph
|
||||||
const _getCountries = async (): Promise<ITerms> => {
|
const _getCountries = async (): Promise<ITerms> => {
|
||||||
let countries: ITerms = await MSGraph.Get(`/termStore/sets/${props.termSetId}/children`, "beta");
|
try {
|
||||||
return (countries);
|
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: <span>Error in retrieving countries. Please contact admin.</span>,
|
||||||
|
show: true
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//* Get the city terms under a country i.e. level 2 children using Graph
|
//* Get the city terms under a country i.e. level 2 children using Graph
|
||||||
const _onCountryChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
|
const _onCountryChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
|
||||||
setCoordinates({ latitude: null, longitude: null });
|
clearData();
|
||||||
setSelectedCityCoordinates(null);
|
|
||||||
setSelectedCity(null);
|
|
||||||
let countryTermId: string = item.key.toString();
|
let countryTermId: string = item.key.toString();
|
||||||
|
|
||||||
MMDService.GetTermsAsDropdownOptions(`/termStore/sets/${props.termSetId}/terms/${countryTermId}/children`, countryTermId, true).then(options => {
|
MMDService.GetTermsAsDropdownOptions(`/termStore/sets/${props.termSetId}/terms/${countryTermId}/children`, countryTermId, true)
|
||||||
setCitiesList(options);
|
.then(options => {
|
||||||
});
|
setCitiesList(options);
|
||||||
|
//setShowMap(false);
|
||||||
|
console.debug("%s Retrieved cities. %o", LOG_SOURCE, options);
|
||||||
|
setMessageBarStatus({
|
||||||
|
type: MessageBarType.warning,
|
||||||
|
message: options.length > 0 ?
|
||||||
|
<span>To see the map, please select a city. </span> :
|
||||||
|
<span> No city terms in the selected country.</span>,
|
||||||
|
show: true
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("%s Error retrieving cities. Details - %o", LOG_SOURCE, error);
|
||||||
|
setMessageBarStatus({
|
||||||
|
type: MessageBarType.error,
|
||||||
|
message: <span>Error in retrieving cities. Please contact admin.</span>,
|
||||||
|
show: true
|
||||||
|
});
|
||||||
|
setCitiesList([]);
|
||||||
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,41 +99,65 @@ const CascadingManagedMetadata: React.SFC<ICascadingManagedMetadataProps> = (pro
|
||||||
longitude: isNaN(Number(long)) ? null : Number(long)
|
longitude: isNaN(Number(long)) ? null : Number(long)
|
||||||
};
|
};
|
||||||
setCoordinates(coordinates);
|
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: <span>To see the map, please check if the coordinates have been configured correctly.</span>,
|
||||||
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
label="Country"
|
label="Country"
|
||||||
placeHolder="Select a country"
|
placeHolder="Select a country"
|
||||||
options={countriesList}
|
options={countriesList}
|
||||||
onChange={_onCountryChange} />
|
defaultSelectedKey=""
|
||||||
|
onChange={_onCountryChange}
|
||||||
|
disabled={!(countriesList.length > 0)} />
|
||||||
|
|
||||||
<Dropdown
|
<Dropdown
|
||||||
label="City"
|
label="City"
|
||||||
selectedKey={selectedCityCoordinates}
|
selectedKey={selectedCityCoordinates}
|
||||||
placeHolder="Select a city"
|
placeHolder="Select a city"
|
||||||
options={citiesList}
|
options={citiesList}
|
||||||
onChange={_onCityChange} />
|
onChange={_onCityChange}
|
||||||
|
disabled={!(citiesList.length > 0)} />
|
||||||
|
|
||||||
{
|
{
|
||||||
coordinates.latitude && coordinates.longitude ?
|
showMap &&
|
||||||
(
|
<Map
|
||||||
<React.Fragment>
|
titleText={`Map of our office in ${selectedCity}`}
|
||||||
<Map
|
coordinates={coordinates}
|
||||||
titleText={`Map of our office in ${selectedCity}`}
|
zoom={15}
|
||||||
coordinates={coordinates}
|
enableSearch={false} />
|
||||||
zoom={15}
|
}
|
||||||
enableSearch={false} />
|
|
||||||
</React.Fragment>
|
{
|
||||||
) :
|
messageBarStatus.show &&
|
||||||
(
|
<div style={{ marginTop: "15px" }}>
|
||||||
<div style={{ marginTop: "15px" }}>
|
<MessageBar messageBarType={messageBarStatus.type}>
|
||||||
<MessageBar messageBarType={MessageBarType.warning}>
|
{messageBarStatus.message}
|
||||||
{selectedCity ? "To see the map, please check if the coordinates have been configured correctly."
|
</MessageBar>
|
||||||
: "To see the map, please select a city."}
|
</div>
|
||||||
</MessageBar>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
|
||||||
export class MMDService {
|
export class MMDService {
|
||||||
|
|
||||||
private static _sessionStorageKey: string = "CMMD_Options";
|
private static _sessionStorageKey: string = "CMMD_Options";
|
||||||
|
private static _logSource: string = "Cascading MMD Service -";
|
||||||
|
|
||||||
public static async GetTermsAsDropdownOptions(apiUrl: string, parent: string, tryFromCache: boolean): Promise<IDropdownOption[]> {
|
public static async GetTermsAsDropdownOptions(apiUrl: string, parent: string, tryFromCache: boolean): Promise<IDropdownOption[]> {
|
||||||
|
|
||||||
|
@ -24,27 +25,34 @@ export class MMDService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async _getTermsAsDropdownOptionsUsingGraph(apiUrl: string, parent: string): Promise<IDropdownOption[]> {
|
private static async _getTermsAsDropdownOptionsUsingGraph(apiUrl: string, parent: string): Promise<IDropdownOption[]> {
|
||||||
let terms: ITerms = await MSGraph.Get(apiUrl, "beta");
|
try {
|
||||||
if (terms.value) {
|
let terms: ITerms = await MSGraph.Get(apiUrl, "beta");
|
||||||
//* Set key as description of the term
|
if (terms.value) {
|
||||||
//* Description will be of the format latitude;longitude
|
//* Set key as description of the term
|
||||||
//* This will be used to render maps
|
//* Description will be of the format latitude;longitude
|
||||||
const options: IDropdownOption[] = terms.value.map(t => ({
|
//* This will be used to render maps
|
||||||
key: t.descriptions[0] ? t.descriptions[0].description : t.id,
|
const options: IDropdownOption[] = terms.value.map(t => ({
|
||||||
text: t.labels[0].name }));
|
key: t.descriptions[0] ? t.descriptions[0].description : t.id,
|
||||||
let optionsToStoreInCache: IOption[] = options.map(o => ({
|
text: t.labels[0].name
|
||||||
key: o.key.toString(),
|
}));
|
||||||
text: o.text,
|
let optionsToStoreInCache: IOption[] = options.map(o => ({
|
||||||
parent
|
key: o.key.toString(),
|
||||||
}));
|
text: o.text,
|
||||||
let optionsFromCache: IOption[] = this._fetchFromSessionStorge();
|
parent
|
||||||
optionsToStoreInCache = [...optionsFromCache, ...optionsToStoreInCache];
|
}));
|
||||||
window.sessionStorage.setItem(this._sessionStorageKey, JSON.stringify(optionsToStoreInCache));
|
let optionsFromCache: IOption[] = this._fetchFromSessionStorge();
|
||||||
console.log("Options added in cache");
|
optionsToStoreInCache = [...optionsFromCache, ...optionsToStoreInCache];
|
||||||
return options;
|
window.sessionStorage.setItem(this._sessionStorageKey, JSON.stringify(optionsToStoreInCache));
|
||||||
} else {
|
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 [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _fetchFromSessionStorge(): IOption[] {
|
private static _fetchFromSessionStorge(): IOption[] {
|
||||||
|
@ -53,11 +61,11 @@ export class MMDService {
|
||||||
if (stringResult) {
|
if (stringResult) {
|
||||||
try {
|
try {
|
||||||
result = JSON.parse(stringResult);
|
result = JSON.parse(stringResult);
|
||||||
if(result.length) {
|
if (result.length) {
|
||||||
console.log("Fetched options from cache");
|
console.debug("%s Fetched data from cache", this._logSource);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error("%s Error getting data from cache. Details - %o", this._logSource, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
Loading…
Reference in New Issue