async function refresh()

in jobs/refreshUsernames/index.ts [19:157]


async function refresh({ providers }: IReposJob): Promise<IReposJobResult> {
  const { operations, insights, config, linkProvider, graphProvider } = providers;

  console.log('reading all links');
  const allLinks = shuffle(await linkProvider.getAll());
  console.log(`READ: ${allLinks.length} links`);
  insights.trackEvent({ name: 'JobRefreshUsernamesReadLinks', properties: { links: String(allLinks.length) } });

  let errors = 0;
  let notFoundErrors = 0;
  let errorList = [];

  let updates = 0;
  let updatedUsernames = 0;
  let updatedAvatars = 0;
  let updatedAadNames = 0;
  let updatedCorporateMails = 0;
  let updatedAadUpns = 0; // should be super rare

  const userDetailsThroatCount = 1;
  const secondsDelayAfterError = 5;
  const secondsDelayAfterSuccess = 0.25;

  const maxAgeSeconds = 24 * 60 * 60; // details can be a day out-of-date

  const throttle = throat(userDetailsThroatCount);
  let i = 0;
  await Promise.all(allLinks.map(link => throttle(async () => {
    ++i;

    // Refresh GitHub username for the ID
    let id = link.thirdPartyId;
    const account = operations.getAccount(id);
    let changed = false;
    try {
      try {
        const refreshOptions = {
          maxAgeSeconds,
          backgroundRefresh: false,
        };
        const details = await account.getDetails(refreshOptions);

        if (details.login && link.thirdPartyUsername !== details.login) {
          insights.trackEvent({ name: 'JobRefreshUsernamesUpdateLogin', properties: { old: link.thirdPartyUsername, new: details.login } });
          link.thirdPartyUsername = details.login;
          changed = true;
          ++updatedUsernames;
        }

        if (details.avatar_url && link.thirdPartyAvatar !== details.avatar_url) {
          link.thirdPartyAvatar = details.avatar_url;
          changed = true;
          ++updatedAvatars;
        }
      } catch (githubError) {
        console.dir(githubError);
      }

      try {
        const graphInfo = await graphProvider.getUserById(link.corporateId);
        if (graphInfo) {
          if (graphInfo.userPrincipalName && link.corporateUsername !== graphInfo.userPrincipalName) {
            link.corporateUsername = graphInfo.userPrincipalName;
            changed = true;
            ++updatedAadUpns;
          }
          if (graphInfo.displayName && link.corporateDisplayName !== graphInfo.displayName) {
            link.corporateDisplayName = graphInfo.displayName;
            changed = true;
            ++updatedAadNames;
          }
          if (graphInfo.mail && link.corporateMailAddress !== graphInfo.mail) {
            link.corporateMailAddress = graphInfo.mail;
            changed = true;
            ++updatedCorporateMails;
          }
          if (graphInfo.mailNickname && link.corporateAlias !== graphInfo.mailNickname.toLowerCase()) {
            link.corporateAlias = graphInfo.mailNickname.toLowerCase();
            changed = true;
          }
        }
      } catch (graphLookupError) {
        // Ignore graph lookup issues, other jobs handle terminated employees
        console.dir(graphLookupError);
      }

      if (changed) {
        await linkProvider.updateLink(link);
        console.log(`${i}/${allLinks.length}: Updates saved for GitHub user ID ${id}`);
        ++updates;
      }
    } catch (getDetailsError) {
      if (getDetailsError.status == /* loose compare */ '404') {
        ++notFoundErrors;
        insights.trackEvent({ name: 'JobRefreshUsernamesNotFound', properties: { githubid: id, error: getDetailsError.message } });
        try {
          await operations.terminateLinkAndMemberships(id, { purpose: UnlinkPurpose.Deleted });
          insights.trackEvent({ name: 'JobRefreshUsernamesUnlinkDelete', properties: { githubid: id, error: getDetailsError.message } });
        } catch (unlinkDeletedAccountError) {
          console.dir(unlinkDeletedAccountError);
          insights.trackException({ exception: unlinkDeletedAccountError, properties: { githubid: id, event: 'JobRefreshUsernamesDeleteError' } });
        }
      } else {
        console.dir(getDetailsError);
        ++errors;
        insights.trackException({ exception: getDetailsError, properties: { name: 'JobRefreshUsernamesError' } });
        errorList.push(getDetailsError);
        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(`Updates: ${updates}`);
  console.log(`GitHub username changes: ${updatedUsernames}`);
  console.log(`GitHub avatar changes: ${updatedAvatars}`);
  console.log(`AAD name changes: ${updatedAadNames}`);
  console.log(`AAD username changes: ${updatedAadUpns}`);
  console.log(`Updated corporate mails: ${updatedCorporateMails}`);

  return {
    successProperties: {
      updates,
      updatedUsernames,
      updatedAvatars,
      updatedAadNames,
      updatedAadUpns,
      updatedCorporateMails,
      errors,
    },
  };
}