Corporate: removing unused company-specific bits
This commit is contained in:
Родитель
ecae7fc77d
Коммит
182a8e2c9e
|
@ -194,20 +194,6 @@
|
|||
"DEBUG": "redis,restapi,startup,appinsights,cache"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Job: Manager hygiene (5)",
|
||||
"program": "${workspaceRoot}/dist/jobs/managers/index.js",
|
||||
"cwd": "${workspaceRoot}/dist",
|
||||
"preLaunchTask": "tsbuild",
|
||||
"sourceMaps": true,
|
||||
"console": "integratedTerminal",
|
||||
"env": {
|
||||
"NODE_ENV": "development",
|
||||
"DEBUG": "redis,restapi,startup,appinsights"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
|
|
|
@ -57,7 +57,6 @@ export interface IProviders {
|
|||
basedir?: string;
|
||||
campaignStateProvider?: ICampaignHelper;
|
||||
campaign?: any; // campaign redirection route, poor variable name
|
||||
corporateContactProvider?: ICorporateContactProvider;
|
||||
config?: SiteConfiguration;
|
||||
customizedNewRepositoryLogic?: ICustomizedNewRepositoryLogic;
|
||||
customizedTeamPermissionsWebhookLogic?: ICustomizedTeamPermissionsWebhookLogic;
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
import Job from './task';
|
||||
import app from '../../app';
|
||||
|
||||
app.runJob(Job, {
|
||||
insightsPrefix: 'JobRefreshManagers',
|
||||
});
|
|
@ -1,263 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
// A simple job to cache the last-known manager e-mail address for linked users
|
||||
// in Redis, using this app's abstracted APIs to be slightly more generic.
|
||||
|
||||
import throat from 'throat';
|
||||
|
||||
import { ICachedEmployeeInformation, ICorporateLink, IReposJob, IReposJobResult } from '../../interfaces';
|
||||
import { createAndInitializeLinkProviderInstance } from '../../lib/linkProviders';
|
||||
import { sleep } from '../../utils';
|
||||
import { IMicrosoftIdentityServiceBasics } from '../../lib/corporateContactProvider';
|
||||
import { RedisPrefixManagerInfoCache } from '../../business';
|
||||
|
||||
export default async function refresh({ providers }: IReposJob): Promise<IReposJobResult> {
|
||||
const { config } = providers;
|
||||
if (config?.jobs?.refreshWrites !== true) {
|
||||
console.log('job is currently disabled to avoid metadata refresh/rewrites');
|
||||
return;
|
||||
}
|
||||
|
||||
const graphProvider = providers.graphProvider;
|
||||
const cacheHelper = providers.cacheProvider;
|
||||
const insights = providers.insights;
|
||||
const linkProvider = await createAndInitializeLinkProviderInstance(providers, config);
|
||||
|
||||
console.log('reading all links to gather manager info ahead of any terminations');
|
||||
const allLinks = await linkProvider.getAll();
|
||||
console.log(`READ: ${allLinks.length} links`);
|
||||
insights.trackEvent({
|
||||
name: 'JobRefreshManagersReadLinks',
|
||||
properties: { links: String(allLinks.length) },
|
||||
});
|
||||
|
||||
let errors = 0;
|
||||
let notFoundErrors = 0;
|
||||
const errorList = [];
|
||||
|
||||
let managerUpdates = 0;
|
||||
let managerSets = 0;
|
||||
let managerMetadataUpdates = 0;
|
||||
|
||||
const userDetailsThroatCount = 1;
|
||||
const secondsDelayAfterError = 1;
|
||||
const secondsDelayAfterSuccess = 0.09; //0.1;
|
||||
|
||||
const managerInfoCachePeriodMinutes = 60 * 24 * 7 * 12; // 12 weeks
|
||||
|
||||
let processed = 0;
|
||||
|
||||
const bulkContacts = new Map<string, IMicrosoftIdentityServiceBasics | boolean>();
|
||||
|
||||
const throttle = throat(userDetailsThroatCount);
|
||||
const unknownServiceAccounts: ICorporateLink[] = [];
|
||||
const formerAccounts: ICorporateLink[] = [];
|
||||
await Promise.all(
|
||||
allLinks.map((link: ICorporateLink) =>
|
||||
throttle(async () => {
|
||||
const employeeDirectoryId = link.corporateId;
|
||||
++processed;
|
||||
bulkContacts.set(link.corporateUsername, false);
|
||||
if (processed % 25 === 0) {
|
||||
console.log(`${processed}/${allLinks.length}.`);
|
||||
}
|
||||
if (link.isServiceAccount) {
|
||||
console.log(`Service account: ${link.corporateUsername}`);
|
||||
}
|
||||
let info = null,
|
||||
infoError = null;
|
||||
try {
|
||||
info = await graphProvider.getUserAndManagerById(employeeDirectoryId);
|
||||
if (link.isServiceAccount) {
|
||||
console.log();
|
||||
// console.dir(info);
|
||||
console.log(`info OK for SA ${link.corporateUsername}`);
|
||||
}
|
||||
} catch (retrievalError) {
|
||||
if (link.isServiceAccount) {
|
||||
// console.dir(retrievalError);
|
||||
console.log(`no info for SA: ${link.corporateUsername}`);
|
||||
unknownServiceAccounts.push(link);
|
||||
} else {
|
||||
console.log();
|
||||
console.log(`Not present: ${link.corporateUsername} ${retrievalError}`);
|
||||
infoError = retrievalError;
|
||||
}
|
||||
infoError = retrievalError;
|
||||
}
|
||||
if (
|
||||
providers.corporateContactProvider &&
|
||||
((info && info.userPrincipalName) || link.corporateUsername)
|
||||
) {
|
||||
try {
|
||||
const userPrincipalName =
|
||||
info && info.userPrincipalName ? info.userPrincipalName : link.corporateUsername;
|
||||
const contactsCache = await providers.corporateContactProvider.lookupContacts(userPrincipalName);
|
||||
if (contactsCache || (!contactsCache && link.isServiceAccount)) {
|
||||
bulkContacts.set(userPrincipalName, contactsCache);
|
||||
}
|
||||
} catch (identityServiceError) {
|
||||
// Bulk cache is a secondary function of this job
|
||||
console.warn(identityServiceError);
|
||||
}
|
||||
}
|
||||
if (link.isServiceAccount) {
|
||||
console.log(`skipping service account link ${link.corporateUsername}`);
|
||||
console.log();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (infoError) {
|
||||
throw infoError;
|
||||
}
|
||||
if (!info || !info.manager) {
|
||||
console.log(
|
||||
`No manager info is set for ${employeeDirectoryId} - ${info.displayName} ${info.userPrincipalName}`
|
||||
);
|
||||
return; // no sleep
|
||||
}
|
||||
// Has the user's corporate display information changed?
|
||||
let linkChanges = false;
|
||||
if (info.displayName !== link.corporateDisplayName) {
|
||||
linkChanges = true;
|
||||
console.log(
|
||||
`Update to corporate link: display name changed from ${link.corporateDisplayName} to ${info.displayName}`
|
||||
);
|
||||
link.corporateDisplayName = info.displayName;
|
||||
}
|
||||
if (info.userPrincipalName !== link.corporateUsername) {
|
||||
linkChanges = true;
|
||||
console.log(
|
||||
`Update to corporate link: username changed from ${link.corporateUsername} to ${info.userPrincipalName}`
|
||||
);
|
||||
link.corporateUsername = info.userPrincipalName;
|
||||
}
|
||||
if (linkChanges) {
|
||||
await linkProvider.updateLink(link);
|
||||
console.log(`Updated link for ${link.corporateId}`);
|
||||
}
|
||||
if (!info.manager.mail) {
|
||||
console.log('No manager mail address');
|
||||
throw new Error('No manager mail address in graph');
|
||||
}
|
||||
const reducedWithManagerInfo: ICachedEmployeeInformation = {
|
||||
id: info.id,
|
||||
displayName: info.displayName,
|
||||
userPrincipalName: info.userPrincipalName,
|
||||
managerId: info.manager.id,
|
||||
managerDisplayName: info.manager.displayName,
|
||||
managerMail: info.manager.mail,
|
||||
};
|
||||
const key = `${RedisPrefixManagerInfoCache}${employeeDirectoryId}`;
|
||||
const currentManagerIfAny = (await cacheHelper.getObjectCompressed(key)) as any;
|
||||
if (!currentManagerIfAny) {
|
||||
await cacheHelper.setObjectCompressedWithExpire(
|
||||
key,
|
||||
reducedWithManagerInfo,
|
||||
managerInfoCachePeriodMinutes
|
||||
);
|
||||
++managerSets;
|
||||
console.log(
|
||||
`Manager for ${reducedWithManagerInfo.displayName} set to ${reducedWithManagerInfo.managerDisplayName}`
|
||||
);
|
||||
} else {
|
||||
let updateEntry = false;
|
||||
if (currentManagerIfAny.managerId !== reducedWithManagerInfo.managerId) {
|
||||
updateEntry = true;
|
||||
++managerUpdates;
|
||||
console.log(
|
||||
`Manager for ${reducedWithManagerInfo.displayName} updated to ${reducedWithManagerInfo.managerDisplayName}`
|
||||
);
|
||||
} else if (
|
||||
currentManagerIfAny.id !== reducedWithManagerInfo.id ||
|
||||
currentManagerIfAny.displayName !== reducedWithManagerInfo.displayName ||
|
||||
currentManagerIfAny.userPrincipalName !== reducedWithManagerInfo.userPrincipalName ||
|
||||
currentManagerIfAny.managerDisplayName !== reducedWithManagerInfo.managerDisplayName ||
|
||||
currentManagerIfAny.managerMail !== reducedWithManagerInfo.managerMail
|
||||
) {
|
||||
updateEntry = true;
|
||||
++managerMetadataUpdates;
|
||||
console.log(`Metadata for ${reducedWithManagerInfo.displayName} updated`);
|
||||
}
|
||||
if (updateEntry) {
|
||||
await cacheHelper.setObjectCompressedWithExpire(
|
||||
key,
|
||||
reducedWithManagerInfo,
|
||||
managerInfoCachePeriodMinutes
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (retrievalError) {
|
||||
if (retrievalError && retrievalError.status && retrievalError.status === 404) {
|
||||
++notFoundErrors;
|
||||
formerAccounts.push(link);
|
||||
// Not deleting links so proactively: await linkProvider.deleteLink(link);
|
||||
insights.trackEvent({
|
||||
name: 'JobRefreshManagersNotFound',
|
||||
properties: { error: retrievalError.message },
|
||||
});
|
||||
} else {
|
||||
console.dir(retrievalError);
|
||||
++errors;
|
||||
insights.trackEvent({
|
||||
name: 'JobRefreshManagersError',
|
||||
properties: { error: retrievalError.message },
|
||||
});
|
||||
}
|
||||
await sleep(secondsDelayAfterError * 1000);
|
||||
return;
|
||||
}
|
||||
await sleep(secondsDelayAfterSuccess * 1000);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
console.log('All done with', errors, 'errors. Not found errors:', notFoundErrors);
|
||||
console.dir(errorList);
|
||||
console.log();
|
||||
|
||||
console.log(`Service Accounts not in the directory: ${unknownServiceAccounts.length}`);
|
||||
console.log(
|
||||
unknownServiceAccounts
|
||||
.map((x) => x.corporateUsername)
|
||||
.sort()
|
||||
.join('\n')
|
||||
);
|
||||
console.log();
|
||||
|
||||
console.log(`Former accounts not in the directory: ${formerAccounts.length}`);
|
||||
console.log(
|
||||
formerAccounts
|
||||
.map((x) => x.corporateUsername)
|
||||
.sort()
|
||||
.join('\n')
|
||||
);
|
||||
console.log();
|
||||
|
||||
if (bulkContacts.size) {
|
||||
console.log(`Writing ${bulkContacts.size} contacts to bulk cache...`);
|
||||
try {
|
||||
await providers.corporateContactProvider.setBulkCachedContacts(bulkContacts);
|
||||
console.log('Cached.');
|
||||
} catch (cacheError) {
|
||||
console.log('Cache problem:');
|
||||
console.warn(cacheError);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Manager updates: ${managerUpdates}`);
|
||||
console.log(`Manager sets: ${managerSets}`);
|
||||
console.log(`Other updates: ${managerMetadataUpdates}`);
|
||||
console.log();
|
||||
return {
|
||||
successProperties: {
|
||||
managerUpdates,
|
||||
managerSets,
|
||||
managerMetadataUpdates,
|
||||
errors,
|
||||
},
|
||||
};
|
||||
}
|
|
@ -3,16 +3,6 @@
|
|||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
import axios, { AxiosError } from 'axios';
|
||||
|
||||
import { CreateError } from '../transitional';
|
||||
import { ICacheHelper } from './caching';
|
||||
|
||||
const DefaultCacheMinutesPerContact = 120;
|
||||
const BulkCacheMinutes = 60 * 24 * 14;
|
||||
|
||||
const BulkCacheKey = 'cc:bulk';
|
||||
|
||||
export interface ICorporateContactInformation {
|
||||
openSourceContact?: string;
|
||||
primaryLegalContact?: string;
|
||||
|
@ -31,154 +21,3 @@ export interface ICorporateContactProvider {
|
|||
getBulkCachedContacts(): Promise<Map<string, ICorporateContactInformation | boolean>>;
|
||||
setBulkCachedContacts(map: Map<string, ICorporateContactInformation | boolean>): Promise<void>;
|
||||
}
|
||||
|
||||
export default function createCorporateContactProviderInstance(
|
||||
config,
|
||||
cacheHelper: ICacheHelper
|
||||
): ICorporateContactProvider {
|
||||
return new MicrosoftIdentityService(config, cacheHelper);
|
||||
}
|
||||
|
||||
export interface IMicrosoftIdentityServiceBasics {
|
||||
aadId?: string;
|
||||
alias?: string;
|
||||
costCenterCode?: string;
|
||||
emailAddress?: string;
|
||||
functionHierarchyExecCode?: string;
|
||||
manager?: string;
|
||||
preferredName?: string;
|
||||
userPrincipalName?: string;
|
||||
}
|
||||
|
||||
interface IMicrosoftIdentityServiceResponse extends IMicrosoftIdentityServiceBasics {
|
||||
attorney?: string;
|
||||
group?: string;
|
||||
highRiskBusiness?: string;
|
||||
immediate?: boolean;
|
||||
legal?: string;
|
||||
legalOssContact?: string;
|
||||
legalPrimaryContact?: string;
|
||||
legalSecondaryContact?: string;
|
||||
lowRiskBusiness?: string;
|
||||
maintainer?: string;
|
||||
structure?: IMicrosoftIdentityServiceBasics[];
|
||||
system?: string;
|
||||
}
|
||||
|
||||
class MicrosoftIdentityService implements ICorporateContactProvider {
|
||||
#identityConfig: any;
|
||||
#cacheHelper: ICacheHelper;
|
||||
|
||||
constructor(config: any, cacheHelper: ICacheHelper) {
|
||||
this.#identityConfig = config.identity;
|
||||
this.#cacheHelper = cacheHelper;
|
||||
}
|
||||
|
||||
async lookupContacts(corporateUsername: string): Promise<ICorporateContactInformation> {
|
||||
let response: IMicrosoftIdentityServiceResponse;
|
||||
const cacheKey = `cc:${corporateUsername}`;
|
||||
if (this.#cacheHelper) {
|
||||
try {
|
||||
response = await this.#cacheHelper.getObject(cacheKey);
|
||||
} catch (ignoreError) {
|
||||
/* ignored */
|
||||
}
|
||||
}
|
||||
if (!response || !Object.keys(response).length) {
|
||||
response = await this.callIdentityService(corporateUsername);
|
||||
if (this.#cacheHelper && response) {
|
||||
// kicks off an async operation
|
||||
this.#cacheHelper.setObjectWithExpire(cacheKey, response, DefaultCacheMinutesPerContact);
|
||||
}
|
||||
}
|
||||
if (!response) {
|
||||
return null;
|
||||
}
|
||||
let managerUsername = null,
|
||||
managerDisplayName = null;
|
||||
const manager = response.structure && response.structure.length ? response.structure[0] : null;
|
||||
if (manager) {
|
||||
managerDisplayName = manager.preferredName;
|
||||
managerUsername = manager.userPrincipalName;
|
||||
}
|
||||
return {
|
||||
openSourceContact: response.legalOssContact,
|
||||
primaryLegalContact: response.legalPrimaryContact,
|
||||
secondaryLegalContact: response.legalSecondaryContact,
|
||||
highRiskBusinessReviewer: response.highRiskBusiness,
|
||||
lowRiskBusinessReviewer: response.lowRiskBusiness,
|
||||
alias: response.alias,
|
||||
emailAddress: response.emailAddress,
|
||||
managerUsername,
|
||||
managerDisplayName,
|
||||
legal: response.legal,
|
||||
};
|
||||
}
|
||||
|
||||
async getBulkCachedContacts(): Promise<Map<string, ICorporateContactInformation | boolean>> {
|
||||
let map = new Map<string, IMicrosoftIdentityServiceResponse | boolean>();
|
||||
if (!this.#cacheHelper) {
|
||||
return map;
|
||||
}
|
||||
const bulk = await this.#cacheHelper.getObject(BulkCacheKey);
|
||||
if (bulk && bulk.entities) {
|
||||
if (Array.isArray(bulk.entities)) {
|
||||
map = new Map<string, IMicrosoftIdentityServiceResponse>(bulk.entities);
|
||||
if (bulk.empties) {
|
||||
for (let i = 0; i < bulk.empties.length; i++) {
|
||||
map.set(bulk.empties[i], false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn(`Cached bulk entry ${BulkCacheKey} does not contain an array of entities`);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
async setBulkCachedContacts(map: Map<string, ICorporateContactInformation | boolean>): Promise<void> {
|
||||
if (!this.#cacheHelper) {
|
||||
return;
|
||||
}
|
||||
const all = Array.from(map.entries());
|
||||
const entities = all.filter((e) => typeof e[1] !== 'boolean');
|
||||
const empties = all
|
||||
.filter((e) => typeof e[1] === 'boolean')
|
||||
.map((e) => e[0])
|
||||
.filter((e) => e);
|
||||
const obj = { entities, empties };
|
||||
await this.#cacheHelper.setObjectCompressedWithExpire(BulkCacheKey, obj, BulkCacheMinutes);
|
||||
}
|
||||
|
||||
private getIdentityServiceRequestOptions(endpoint: string) {
|
||||
const url = this.#identityConfig.url + endpoint;
|
||||
const authToken = 'Basic ' + Buffer.from(this.#identityConfig.pat + ':', 'utf8').toString('base64');
|
||||
const headers = {
|
||||
Authorization: authToken,
|
||||
};
|
||||
return { url, headers };
|
||||
}
|
||||
|
||||
async callIdentityService(corporateUsername: string): Promise<IMicrosoftIdentityServiceResponse> {
|
||||
try {
|
||||
const response = await axios(this.getIdentityServiceRequestOptions(`/${corporateUsername}`));
|
||||
if ((response.data as any).error?.message) {
|
||||
// axios returns unknown now
|
||||
throw CreateError.InvalidParameters((response.data as any).error.message);
|
||||
}
|
||||
const entity = response.data as IMicrosoftIdentityServiceResponse;
|
||||
return entity;
|
||||
} catch (error) {
|
||||
const axiosError = error as AxiosError;
|
||||
if (axiosError?.response?.status === 404) {
|
||||
return null;
|
||||
} else if (axiosError?.response?.status >= 300) {
|
||||
throw CreateError.CreateStatusCodeError(
|
||||
axiosError.response.status,
|
||||
`Response code: ${axiosError.response.status}`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,6 @@ import { IProviders } from '../../interfaces';
|
|||
import { IMailAddressProvider } from '.';
|
||||
|
||||
export default function createMailAddressProvider(options): IMailAddressProvider {
|
||||
const config = options.config;
|
||||
if (!config.identity || !config.identity.url || !config.identity.pat) {
|
||||
throw new Error('Not configured for the Identity service');
|
||||
}
|
||||
const providers = options.providers as IProviders;
|
||||
if (!providers) {
|
||||
throw new Error(
|
||||
|
|
|
@ -61,7 +61,6 @@ import CosmosCache from '../lib/caching/cosmosdb';
|
|||
import BlobCache from '../lib/caching/blob';
|
||||
import { StatefulCampaignProvider } from '../lib/campaigns';
|
||||
import CosmosHelper from '../lib/cosmosHelper';
|
||||
import createCorporateContactProviderInstance from '../lib/corporateContactProvider';
|
||||
import { IQueueProcessor } from '../lib/queues';
|
||||
import ServiceBusQueueProcessor from '../lib/queues/servicebus';
|
||||
import AzureQueuesProcessor from '../lib/queues/azurequeue';
|
||||
|
@ -266,11 +265,6 @@ async function initializeAsync(
|
|||
});
|
||||
await providers.diagnosticsDrop.initialize();
|
||||
}
|
||||
|
||||
providers.corporateContactProvider = createCorporateContactProviderInstance(
|
||||
config,
|
||||
providers.cacheProvider
|
||||
);
|
||||
providers.corporateAdministrationProfile = getCompanySpecificDeployment()?.administrationSection;
|
||||
providers.corporateViews = await initializeCorporateViews(providers, rootdir);
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
10
package.json
10
package.json
|
@ -77,7 +77,7 @@
|
|||
"app-root-path": "3.1.0",
|
||||
"applicationinsights": "2.4.0",
|
||||
"async-prompt": "1.0.1",
|
||||
"axios": "1.2.2",
|
||||
"axios": "1.2.3",
|
||||
"basic-auth": "2.0.1",
|
||||
"body-parser": "1.20.1",
|
||||
"color-contrast-checker": "2.1.0",
|
||||
|
@ -150,9 +150,9 @@
|
|||
"@types/simple-oauth2": "4.1.1",
|
||||
"@types/validator": "13.7.10",
|
||||
"@typescript-eslint/eslint-plugin": "5.48.1",
|
||||
"@typescript-eslint/parser": "5.48.1",
|
||||
"cspell": "6.18.1",
|
||||
"eslint": "8.31.0",
|
||||
"@typescript-eslint/parser": "5.48.2",
|
||||
"cspell": "6.19.1",
|
||||
"eslint": "8.32.0",
|
||||
"eslint-config-prettier": "8.6.0",
|
||||
"eslint-plugin-n": "15.6.1",
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
|
@ -161,7 +161,7 @@
|
|||
"jest-junit": "15.0.0",
|
||||
"lint-staged": "13.1.0",
|
||||
"markdownlint-cli2": "0.6.0",
|
||||
"prettier": "2.8.2",
|
||||
"prettier": "2.8.3",
|
||||
"ts-jest": "29.0.5",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "4.9.4"
|
||||
|
|
|
@ -7,8 +7,6 @@ import { Router } from 'express';
|
|||
import asyncHandler from 'express-async-handler';
|
||||
const router: Router = Router();
|
||||
|
||||
import { getProviders } from '../../transitional';
|
||||
|
||||
import approvalsRoute from './approvals';
|
||||
import authorizationsRoute from './authorizations';
|
||||
import personalAccessTokensRoute from './personalAccessTokens';
|
||||
|
@ -23,23 +21,11 @@ router.use(asyncHandler(AddLinkToRequest));
|
|||
router.get(
|
||||
'/',
|
||||
asyncHandler(async (req: ReposAppRequest, res) => {
|
||||
const providers = getProviders(req);
|
||||
const link = req.individualContext.link;
|
||||
let legalContactInformation = null;
|
||||
try {
|
||||
if (providers.corporateContactProvider) {
|
||||
legalContactInformation = await providers.corporateContactProvider.lookupContacts(
|
||||
link.corporateUsername
|
||||
);
|
||||
}
|
||||
} catch (ignoredError) {
|
||||
/* ignored */
|
||||
}
|
||||
req.individualContext.webContext.render({
|
||||
view: 'settings',
|
||||
title: 'Settings',
|
||||
state: {
|
||||
legalContactInformation,
|
||||
link,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -40,33 +40,3 @@ block content
|
|||
|
||||
p
|
||||
a.btn.btn-muted.btn-sm(href='/link/remove') Remove link
|
||||
|
||||
h1 Business profile
|
||||
|
||||
if !legalContactInformation
|
||||
p No corporate profile information is available.
|
||||
|
||||
if legalContactInformation && legalContactInformation.legal
|
||||
h4 Legal contact
|
||||
p For legal advice you will want to refer to your team's primary legal contact:
|
||||
p: strong: a(href='mailto:' + legalContactInformation.legal)= legalContactInformation.legal
|
||||
|
||||
if legalContactInformation.attorney && legalContactInformation.attorney !== legalContactInformation.legal
|
||||
h4 Attorney
|
||||
p: strong: a(href='mailto:' + legalContactInformation.attorney)= legalContactInformation.attorney
|
||||
|
||||
if legalContactInformation.highRiskBusiness || legalContactInformation.lowRiskBusiness
|
||||
h4 Open source approval business reviewers
|
||||
p.
|
||||
Requests to release open source or to use certain components may
|
||||
be subject to a business review. The reviewer(s) associated with
|
||||
your corporate identity are:
|
||||
if legalContactInformation.group
|
||||
h5 Group
|
||||
p= legalContactInformation.group
|
||||
if legalContactInformation.lowRiskBusiness
|
||||
h5 Reviewer
|
||||
p= legalContactInformation.lowRiskBusiness
|
||||
if legalContactInformation.highRiskBusiness
|
||||
h5 High-risk reviewer
|
||||
p= legalContactInformation.highRiskBusiness
|
||||
|
|
Загрузка…
Ссылка в новой задаче