feat: Update version to 1.0.1 and enhance user profile retrieval in OrganizationChartDataService

This commit is contained in:
Torsten Brendgen
2026-08-24 14:05:36 +02:00
parent 1a69236c3a
commit 83dd10f255
5 changed files with 67 additions and 29 deletions
@@ -41,7 +41,6 @@ export class OrganizationChartDataService {
cacheMinutes: number
): Promise<IOrganizationChartData> {
const account: string = normalizeAccount(accountName);
if (!account) { return Promise.reject(new Error('A user account is required.')); }
const state: ITreeLoadState = {
count: 0,
maximum: normalizeMaxPeople(maxPeople),
@@ -49,11 +48,28 @@ export class OrganizationChartDataService {
truncated: false,
visited: {}
};
return this.buildNode(account, 0, state, cacheMinutes).then((root: IOrganizationNode): IOrganizationChartData => ({
root: root,
peopleCount: state.count,
truncated: state.truncated
}));
const rootRequest: Promise<IUserProfile> = account
? this.getProfile(account, cacheMinutes)
: this.getMyProfile(cacheMinutes);
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> {
@@ -64,6 +80,10 @@ export class OrganizationChartDataService {
const escaped: string = account.replace(/'/g, "''");
const url: string = this.webUrl +
"/_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 })
.then((response: SPHttpClientResponse): Promise<any> => { // tslint:disable-line:no-any
if (response.ok) { return response.json(); }
@@ -74,10 +94,14 @@ export class OrganizationChartDataService {
const profile: IUserProfile = this.mapProfile(raw || {});
const minutes: number = normalizeCacheMinutes(cacheMinutes);
if (minutes > 0) {
OrganizationChartDataService.cache[key] = {
OrganizationChartDataService.cache[cacheKey] = {
profile: profile,
expires: new Date().getTime() + minutes * 60000
};
if (profile.accountName) {
OrganizationChartDataService.cache[profile.accountName.toLowerCase()] =
OrganizationChartDataService.cache[cacheKey];
}
}
return profile;
});
@@ -95,26 +119,39 @@ export class OrganizationChartDataService {
if (state.count >= state.maximum) { state.truncated = true; }
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.count++;
return this.getProfile(account, cacheMinutes).then((profile: IUserProfile): Promise<IOrganizationNode> => {
const node: IOrganizationNode = { profile: profile, children: [] };
if (depth >= state.maximumDepth) {
if (profile.directReports.length) { state.truncated = true; }
return Promise.resolve(node);
}
const accounts: string[] = uniqueAccounts(profile.directReports, profile.accountName, state.maximum);
const requests: Array<Promise<IOrganizationNode>> = accounts.map(
(childAccount: string): Promise<IOrganizationNode> =>
this.buildNode(childAccount, depth + 1, state, cacheMinutes)
.catch((): IOrganizationNode => undefined as IOrganizationNode)
);
return Promise.all(requests).then((children: IOrganizationNode[]): IOrganizationNode => {
node.children = children.filter((child: IOrganizationNode): boolean => !!child)
.sort((left: IOrganizationNode, right: IOrganizationNode): number =>
left.profile.displayName.localeCompare(right.profile.displayName));
return node;
});
const node: IOrganizationNode = { profile: profile, children: [] };
if (depth >= state.maximumDepth) {
if (profile.directReports.length) { state.truncated = true; }
return Promise.resolve(node);
}
const accounts: string[] = uniqueAccounts(profile.directReports, profile.accountName, state.maximum);
const requests: Array<Promise<IOrganizationNode>> = accounts.map(
(childAccount: string): Promise<IOrganizationNode> =>
this.buildNode(childAccount, depth + 1, state, cacheMinutes)
.catch((): IOrganizationNode => undefined as IOrganizationNode)
);
return Promise.all(requests).then((children: IOrganizationNode[]): IOrganizationNode => {
node.children = children.filter((child: IOrganizationNode): boolean => !!child)
.sort((left: IOrganizationNode, right: IOrganizationNode): number =>
left.profile.displayName.localeCompare(right.profile.displayName));
return node;
});
}