private async Task LoadLayout(ProcessorModel model, WorkItemTypeModel wit, AzureDevOpsEndpoint endpoint, string processId)
 {
     wit.Layout = (await endpoint.GetApiDefinitionAsync <WorkItemLayout>(new object[] { processId, wit.WorkItemType.ReferenceName }, queryForDetails: false));
     foreach (var page in wit.Layout.Pages)
     {
         var pageKey = $"{wit.WorkItemType.ReferenceName}::{page.Label}";
         if (model.WorkItemPages.ContainsKey(pageKey))
         {
             model.WorkItemPages[pageKey] = page;
         }
         else
         {
             model.WorkItemPages.Add(pageKey, page);
         }
         foreach (var section in page.Sections)
         {
             foreach (var group in section.Groups)
             {
                 var groupKey = $"{wit.WorkItemType.ReferenceName}::{page.Label}::{section.Id}::{group.Label}";
                 if (model.WorkItemGroups.ContainsKey(groupKey))
                 {
                     model.WorkItemGroups[groupKey] = group;
                 }
                 else
                 {
                     model.WorkItemGroups.Add(groupKey, group);
                 }
                 foreach (var control in group.Controls)
                 {
                     var controlKey = $"{wit.WorkItemType.ReferenceName}::{page.Label}::{section.Id}::{group.Label}::{control.Id}";
                     if (model.WorkItemControls.ContainsKey(controlKey))
                     {
                         model.WorkItemControls[controlKey] = control;
                     }
                     else
                     {
                         model.WorkItemControls.Add(controlKey, control);
                     }
                 }
             }
         }
     }
 }
 /// <summary>
 /// Retrieve the selected pipeline definitions from the Azure DevOps Endpoint for the <typeparamref name="DefinitionType"/> type.
 /// </summary>
 /// <typeparam name="DefinitionType">The type of Pipeline definition to query. The type must inherit from <see cref="RestApiDefinition"/>.</typeparam>
 /// <param name="endpoint">The <see cref="AzureDevOpsEndpoint"/> to query against.</param>
 /// <param name="definitionNames">The list of definitions to query for. If the value is <c>null</c> or an empty list, all definitions will be queried.</param>
 /// <returns></returns>
 private async Task <IEnumerable <DefinitionType> > GetSelectedDefinitionsFromEndpointAsync <DefinitionType>(AzureDevOpsEndpoint endpoint, List <string> definitionNames)
     where DefinitionType : RestApiDefinition, new()
 {
     IEnumerable <Task <IEnumerable <DefinitionType> > > GetDefinitionListTasks(AzureDevOpsEndpoint endpoint, List <string> definitionNames) =>
     definitionNames switch
     {
         null or {
             Count : 0
         } => new List <Task <IEnumerable <DefinitionType> > > {
        private async Task BuildModel(ProcessorModel model, AzureDevOpsEndpoint endpoint, bool warnOnMissing)
        {
            // Grab all the procs, then iterate over them looking for procs user has configured to be
            // sync'd. Then grab all Work Item Types for the given process and filter those by the ones user
            // wants to sync.

            Log.LogDebug($"Loading model for [{endpoint.Options.Name}].");

            var rootFields = (await endpoint.GetApiDefinitionsAsync <WorkItemField>(queryForDetails: false))
                             .ToDictionary(x => x.ReferenceName, x => x);

            if (endpoint.Options.Name == Source.Options.Name)
            {
                SourceFields = rootFields;
            }
            else
            {
                TargetFields = rootFields;
            }


            var procs = await endpoint.GetApiDefinitionsAsync <ProcessDefinition>();

            foreach (var processFilter in _Options.Processes.Keys)
            {
                string mappedProcName = processFilter;
                if (model == TargetModel && _Options.ProcessMaps.ContainsKey(processFilter))
                {
                    mappedProcName = _Options.ProcessMaps[processFilter];
                }

                var proc = procs.FirstOrDefault(p => processFilter == "*" ||
                                                p.Name.Equals(mappedProcName, StringComparison.OrdinalIgnoreCase));
                if (proc != null)
                {
                    //if (!isMapped) mappedProcName = proc.Name;
                    if (!model.ProcessDefinitions.ContainsKey(proc.Id))
                    {
                        model.ProcessDefinitions.Add(mappedProcName, new ProcessDefinitionModel()
                        {
                            Process       = proc,
                            MappedFrom    = processFilter,
                            WorkItemTypes = new List <WorkItemTypeModel>()
                        });
                    }

                    model.ProcessDefinitions[mappedProcName].WorkItemBehaviors = (await endpoint.GetApiDefinitionsAsync <WorkItemBehavior>(new object[] { proc.Id })).ToList();
                    model.ProcessDefinitions[mappedProcName].WorkItemBehaviors.ForEach(b => {
                        if (!model.WorkItemBehaviors.ContainsKey(b.Id))
                        {
                            model.WorkItemBehaviors.Add(b.Id, b);
                        }
                    });

                    #region Build Work Item Types data ...

                    var procWits = (await endpoint.GetApiDefinitionsAsync <WorkItemType>(new object[] { proc.Id }, singleDefinitionQueryString: "$expand=All"));
                    procWits = procWits.Where(x => _Options.Processes[processFilter].Any(a => a == "*" ||
                                                                                         x.Name.Equals(a, StringComparison.OrdinalIgnoreCase)));
                    if (procWits != null && procWits.Count() > 0)
                    {
                        foreach (var wit in procWits)
                        {
                            if (!model.WorkItemTypes.ContainsKey(wit.Id))
                            {
                                model.WorkItemTypes.Add(wit.Id, new WorkItemTypeModel()
                                {
                                    WorkItemType = wit
                                });
                                model.ProcessDefinitions[mappedProcName].WorkItemTypes.Add(model.WorkItemTypes[wit.Id]);
                            }
                            else
                            {
                                model.WorkItemTypes[wit.Id] = new WorkItemTypeModel()
                                {
                                    WorkItemType = wit
                                };
                            }

                            #region --- Loading Wit Type Details ---
                            await Task.WhenAll(
                                Task.Run(async() => await LoadLayout(model, model.WorkItemTypes[wit.Id], endpoint, proc.Id)),
                                Task.Run(async() =>
                            {
                                model.WorkItemTypes[wit.Id].Fields =
                                    (await endpoint.GetApiDefinitionsAsync <WorkItemTypeField>(new object[] { proc.Id, wit.Id }, singleDefinitionQueryString: "$expand=All")).ToList();
                                model.WorkItemTypes[wit.Id].Fields.ForEach(field => model.WorkItemFields.Add(field.Id, field));
                            }),
                                Task.Run(async() =>
                            {
                                model.WorkItemTypes[wit.Id].States =
                                    (await endpoint.GetApiDefinitionsAsync <WorkItemState>(new object[] { proc.Id, wit.Id })).ToList();
                                model.WorkItemTypes[wit.Id].States.ForEach(state => model.WorkItemStates.Add(state.Name, state));
                            }),
                                Task.Run(async() =>
                            {
                                model.WorkItemTypes[wit.Id].Rules =
                                    (await endpoint.GetApiDefinitionsAsync <WorkItemRule>(new object[] { proc.Id, wit.Id })).ToList();
                                model.WorkItemTypes[wit.Id].Rules.ForEach(rule => model.WorkItemRules.Add(rule.Id, rule));
                            }),
                                Task.Run(async() =>
                            {
                                model.WorkItemTypes[wit.Id].Behaviors =
                                    (await endpoint.GetApiDefinitionsAsync <WorkItemTypeBehavior>(new object[] { proc.Id, wit.Id })).ToList();
                                model.WorkItemTypes[wit.Id].Behaviors.ForEach(rule => {
                                    if (!model.WorkItemTypeBehaviors.ContainsKey(rule.Id))
                                    {
                                        model.WorkItemTypeBehaviors.Add(rule.Id, rule);
                                    }
                                });
                            })
                                );

                            #endregion
                        }
                    }
                    #endregion
                }
                else if (warnOnMissing)
                {
                    Log.LogWarning($"Unable to locate {processFilter} process in {endpoint.Options.Name} organization.");
                }
            }
        }