async function processTeam()

in jobs/reports/teams.ts [147:302]


async function processTeam(context: IReportsContext, team: Team) {
  console.log('team: ' + context.processing.teams.remaining-- + ': ' + team.organization.name + '/' + team.name);
  try {
    const teamContext: IReportsTeamContext = {
      parent: context,
      definitionsUsed: new Set(),
      issues: {},
      name: team.name,
      nameLowercase: team.name.toLowerCase(),
      team: team,
    };
    if (!context.teamData) {
      context.teamData = [];
    }
    context.teamData.push(teamContext);
    await getTeamDetails(teamContext);
    const maintainers = await identifyTeamMaintainers(teamContext, team);
    await gatherLinkData(teamContext, maintainers);
    await identityMaintainersWithoutLinks(teamContext, maintainers);
    const organization = team.organization;
    // We do not provide reports for private engineering orgs for now
    const privateEngineering = organization.privateEngineering;
    // Some organizations, such as the .NET Foundation, will allow external community members to
    // be organization members
    const externalMembersPermitted = organization.externalMembersPermitted;
    const slug = team.slug;
    const basicTeam: IReportsBasicTeam = {
      teamName: team.name,
      teamSlug: slug,
      entityName: team.name,
      orgName: organization.name,
      id: team.id.toString(),
    };
    const orgName = team.organization.name;
    // Recipients
    teamContext.recipients = [];
    const corporateAdministrators = [];
    if (teamContext.maintainers) {
      for (let y = 0; y < teamContext.maintainers.length; y++) {
        const admin = teamContext.maintainers[y];
        if (admin && admin.link && admin.link.aadupn) {
          corporateAdministrators.push(admin.link.aadupn);
          if (!privateEngineering) {
            // Private engineering orgs do not send individuals nags on emails for now
            teamContext.recipients.push({
              type: 'upn',
              value: admin.link.aadupn,
              reasons: transformReasonsToArray(admin, team.name, orgName),
            });
          }
        }
      }
    }
    // Send to org admins
    const orgData = context.organizationData[orgName];
    for (let i = 0; orgData && orgData.organizationContext && orgData.organizationContext.recipients && orgData.organizationContext.recipients.length && i < orgData.organizationContext.recipients.length; i++) {
      teamContext.recipients.push(orgData.organizationContext.recipients[i]);
    }
    basicTeam.maintainers = teamContext.maintainers && teamContext.maintainers.length ? teamContext.maintainers.length.toString() : 'None';
    const actionPromoteMembers = {
      link: `https://github.com/orgs/${orgName}/teams/${slug}/members`,
      text: 'Promote members',
    };
    const actionRemoveMembers = {
      link: `https://github.com/orgs/${orgName}/teams/${slug}/members`,
      text: 'Remove members',
    };
    const actionDelete = {
      link: `https://github.com/orgs/${orgName}/teams/${slug}/edit`,
      text: 'Consider deleting',
    };
    const actionView = {
      link: `https://github.com/orgs/${orgName}/teams/${slug}/members`,
      text: 'Open',
    };
    const actionViewInPortal = context.config.urls ? {
      link: `${context.config.urls.repos}${organization.name}/teams/${slug}`,
      text: 'Manage team',
    } : null;
    let createdAt = team.created_at ? moment(team.created_at) : null;
    if (createdAt) {
      basicTeam.created = createdAt.format(simpleDateFormat);
    }
    let updatedAt = team.updated_at ? moment(team.updated_at) : null;
    if (updatedAt) {
      basicTeam.updated = updatedAt.format(simpleDateFormat);
    }
    let mostRecentActivityMoment = createdAt;
    let mostRecentActivity = 'Created';
    if (updatedAt && updatedAt.isAfter(mostRecentActivityMoment)) {
      mostRecentActivity = 'Updated';
      mostRecentActivityMoment = updatedAt;
    }
    // Completely empty standard teams (we exclude system teams that may be used for specific portal permissions)
    const systemTeamIds = team.organization.systemTeamIds;
    const isSystemTeam = systemTeamIds.includes(team.id);
    if (team.members_count == 0 && team.repos_count == 0 && !isSystemTeam) {
      addEntityToIssueType(context, teamContext, 'emptyTeams', basicTeam, actionDelete);
    } else {
      // Member or maintainer issues
      if (!isSystemTeam && team.members_count == 0) {
        addEntityToIssueType(context, teamContext, 'noTeamMembers', basicTeam, actionDelete, actionViewInPortal);
      } else if (!isSystemTeam && teamContext.teamMaintainers.length === 0) {
        addEntityToIssueType(context, teamContext, 'noTeamMaintainers', basicTeam, actionPromoteMembers, actionViewInPortal);
      } else if (teamContext.maintainersByType.unlinked.length > 0) {
        const logins = [];
        for (let z = 0; z < teamContext.maintainersByType.unlinked.length; z++) {
          const unlinkedEntry = teamContext.maintainersByType.unlinked[z];
          logins.push(unlinkedEntry.login);
        }
        const specialEntity = Object.assign({
          logins: logins.join(', '),
        }, basicTeam);
        const reportName = externalMembersPermitted ? 'unlinkedMaintainersWhenAllowed' : 'unlinkedMaintainers';
        // TODO: use the operations e-mail
        addEntityToIssueType(context, teamContext, reportName, specialEntity, actionRemoveMembers, {
          link: `mailto:opensource@microsoft.com?subject=Reporting a former employee related to the ${orgName} ${team.name} team`,
          text: 'Former employee?',
        });
      }
      // No repositories
      if (team.repos_count <= 0 && !isSystemTeam) {
        addEntityToIssueType(context, teamContext, 'TeamsWithoutRepositories', basicTeam, actionViewInPortal);
      }
    }
    const thisWeek = moment().subtract(7, 'days');
    const today = moment().subtract(1, 'days');
    const ageInMonths = today.diff(createdAt, 'months');
    if (ageInMonths > 0) {
      basicTeam.ageInMonths = ageInMonths === 1 ? '1 month' : ageInMonths + ' months';
    }
    const monthsSinceUpdates = today.diff(mostRecentActivityMoment, 'months');
    const timeAsString = monthsSinceUpdates + ' month' + (monthsSinceUpdates === 1 ? '' : 's');
    basicTeam.recentActivity = monthsSinceUpdates < 1 ? 'Active' : `${timeAsString} (${mostRecentActivity})`;
    if (createdAt.isAfter(thisWeek) && !privateEngineering) {
      // New public and private repos
      const teamForManagerAndLawyer = shallowCloneWithAdditionalRecipients(basicTeam, teamContext.additionalRecipients);
      if (createdAt.isAfter(today)) {
        addEntityToIssueType(context, teamContext, 'NewTeamsToday', teamForManagerAndLawyer, actionView, actionViewInPortal);
      }
      // Always include in the weekly summary
      addEntityToIssueType(context, teamContext, 'NewTeamsWeek', teamForManagerAndLawyer, actionView, actionViewInPortal);
    }
    if (context.settings.teamDelayAfter) {
      await sleep(context.settings.teamDelayAfter);
    }
    return context;
  } catch (problem) {
    if (ErrorHelper.IsNotFound(problem)) {
      // Missing teams are OK to not spew too many errors about...
    } else {
      console.dir(problem);
    }
    throw problem;
  }
}