private async Task EnsureSynchronizedNotificationTeamIsChild(WebApiTeam parent, WebApiTeam child, bool persistChanges)
        {
            var parentDescriptor = await service.GetDescriptorAsync(parent.Id);

            var childDescriptor = await service.GetDescriptorAsync(child.Id);

            var isInTeam = await service.CheckMembershipAsync(parentDescriptor, childDescriptor);

            logger.LogInformation("Child In Parent ParentId = {0}, ChildId = {1}, IsInTeam = {2}", parent.Id, child.Id, isInTeam);

            if (!isInTeam)
            {
                logger.LogInformation("Adding Child Team");

                if (persistChanges)
                {
                    await service.AddToTeamAsync(parentDescriptor, childDescriptor);
                }
            }
        }
        private async Task SyncTeamWithCodeOwnerFile(BuildDefinition pipeline, WebApiTeam team, GitHubToAADConverter gitHubToAADConverter, GitHubService gitHubService, bool persistChanges)
        {
            using (logger.BeginScope("Team Name = {0}", team.Name))
            {
                if (pipeline.Process.Type != PipelineYamlProcessType)
                {
                    return;
                }

                // Get contents of CODEOWNERS
                logger.LogInformation("Fetching CODEOWNERS file");
                Uri repoUrl = pipeline.Repository.Url;

                if (repoUrl != null)
                {
                    repoUrl = new Uri(Regex.Replace(repoUrl.ToString(), @"\.git$", String.Empty));
                }
                else
                {
                    logger.LogError("No repository url returned from pipeline. Repo id: {0}", pipeline.Repository.Id);
                    return;
                }
                var codeOwnerEntries = await gitHubService.GetCodeownersFile(repoUrl);

                if (codeOwnerEntries == default)
                {
                    logger.LogInformation("CODEOWNERS file not found, skipping sync");
                    return;
                }
                var process = pipeline.Process as YamlProcess;

                logger.LogInformation("Searching CODEOWNERS for matching path for {0}", process.YamlFilename);

                var codeOwnerEntry = CodeOwnersFile.FindOwnersForClosestMatch(codeOwnerEntries, process.YamlFilename);
                codeOwnerEntry.FilterOutNonUserAliases();

                logger.LogInformation("Matching Contacts Path = {0}, NumContacts = {1}", process.YamlFilename, codeOwnerEntry.Owners.Count);

                // Get set of team members in the CODEOWNERS file
                var codeownersDescriptors = new List <String>();
                foreach (var contact in codeOwnerEntry.Owners)
                {
                    if (!codeOwnerCache.ContainsKey(contact))
                    {
                        // TODO: Better to have retry if no success on this call.
                        var userPrincipal = gitHubToAADConverter.GetUserPrincipalNameFromGithub(contact);
                        if (!string.IsNullOrEmpty(userPrincipal))
                        {
                            codeOwnerCache[contact] = await service.GetDescriptorForPrincipal(userPrincipal);
                        }
                        else
                        {
                            logger.LogInformation("Cannot find the user principal for github {0}", contact);
                            codeOwnerCache[contact] = null;
                        }
                    }
                    codeownersDescriptors.Add(codeOwnerCache[contact]);
                }


                var codeownersSet = new HashSet <string>(codeownersDescriptors);
                // Get set of team members in the DevOps teams
                var teamMembers = await service.GetMembersAsync(team);

                var teamDescriptors = new List <String>();
                foreach (var member in teamMembers)
                {
                    if (!teamMemberCache.ContainsKey(member.Identity.Id))
                    {
                        var teamMemberDescriptor = (await service.GetUserFromId(new Guid(member.Identity.Id))).SubjectDescriptor.ToString();
                        teamMemberCache[member.Identity.Id] = teamMemberDescriptor;
                    }
                    teamDescriptors.Add(teamMemberCache[member.Identity.Id]);
                }
                var teamSet          = new HashSet <string>(teamDescriptors);
                var contactsToRemove = teamSet.Except(codeownersSet);
                var contactsToAdd    = codeownersSet.Except(teamSet);

                foreach (var descriptor in contactsToRemove)
                {
                    if (persistChanges && descriptor != null)
                    {
                        var teamDescriptor = await service.GetDescriptorAsync(team.Id);

                        logger.LogInformation("Delete Contact TeamDescriptor = {0}, ContactDescriptor = {1}", teamDescriptor, descriptor);
                        await service.RemoveMember(teamDescriptor, descriptor);
                    }
                }

                foreach (var descriptor in contactsToAdd)
                {
                    if (persistChanges && descriptor != null)
                    {
                        var teamDescriptor = await service.GetDescriptorAsync(team.Id);

                        logger.LogInformation("Add Contact TeamDescriptor = {0}, ContactDescriptor = {1}", teamDescriptor, descriptor);
                        await service.AddToTeamAsync(teamDescriptor, descriptor);
                    }
                }
            }
        }