feat: Update version to 1.0.1 and enhance user profile retrieval in OrganizationChartDataService
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
"name": "organization-chart-webpart",
|
"name": "organization-chart-webpart",
|
||||||
"title": "organization-chart-webpart",
|
"title": "organization-chart-webpart",
|
||||||
"id": "b2f88ea8-f7da-4d0e-a945-6973bf1acd1e",
|
"id": "b2f88ea8-f7da-4d0e-a945-6973bf1acd1e",
|
||||||
"version": "1.0.0.0",
|
"version": "1.0.1.0",
|
||||||
"includeClientSideAssets": true,
|
"includeClientSideAssets": true,
|
||||||
"skipFeatureDeployment": true
|
"skipFeatureDeployment": true
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "organization-chart-webpart",
|
"name": "organization-chart-webpart",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"engines": { "node": ">=6.9.0 <9.0.0" },
|
"engines": { "node": ">=6.9.0 <9.0.0" },
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ export class OrganizationChartDataService {
|
|||||||
cacheMinutes: number
|
cacheMinutes: number
|
||||||
): Promise<IOrganizationChartData> {
|
): Promise<IOrganizationChartData> {
|
||||||
const account: string = normalizeAccount(accountName);
|
const account: string = normalizeAccount(accountName);
|
||||||
if (!account) { return Promise.reject(new Error('A user account is required.')); }
|
|
||||||
const state: ITreeLoadState = {
|
const state: ITreeLoadState = {
|
||||||
count: 0,
|
count: 0,
|
||||||
maximum: normalizeMaxPeople(maxPeople),
|
maximum: normalizeMaxPeople(maxPeople),
|
||||||
@@ -49,11 +48,28 @@ export class OrganizationChartDataService {
|
|||||||
truncated: false,
|
truncated: false,
|
||||||
visited: {}
|
visited: {}
|
||||||
};
|
};
|
||||||
return this.buildNode(account, 0, state, cacheMinutes).then((root: IOrganizationNode): IOrganizationChartData => ({
|
const rootRequest: Promise<IUserProfile> = account
|
||||||
root: root,
|
? this.getProfile(account, cacheMinutes)
|
||||||
peopleCount: state.count,
|
: this.getMyProfile(cacheMinutes);
|
||||||
truncated: state.truncated
|
return rootRequest
|
||||||
}));
|
.then((profile: IUserProfile): Promise<IOrganizationNode> =>
|
||||||
|
this.buildProfileNode(profile, 0, state, cacheMinutes))
|
||||||
|
.then((root: IOrganizationNode): IOrganizationChartData => ({
|
||||||
|
root: root,
|
||||||
|
peopleCount: state.count,
|
||||||
|
truncated: state.truncated
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMyProfile(cacheMinutes: number): Promise<IUserProfile> {
|
||||||
|
const cacheKey: string = '__current_user__';
|
||||||
|
const cached: IProfileCacheEntry = OrganizationChartDataService.cache[cacheKey];
|
||||||
|
if (cached && cached.expires > new Date().getTime()) { return Promise.resolve(cached.profile); }
|
||||||
|
return this.requestProfile(
|
||||||
|
this.webUrl + '/_api/SP.UserProfiles.PeopleManager/GetMyProperties',
|
||||||
|
cacheKey,
|
||||||
|
cacheMinutes
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getProfile(accountName: string, cacheMinutes: number): Promise<IUserProfile> {
|
public getProfile(accountName: string, cacheMinutes: number): Promise<IUserProfile> {
|
||||||
@@ -64,6 +80,10 @@ export class OrganizationChartDataService {
|
|||||||
const escaped: string = account.replace(/'/g, "''");
|
const escaped: string = account.replace(/'/g, "''");
|
||||||
const url: string = this.webUrl +
|
const url: string = this.webUrl +
|
||||||
"/_api/SP.UserProfiles.PeopleManager/GetPropertiesFor(accountName=@v)?@v='" + encodeURIComponent(escaped) + "'";
|
"/_api/SP.UserProfiles.PeopleManager/GetPropertiesFor(accountName=@v)?@v='" + encodeURIComponent(escaped) + "'";
|
||||||
|
return this.requestProfile(url, key, cacheMinutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private requestProfile(url: string, cacheKey: string, cacheMinutes: number): Promise<IUserProfile> {
|
||||||
return this.context.spHttpClient.get(url, SPHttpClient.configurations.v1, { headers: this.headers })
|
return this.context.spHttpClient.get(url, SPHttpClient.configurations.v1, { headers: this.headers })
|
||||||
.then((response: SPHttpClientResponse): Promise<any> => { // tslint:disable-line:no-any
|
.then((response: SPHttpClientResponse): Promise<any> => { // tslint:disable-line:no-any
|
||||||
if (response.ok) { return response.json(); }
|
if (response.ok) { return response.json(); }
|
||||||
@@ -74,10 +94,14 @@ export class OrganizationChartDataService {
|
|||||||
const profile: IUserProfile = this.mapProfile(raw || {});
|
const profile: IUserProfile = this.mapProfile(raw || {});
|
||||||
const minutes: number = normalizeCacheMinutes(cacheMinutes);
|
const minutes: number = normalizeCacheMinutes(cacheMinutes);
|
||||||
if (minutes > 0) {
|
if (minutes > 0) {
|
||||||
OrganizationChartDataService.cache[key] = {
|
OrganizationChartDataService.cache[cacheKey] = {
|
||||||
profile: profile,
|
profile: profile,
|
||||||
expires: new Date().getTime() + minutes * 60000
|
expires: new Date().getTime() + minutes * 60000
|
||||||
};
|
};
|
||||||
|
if (profile.accountName) {
|
||||||
|
OrganizationChartDataService.cache[profile.accountName.toLowerCase()] =
|
||||||
|
OrganizationChartDataService.cache[cacheKey];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return profile;
|
return profile;
|
||||||
});
|
});
|
||||||
@@ -95,26 +119,39 @@ export class OrganizationChartDataService {
|
|||||||
if (state.count >= state.maximum) { state.truncated = true; }
|
if (state.count >= state.maximum) { state.truncated = true; }
|
||||||
return Promise.resolve(undefined as IOrganizationNode);
|
return Promise.resolve(undefined as IOrganizationNode);
|
||||||
}
|
}
|
||||||
|
return this.getProfile(account, cacheMinutes).then((profile: IUserProfile): Promise<IOrganizationNode> =>
|
||||||
|
this.buildProfileNode(profile, depth, state, cacheMinutes));
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildProfileNode(
|
||||||
|
profile: IUserProfile,
|
||||||
|
depth: number,
|
||||||
|
state: ITreeLoadState,
|
||||||
|
cacheMinutes: number
|
||||||
|
): Promise<IOrganizationNode> {
|
||||||
|
const key: string = normalizeAccount(profile.accountName).toLowerCase();
|
||||||
|
if (!key || state.visited[key] || state.count >= state.maximum) {
|
||||||
|
if (state.count >= state.maximum) { state.truncated = true; }
|
||||||
|
return Promise.resolve(undefined as IOrganizationNode);
|
||||||
|
}
|
||||||
state.visited[key] = true;
|
state.visited[key] = true;
|
||||||
state.count++;
|
state.count++;
|
||||||
return this.getProfile(account, cacheMinutes).then((profile: IUserProfile): Promise<IOrganizationNode> => {
|
const node: IOrganizationNode = { profile: profile, children: [] };
|
||||||
const node: IOrganizationNode = { profile: profile, children: [] };
|
if (depth >= state.maximumDepth) {
|
||||||
if (depth >= state.maximumDepth) {
|
if (profile.directReports.length) { state.truncated = true; }
|
||||||
if (profile.directReports.length) { state.truncated = true; }
|
return Promise.resolve(node);
|
||||||
return Promise.resolve(node);
|
}
|
||||||
}
|
const accounts: string[] = uniqueAccounts(profile.directReports, profile.accountName, state.maximum);
|
||||||
const accounts: string[] = uniqueAccounts(profile.directReports, profile.accountName, state.maximum);
|
const requests: Array<Promise<IOrganizationNode>> = accounts.map(
|
||||||
const requests: Array<Promise<IOrganizationNode>> = accounts.map(
|
(childAccount: string): Promise<IOrganizationNode> =>
|
||||||
(childAccount: string): Promise<IOrganizationNode> =>
|
this.buildNode(childAccount, depth + 1, state, cacheMinutes)
|
||||||
this.buildNode(childAccount, depth + 1, state, cacheMinutes)
|
.catch((): IOrganizationNode => undefined as IOrganizationNode)
|
||||||
.catch((): IOrganizationNode => undefined as IOrganizationNode)
|
);
|
||||||
);
|
return Promise.all(requests).then((children: IOrganizationNode[]): IOrganizationNode => {
|
||||||
return Promise.all(requests).then((children: IOrganizationNode[]): IOrganizationNode => {
|
node.children = children.filter((child: IOrganizationNode): boolean => !!child)
|
||||||
node.children = children.filter((child: IOrganizationNode): boolean => !!child)
|
.sort((left: IOrganizationNode, right: IOrganizationNode): number =>
|
||||||
.sort((left: IOrganizationNode, right: IOrganizationNode): number =>
|
left.profile.displayName.localeCompare(right.profile.displayName));
|
||||||
left.profile.displayName.localeCompare(right.profile.displayName));
|
return node;
|
||||||
return node;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,8 @@ export default class OrganizationChartWebPart extends BaseClientSideWebPart<IOrg
|
|||||||
private disposed: boolean = false;
|
private disposed: boolean = false;
|
||||||
|
|
||||||
public render(): void {
|
public render(): void {
|
||||||
const account: string = normalizeAccount(this.properties.startUser || this.context.pageContext.user.loginName);
|
const account: string = normalizeAccount(this.properties.startUser);
|
||||||
const version: number = ++this.requestVersion;
|
const version: number = ++this.requestVersion;
|
||||||
if (!account) { this.renderStatus(strings.ConfigureMessage, false); return; }
|
|
||||||
this.renderStatus(strings.LoadingMessage, false);
|
this.renderStatus(strings.LoadingMessage, false);
|
||||||
this.service.getOrganization(
|
this.service.getOrganization(
|
||||||
account,
|
account,
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ assert(packageSolution.solution.includeClientSideAssets === true && packageSolut
|
|||||||
assert(!!config.bundles['organization-chart-webpart'], 'Bundle name is inconsistent.');
|
assert(!!config.bundles['organization-chart-webpart'], 'Bundle name is inconsistent.');
|
||||||
assert(manifest.componentType === 'WebPart' && manifest.alias === 'OrganizationChartWebPart', 'Manifest is invalid.');
|
assert(manifest.componentType === 'WebPart' && manifest.alias === 'OrganizationChartWebPart', 'Manifest is invalid.');
|
||||||
assert(service.indexOf('/_api/SP.UserProfiles.PeopleManager/GetPropertiesFor') >= 0, 'User Profile REST access is missing.');
|
assert(service.indexOf('/_api/SP.UserProfiles.PeopleManager/GetPropertiesFor') >= 0, 'User Profile REST access is missing.');
|
||||||
|
assert(service.indexOf('/_api/SP.UserProfiles.PeopleManager/GetMyProperties') >= 0,
|
||||||
|
'Reliable current-user profile access is missing.');
|
||||||
assert(service.indexOf('profile.directReports') >= 0 && service.indexOf('this.buildNode(childAccount, depth + 1') >= 0,
|
assert(service.indexOf('profile.directReports') >= 0 && service.indexOf('this.buildNode(childAccount, depth + 1') >= 0,
|
||||||
'Recursive downward organization loading is missing.');
|
'Recursive downward organization loading is missing.');
|
||||||
assert(webPart.indexOf("PropertyPaneTextField('startUser'") >= 0, 'Starting-person configuration is missing.');
|
assert(webPart.indexOf("PropertyPaneTextField('startUser'") >= 0, 'Starting-person configuration is missing.');
|
||||||
|
|||||||
Reference in New Issue
Block a user