UNPKG

@atlaskit/profilecard

Version:

A React component to display a card with user information.

197 lines (192 loc) 7.22 kB
import { fg } from '@atlaskit/platform-feature-flags'; import CachingClient from './CachingClient'; import { getOrgIdForCloudIdFromAGG } from './getOrgIdForCloudIdFromAGG'; import { directoryGraphqlQuery } from './graphqlUtils'; const UNSHARDED_PREFIX = '/gateway/api/watermelon'; export const buildReportingLinesQuery = aaid => ({ query: ` fragment ReportingLinesUserPII on UserPII { name picture } fragment ReportingLinesUserFragment on ReportingLinesUser { accountIdentifier identifierType pii { ...ReportingLinesUserPII } } query ReportingLines($aaid: String) { reportingLines(aaidOrHash: $aaid) { managers { ...ReportingLinesUserFragment } reports { ...ReportingLinesUserFragment } } } `, variables: { aaid } }); const orgContainsAnyWorkspacePromiseCache = new Map(); const orgIdPromiseCache = new Map(); const workspaceExistsWithTypePromiseCache = new Map(); class TeamCentralCardClient extends CachingClient { /** * Simple circuit breaker to avoid making unnecessary calls to Team Central on auth failures * This is to handle the case where products may have provided teamCentralUrl, but the site itself * doesn't actually have any TC product. * * There's currently no way to reset this circuit breaker, but that's fine. This is meant to * catch a pretty specific edge case. */ constructor(options) { super(options); this.options = options; this.bypassOnFailure = false; this.orgContainsAnyWorkspacePromise = this.createOrgContainsAnyWorkspacePromise(options); this.workspaceExistsWithTypePromise = this.preloadWorkspaceExistsWithType(options.cloudId); this.orgIdPromise = this.preloadOrgId(options.gatewayGraphqlUrl, options.cloudId, options.orgId); } createOrgContainsAnyWorkspacePromise(config) { if (config.cloudId) { let promise = orgContainsAnyWorkspacePromiseCache.get(config.cloudId); if (!promise) { promise = this.getOrgContainsAnyWorkspace(config.cloudId); orgContainsAnyWorkspacePromiseCache.set(config.cloudId, promise); } return promise; } return Promise.resolve(true); } getReportingLines(userId) { return this.orgContainsAnyWorkspacePromise.then(orgContainsAnyWorkspace => { if (orgContainsAnyWorkspace) { if (!userId) { return Promise.reject(new Error('userId missing')); } const cache = this.getCachedProfile(userId); if (cache) { return Promise.resolve(cache); } if (this.bypassOnFailure) { return Promise.resolve({}); } return new Promise(resolve => { this.makeRequest(userId).then(data => { const enhancedData = { managers: this.filterReportingLinesUser(data === null || data === void 0 ? void 0 : data.managers), reports: this.filterReportingLinesUser(data === null || data === void 0 ? void 0 : data.reports) }; if (this.cache) { this.setCachedProfile(userId, enhancedData); } resolve(enhancedData); }).catch(error => { if ((error === null || error === void 0 ? void 0 : error.status) === 401 || (error === null || error === void 0 ? void 0 : error.status) === 403) { // Trigger circuit breaker this.bypassOnFailure = true; } /** * Reporting lines aren't part of the critical path of profile card. * Just resolve with empty values instead of bubbling up the error. */ resolve({}); }); }); } return Promise.resolve({ managers: [], reports: [] }); }, () => Promise.resolve({ managers: [], reports: [] })); } /** * `public` so that mock client can override it; do not use it otherwise! */ async makeRequest(userId) { if (this.options.teamCentralDisabled === true) { throw new Error('makeRequest cannot be called when the client has been disabled'); } const query = buildReportingLinesQuery(userId); const response = await directoryGraphqlQuery('/gateway/api/watermelon/graphql?operationName=ReportingLines', query); return response.reportingLines; } async checkWorkspaceExists() { const workspaceExistsPromise = fg('enable_ptc_townsquare_reporting_lines_unsharded') ? this.workspaceExistsWithTypePromise.then(workspaceExistsWithType => workspaceExistsWithType !== undefined) : this.orgContainsAnyWorkspacePromise; return workspaceExistsPromise.then(workspaceExistsWithType => { if (workspaceExistsWithType) { return Promise.resolve(true); } return Promise.resolve(false); }, () => Promise.resolve(false)); } async getIsGlobalExperienceWorkspace() { return (await this.workspaceExistsWithTypePromise) === 'GLOBAL_EXPERIENCE'; } getOrgId() { return this.orgIdPromise; } preloadWorkspaceExistsWithType(cloudId) { if (cloudId === undefined) { return Promise.resolve(undefined); } const maybeWorkspaceExistsWithTypePromise = workspaceExistsWithTypePromiseCache.get(cloudId); if (maybeWorkspaceExistsWithTypePromise !== undefined) { return maybeWorkspaceExistsWithTypePromise; } const workspaceExistsWithTypePromise = this.getWorkspaceExistsWithType(cloudId); workspaceExistsWithTypePromiseCache.set(cloudId, workspaceExistsWithTypePromise); return workspaceExistsWithTypePromise; } getOrgContainsAnyWorkspace(cloudId) { if (cloudId) { return fetch((fg('enable_ptc_townsquare_reporting_lines_unsharded') ? UNSHARDED_PREFIX : this.getShardedApiPath(cloudId)) + `/organization/containsAnyWorkspace?cloudId=${cloudId}`).then(res => { return !res || res && res.ok; }); } else { return Promise.resolve(false); } } async getWorkspaceExistsWithType(cloudId) { try { const response = await fetch(`${this.getShardedApiPath(cloudId)}/workspace/existsWithWorkspaceType?cloudId=${cloudId}`, { credentials: 'include' }); if (response.ok) { const workspaceType = await response.text(); return Promise.resolve(workspaceType); } } catch (err) { return Promise.resolve(undefined); } } preloadOrgId(gatewayGraphqlUrl, cloudId, orgId) { if (cloudId === undefined) { return Promise.resolve(null); } if (orgId !== undefined) { return Promise.resolve(orgId); } const maybeOrgIdForCloudIdPromise = orgIdPromiseCache.get(cloudId); if (maybeOrgIdForCloudIdPromise !== undefined) { return maybeOrgIdForCloudIdPromise; } const orgIdForCloudIdPromise = getOrgIdForCloudIdFromAGG(gatewayGraphqlUrl, cloudId); orgIdPromiseCache.set(cloudId, orgIdForCloudIdPromise); return orgIdForCloudIdPromise; } getShardedApiPath(cloudId) { return `/gateway/api/townsquare/s/${cloudId}`; } filterReportingLinesUser(users = []) { return users.filter(user => user.identifierType === 'ATLASSIAN_ID'); } } export default TeamCentralCardClient;