Framework/Abstracts/MetaInfoProvider.ps1

class MetaInfoProvider {
    hidden static [MetaInfoProvider] $metaInfoInstance = [MetaInfoProvider]::new()
    static [MetaInfoProvider] $Instance = [MetaInfoProvider]::GetInstance()

    hidden [bool] $bUseADOInfoAPI 
    hidden [string] $FuncAPI = '/api/getadoinfo'; 
    hidden [string] $code;
    hidden [string] $baseURL;
    hidden [PSObject] $ControlSettings; 
    
    hidden [PSObject] $buildSTDetails;
    hidden [PSObject] $releaseSTDetails;
    hidden [PSObject] $svcConnSTDetails;
    hidden [PSObject] $agtPoolSTDetails;
    hidden [PSObject] $varGroupSTDetails;
    hidden [PSObject] $serviceTreeDetails;
    
    #Variable to check whether ST file is present in policy, if ST file is not present then set them to false so for next resource don't call policy server to fetch this file
    hidden [bool] $checkBuildSTFileOnServer = $true;    
    hidden [bool] $checkReleaseSTFileOnServer = $true;
    hidden [bool] $checkServiceConnectionSTFileOnServer = $true;
    hidden [bool] $checkAgentPoolSTFileOnServer = $true;    
    hidden [bool] $checkVariableGroupSTFileOnServer = $true;    
    hidden [bool] $checkServiceTreeFileOnServer = $true;

    hidden MetaInfoProvider() {
        #Getting call only once and set bUseADOInfoAPI
        $this.IsADOInfoAPIEnabled();
    }

    #Return MetaInfoProvider instance
    hidden static [MetaInfoProvider] GetInstance() {
        return [MetaInfoProvider]::metaInfoInstance
    }

    #checking adoinfo api is enabled or not in org policy file
    [bool] IsADOInfoAPIEnabled()
    {
        if ($null -eq $this.ControlSettings)
        {
            $this.ControlSettings = [ConfigurationManager]::LoadServerConfigFile("ControlSettings.json");
        }
        $adoInfoAPI = $this.ControlSettings.ADOInfoAPI;
        if ($null -ne $adoInfoAPI -and $Env:AzSKADODoNotUseADOInfoAPI -ne $true)
        {
            #TODO
            #$adoInfoAPI.Enabled = $false;
            if ($adoInfoAPI.Enabled)
            {
                $this.bUseADOInfoAPI = $true;
                $this.code = $adoInfoAPI.Code;
                $this.baseURL = $adoInfoAPI.Endpoint;
            }
        }
        return $this.bUseADOInfoAPI;
    }

    #Calling adoinfo api and returning response
    [PSObject] CallADOInfoAPI($queryString )
    {
        $adoInfoInvokeURL = $this.baseURL + $this.FuncAPI + $queryString
        $Header = @{
            "x-functions-key" = $this.code;
        }
        $rsrcList = $null
        
        try 
        {
            $rsrcList = Invoke-RestMethod -Method 'GET' -Uri $adoInfoInvokeURL -Headers $Header
        }
        catch
        {
            Write-Host "Error calling ADO Info API. `r`nPlease contact your project's ADO security team." -ForegroundColor Red 
        }
        return $rsrcList;
    }
    
    #Fetching sesrvice id associated resources and internally calling adoinfo api if enabled else getting data from local org policy files
    [PSObject] FetchServiceAssociatedResources($svcId, $projectName, $resourceTypeName)
    {
        $rsrcList = $null;
        if ($this.bUseADOInfoAPI -eq $true)
        {
            #TODO: Look at cleaning up these multiple "-in" checks across the API_call-v-Policy_Repo cases...
            #TODO-PERF: For now we are erring on the side of avoiding multiple network calls...revisit based on observed pattern of -svcid <xyz> usage
            $qs = "?svcId={0}" -f $svcId
            $rsrcList = $this.CallADOInfoAPI($qs);
        }
        else 
        {
            $this.FetchMappingFiles($resourceTypeName);

            $buildList = @();
            $releaseList = @();
            $svcConnList = @();
            $varGroupList = @();
            $agentPoolList = @();

            if ($this.buildSTDetails) {
                $buildList += $this.buildSTDetails.Data | Where-Object { ($_.serviceId -eq $svcId) -and ($_.projectName -eq $projectName) }
            }
            if ($this.releaseSTDetails) {
                $releaseList += $this.releaseSTDetails.Data | Where-Object { ($_.serviceId -eq $svcId) -and ($_.projectName -eq $projectName) }
            }
            if ($this.svcConnSTDetails) {
                $svcConnList += $this.svcConnSTDetails.Data | Where-Object { ($_.serviceId -eq $svcId) -and ($_.projectName -eq $projectName) }
            }
            if ($this.agtPoolSTDetails) {
                $agentPoolList += $this.agtPoolSTDetails.Data | Where-Object { ($_.serviceId -eq $svcId) -and ($_.projectName -eq $projectName) }
            }
            if ($this.varGroupSTDetails) {
                $varGroupList += $this.varGroupSTDetails.Data | Where-Object { ($_.serviceId -eq $svcId) -and ($_.projectName -eq $projectName) }
            } 
            
            $rsrcList = @{
                Builds = $buildList
                Releases = $releaseList
                ServiceConnections = $svcConnList
                AgentPools = $agentPoolList
                VariableGroups = $varGroupList
            }

        }
        return $rsrcList; 
    }
    
    #Fetching service tree info details based on resource id and internally calling adoinfo api and loading resource file if enabled, else loading resource file from local org policy files
    [PSObject] FetchResourceMappingWithServiceData($rscId, $projectName, $resourceTypeName)
    {
        $serviceTreeInfo = $null;
        try 
        {
            #check if adoinfoapi is enabled in org-policy file
            if ($this.bUseADOInfoAPI -eq $true)
            {
                $qs = "?ResourceType=$resourceTypeName";
                #call adoinfoapi only if STDetails files is not already loaded.
                if ( ($resourceTypeName -eq "Build" -and !$this.buildSTDetails) -or ($resourceTypeName -eq "Release" -and !$this.releaseSTDetails) -or ($resourceTypeName -eq "ServiceConnection" -and !$this.svcConnSTDetails) -or ($resourceTypeName -eq "AgentPool" -and !$this.agtPoolSTDetails)  -or ($resourceTypeName -eq "VariableGroupp" -and !$this.varGroupSTDetails) ) {
                    $rsrcList = $this.CallADOInfoAPI($qs);
                    if ($rsrcList -and ( [Helpers]::CheckMember($rsrcList, "Data") -and $rsrcList.Data) ) {
                        $this.BindADOInfoAPIResponseToSTMappingFiles($rsrcList, $resourceTypeName);
                    }
                    #If not get files from adoinfoapi, take then from local org policy files.
                    #else {
                    # $this.FetchMappingFiles($resourceTypeName);
                    #}
                }
            }
            else 
            {
                $this.FetchMappingFiles($resourceTypeName);
            }

            $serviceTreeInfo = $this.GetServiceDataForResource($rscId, $resourceTypeName);
        }
        catch
        {
            Write-Host "Could not fetch service mapping files. `r`nPlease contact your project's ADO security team." -ForegroundColor Red 
        }
        return $serviceTreeInfo; 
    }

    #Binding adoinfo api response to class local variable
    hidden [void] BindADOInfoAPIResponseToSTMappingFiles($resourceList, $resourceTypeName)
    {
        if ($resourceTypeName -eq "Build") {
            $this.buildSTDetails = $resourceList;
        }
        elseif ($resourceTypeName -eq "Release") {
            $this.releaseSTDetails = $resourceList;
        }
        elseif ($resourceTypeName -eq "ServiceConnection") {
            $this.svcConnSTDetails = $resourceList;
        }
        elseif ($resourceTypeName -eq "AgentPool") {
            $this.agtPoolSTDetails = $resourceList;
        }
        elseif ($resourceTypeName -eq "VariableGroup") {
            $this.varGroupSTDetails = $resourceList;
        }
        elseif ($resourceTypeName -eq "ServiceTree") {
            $this.serviceTreeDetails = $resourceList;
        }
    }

    #Loading local org policy ST files based on supplied resource type,
    #1.Fetch ST files from policy only if ...STDetails variable is null (if not already fetch)
    #2.Do not fetch ST files again from policy, if already fetched and file is not present in policy server.
    [void] FetchMappingFiles($ResourceTypeName)
    {
        if ($ResourceTypeName -in ([ResourceTypeName]::Build, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_User))
        {
           if (!$this.buildSTDetails -and $this.checkBuildSTFileOnServer) {
                $this.buildSTDetails = [ConfigurationManager]::LoadServerConfigFile("BuildSTData.json");
                
                $this.checkBuildSTFileOnServer = $false;
            }    
        }

        if ($ResourceTypeName -in ([ResourceTypeName]::Release, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_User))
        {
            if (!$this.releaseSTDetails -and $this.checkReleaseSTFileOnServer) {
                $this.releaseSTDetails = [ConfigurationManager]::LoadServerConfigFile("ReleaseSTData.json");

                $this.checkReleaseSTFileOnServer = $false;
            }
        }

        if ($ResourceTypeName -in ([ResourceTypeName]::ServiceConnection, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_User))
        {
            if (!$this.svcConnSTDetails -and $this.checkServiceConnectionSTFileOnServer) {
                $this.svcConnSTDetails = [ConfigurationManager]::LoadServerConfigFile("ServiceConnectionSTData.json");
                
                $this.checkServiceConnectionSTFileOnServer = $false;
            }
        }
        if ($ResourceTypeName -in ([ResourceTypeName]::AgentPool, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_User))
        {
            if (!$this.agtPoolSTDetails -and $this.checkAgentPoolSTFileOnServer) {
                $this.agtPoolSTDetails = [ConfigurationManager]::LoadServerConfigFile("AgentPoolSTData.json");
                
                $this.checkAgentPoolSTFileOnServer = $false;
            }
        }

        if ($ResourceTypeName -in ([ResourceTypeName]::VariableGroup, [ResourceTypeName]::All))
        {
            if (!$this.varGroupSTDetails -and $this.checkVariableGroupSTFileOnServer) {
                $this.varGroupSTDetails = [ConfigurationManager]::LoadServerConfigFile("VariableGroupSTData.json");
                $this.checkVariableGroupSTFileOnServer = $false;
                
            }
        
        }
        
        if ($ResourceTypeName -eq "ServiceTree")
        {
            if (!$this.serviceTreeDetails -and $this.checkServiceTreeFileOnServer) {
                $this.serviceTreeDetails = [ConfigurationManager]::LoadServerConfigFile("ServiceTreeData.json");
                $this.checkServiceTreeFileOnServer = $false;
            }
        }
    }

    #Fetching service tree data based on resource id from ST data loaded in class variables
    hidden [PSObject] GetServiceDataForResource($rscId, $resourceTypeName)
    {
        $serviceTreeInfo = $null;
        if(($resourceTypeName -eq "Build") -and $this.buildSTDetails -and [Helpers]::CheckMember($this.buildSTDetails, "Data"))
        {
            $buildSTData = $this.buildSTDetails.Data | Where-Object { $_.buildDefinitionID -eq $rscId -and $_.projectName -eq $projectName }; 
            
            if ($buildSTData) 
            {
                $serviceTreeInfo = $this.GetDataFromServiceTree($buildSTData.serviceID);
            }
        }
        elseif(($resourceTypeName -eq "Release") -and $this.releaseSTDetails -and [Helpers]::CheckMember($this.releaseSTDetails, "Data"))
        {
            $releaseSTData = $this.releaseSTDetails.Data | Where-Object { $_.releaseDefinitionID -eq $rscId -and $_.projectName -eq $projectName}; 
            if ($releaseSTData) 
            {
                $serviceTreeInfo = $this.GetDataFromServiceTree($releaseSTData.serviceID);
            }
        }
        elseif(($resourceTypeName -eq "ServiceConnection") -and $this.svcConnSTDetails -and [Helpers]::CheckMember($this.svcConnSTDetails, "Data"))
        {
            $svcConnSTData = $this.svcConnSTDetails.Data | Where-Object { $_.serviceConnectionID -eq $rscId -and $_.projectName -eq $projectName}; 
            if ($svcConnSTData) 
            {
                $serviceTreeInfo = $this.GetDataFromServiceTree($svcConnSTData.serviceID);
            }
        }
        elseif(($resourceTypeName -eq "AgentPool") -and $this.agtPoolSTDetails -and [Helpers]::CheckMember($this.agtPoolSTDetails, "Data"))
        {
            $agtPoolSTData = $this.agtPoolSTDetails.Data | Where-Object { $_.agentPoolID -eq $rscId -and $_.projectName -eq $projectName}; 
            if ($agtPoolSTData) 
            {
                $serviceTreeInfo = $this.GetDataFromServiceTree($agtPoolSTData.serviceID);
            }
        }
        elseif(($resourceTypeName -eq "VariableGroup") -and $this.varGroupSTDetails -and [Helpers]::CheckMember($this.varGroupSTDetails, "Data"))
        {
            $varGroupSTData = $this.varGroupSTDetails.Data | Where-Object { $_.variableGroupID -eq $rscId -and $_.projectName -eq $projectName}; 
            if ($varGroupSTData) 
            {
                $serviceTreeInfo = $this.GetDataFromServiceTree($varGroupSTData.serviceID);
            }
        }

        return $serviceTreeInfo;
    }

    #Fetching Service tree info data based on service id from service tree mapping file
    hidden [PSObject] GetDataFromServiceTree($serviceId) 
    {
        $serviceTreeInfo = $null;        
        if (!$this.serviceTreeDetails) 
        {
            if ($this.bUseADOInfoAPI -eq $true) {
                $qs = "?ResourceType=ServiceTree";
                $rsrcList = $this.CallADOInfoAPI($qs);
                if ($rsrcList -and [Helpers]::CheckMember($rsrcList, "Data") -and $rsrcList.Data) {
                    $this.BindADOInfoAPIResponseToSTMappingFiles($rsrcList, "ServiceTree");
                }
                #If not get file from adoinso api, get it from local org policy file.
                #else {
                # $this.FetchMappingFiles("ServiceTree");
                #}
            }
            else {
                $this.FetchMappingFiles("ServiceTree");
            }  
        }
        if ($this.serviceTreeDetails -and [Helpers]::CheckMember($this.serviceTreeDetails, "Data")) {
            $serviceTreeInfo = $this.serviceTreeDetails.Data | Where-Object { $_.serviceID -eq $serviceId };
        }
        return $serviceTreeInfo;
    }
}


# SIG # Begin signature block
# MIIhewYJKoZIhvcNAQcCoIIhbDCCIWgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA8LCMFn6oguX8/
# 0cdwAQyV2UD2NHHMWdBSDxFtGnE4qKCCC28wggTrMIID06ADAgECAhMzAAAD53EW
# vSG3L5ZCAAAAAAPnMA0GCSqGSIb3DQEBCwUAMHkxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBUZXN0aW5nIFBD
# QSAyMDEwMB4XDTIwMDMwNDE5NTgzOVoXDTIxMDMwMzE5NTgzOVowfDELMAkGA1UE
# BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
# BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdQ29kZSBTaWdu
# IFRlc3QgKERPIE5PVCBUUlVTVCkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
# AoIBAQC0dFU8yYFFisft2syLgnrgoEcOrrzraGs5owrAJ8YWyYuxhhk7UUJP0YAC
# wuDAlPQMHrhnEhZsqmD7DfWGzz33gxe7hvcNpHdhItPpgXiVkh3thZrWz4jfHFGc
# RMW1zyebGUJ16gN5cYWsI18Pax9tBZW1YZIef2hIQNU5Vr5QhVKZVAbaqZFqJRo+
# 51czrP44ZnofEMr3Z3HBmIS7C97kkFYS/G8JpkufIuDsTchX7dWduHhMbFIem+Zx
# nT7mrsps0D5hXV3L9JPe8TFm1T0iwaFy6RWFaWPelibrTryIbWk6Qrv4Lz89WMM6
# XFxlrqQVphAmhns1+rNrr6yacRCtAgMBAAGjggFnMIIBYzATBgNVHSUEDDAKBggr
# BgEFBQcDAzAdBgNVHQ4EFgQUseZoPiUpJDttlBAhnIzqzbcXsK4wUAYDVR0RBEkw
# R6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNv
# MRYwFAYDVQQFEw0yMzAwNzIrNDU4Mzk0MB8GA1UdIwQYMBaAFN3WR4sjFC/YOGhC
# oz5tw/CQ9yzQMFMGA1UdHwRMMEowSKBGoESGQmh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Rlc1BDQV8yMDEwLTA3LTAxLmNybDBX
# BggrBgEFBQcBAQRLMEkwRwYIKwYBBQUHMAKGO2h0dHA6Ly93d3cubWljcm9zb2Z0
# LmNvbS9wa2kvY2VydHMvTWljVGVzUENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB
# /wQCMAAwDQYJKoZIhvcNAQELBQADggEBAJYdTCu6GLf0F8qu4JuKidCt6hweTHFz
# 012VGqDoVNN8REwov3VMjK71y8oL6wgvx29RYYqD2sKn6a/NcKUlHJjttvbXW/Az
# NK4FetsfpyURFCRTS8C5hRcGZTIZfiSsJXn0N/yV/pbf/M6N4c0Q//I5f+e5lMch
# 0jf6TGVLEHcXgOOH1PcS4Rd9LjAaggJG7VAOrIQaoSfgtsMn/a0CoYXeigizHb4k
# sZW2nEC5JSAZ49b3Y1Pjvtr1H6xfMewXwtGCEvTq2btl8in/TV8du5cimL7VmZAa
# aggJr0eFOmLCNUgGhH+Ic+sLH7G7vpkdggW9PRQ0wtQm8ofUIYhIn2swggZ8MIIE
# ZKADAgECAgphEYRvAAAAAAADMA0GCSqGSIb3DQEBCwUAMIGQMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTowOAYDVQQDEzFNaWNyb3NvZnQgVGVz
# dGluZyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIx
# MjMwMVoXDTI1MDcwMTIxMzMwMVoweTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjEjMCEGA1UEAxMaTWljcm9zb2Z0IFRlc3RpbmcgUENBIDIwMTAw
# ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBvSHVS2YGAJIwORjKy3NC
# WbHvmyeo4OhVvSmw+SQfOtHow1mJ7ZG2wegzY/ZaZBniLnwMkIAFOL8cproNai/v
# J5er3vbvUPOD59fDRTciPxi1wpYRto0Sg1mLJ1EGVnW5YGoTDtUmPy2WqgXMoYc/
# vk807wxMb8wE1KHmZ80KJzOf46+bb2h8vLQMczSMWoH5h/tUHMVHbOqfV7RZ/c4Z
# qXd8h0KftXmUvMt2ktuWl6FfBCQ5/qGV4Z+G417ZXFbfQ5CfyRTq0fWgW6vzCATd
# KK8b4qouE6AK7dKZRCr1mUT7K6RP8bthwh0t9SUnAqh475M59F51ge7S4HYMWyPv
# AgMBAAGjggHsMIIB6DAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU3dZHiyMU
# L9g4aEKjPm3D8JD3LNAwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0P
# BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUowEEfjCIM+u5MZzK
# 64V2Z/xltNEwWQYDVR0fBFIwUDBOoEygSoZIaHR0cDovL2NybC5taWNyb3NvZnQu
# Y29tL3BraS9jcmwvcHJvZHVjdHMvTWljVGVzUm9vQ2VyQXV0XzIwMTAtMDYtMTcu
# Y3JsMF0GCCsGAQUFBwEBBFEwTzBNBggrBgEFBQcwAoZBaHR0cDovL3d3dy5taWNy
# b3NvZnQuY29tL3BraS9jZXJ0cy9NaWNUZXNSb29DZXJBdXRfMjAxMC0wNi0xNy5j
# cnQwgaAGA1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIB
# FjFodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQu
# aHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8A
# UwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBAYQU+N90z
# e1LCSGuA92ADFrbJLol+vdlYHGPT9ZLS9loEDQjuh7/rmDQ6ZXxQ5MgbKVB5VXsY
# OZG1QSbzF3+YlWd8TN1y5B21BM3DSPb6r+3brl50kW2t1JqACtiLbZnmhmh5hkdJ
# i8HYUfLQ7xKcP0g1CIJP9CyKil7UJv/HnMXKigTGiBaHjfVtVwG5k8roymrEirpB
# DcOMVB0OZiTXxYIHDbM4v7LItZYIISdPs6+LwxwzwdroMdpj42+3dWQBumpRGQAg
# qJ9i5UiBQtUM+9vLpKIRnujnWfQxbaIuIt2HRLFpHUYKGOXRlf148o+71dX3YWap
# 88+ocaxkM8rkavgDNkcWSe9Dpoq8a3tS2P9BpxewDV+iSzF0JRo9UOZeciaSQDZv
# rkQskxJjtdO725L6E5Fu1Ti+lGl6exRCnhPbooxCqHEGLRdiwXkrmLp+huTGAK8z
# mfEt0d1JFrrDdu5kqoG3OVT2dN4JVFNpOFvCU/LNiVDCyCIcG0cSRVtDjyNckMhu
# 1PcPtberjr1mcL8RkTzvonoH4pIvQk1k4IOLpdxslOj2oigApZjqCBJA3mIEZHln
# wRuglg4Er74nSmL6953C0r1Vwl7T0vXnQO8izb+incAb1r6Y+45N5aVXww+PqHJB
# RjvhjyBKG+1aDLVM3ixjV9P6OZkOvp4uozGCFWIwghVeAgEBMIGQMHkxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29m
# dCBUZXN0aW5nIFBDQSAyMDEwAhMzAAAD53EWvSG3L5ZCAAAAAAPnMA0GCWCGSAFl
# AwQCAQUAoIGuMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC
# AQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCDXxTFE7I408ga3F1ju
# AyZ3fI4/XdL1fbirTvvtxKcuVzBCBgorBgEEAYI3AgEMMTQwMqAUgBIATQBpAGMA
# cgBvAHMAbwBmAHShGoAYaHR0cDovL3d3dy5taWNyb3NvZnQuY29tMA0GCSqGSIb3
# DQEBAQUABIIBAIhJidEno9TTCxlfU3vnmieNfygyqrXbToPsAisEAdjg8ODyMC9j
# q4PiQcBWtwPBvRRTYRk8v7m1LXtVZMmuwdX5z2Iyilgzg2/FBEPRvqS9iDbj+EXw
# feliBQV5Amd/uLmIjbOu/Zxkl4AkgTokGZZXbYB4V4h9fSZfag60vOiMp2jCYIpp
# d1hBdNZWHOqK4AlzjREiORo0rE3kuvxyrF4oKRWDl4arqhmCL4e117iSR05J7Zbd
# qimZAJa+y72hnlZI2uUN5DnWIrokhClRQWwHN3c0+JOJKDNgCpJLIDzFY59O2Xe8
# zhs+kZ3CXkH4ae0saTl3zZnnaVT2yS6goAShghLxMIIS7QYKKwYBBAGCNwMDATGC
# Et0wghLZBgkqhkiG9w0BBwKgghLKMIISxgIBAzEPMA0GCWCGSAFlAwQCAQUAMIIB
# VQYLKoZIhvcNAQkQAQSgggFEBIIBQDCCATwCAQEGCisGAQQBhFkKAwEwMTANBglg
# hkgBZQMEAgEFAAQgMOLmtDzU1Hd5WUcWtxzD29sp9ICeenFQSRi3BOd7k54CBl+7
# 6PTx2RgTMjAyMDExMjcxMTQ4NTMuMjU5WjAEgAIB9KCB1KSB0TCBzjELMAkGA1UE
# BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
# BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0
# IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO
# OkQ5REUtRTM5QS00M0ZFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNloIIORDCCBPUwggPdoAMCAQICEzMAAAEtLk1BymNlM6AAAAAAAS0wDQYJ
# KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMTkx
# MjE5MDExNTA0WhcNMjEwMzE3MDExNTA0WjCBzjELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9wZXJhdGlvbnMg
# UHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQ5REUtRTM5QS00
# M0ZFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqWGN9HVJOphFNLezSLiMhxIxX4bg
# 3ShiDCVAr6mXgNUwWavWJNkcUuPdO3tXoX4z8FfHSH2LS67XIGeoKo06S4kRFNtW
# czi7cm9HHOxB8KMF+oP8I3Cgw16SjKUgHPE/nRKSOnWk+ydAEodoI/y2C69hXOhN
# DnirZWlgQ//45hNB4Q+pYWLxaCC+xyS56txQaSFhYzwNX/glTs3+tsOu1qZht7wu
# 2RWJNBhuKBRIICiv0OG0Bm+rwDQDvdcUfZ1/bAOUu0CcoJyxW9dKZnflsCqd43i4
# RBXLw1B1F4YjW0jpTGgLteeMa8rgxwN0qFq80nsMCdI/n4b8NOR1YP6U3wIDAQAB
# o4IBGzCCARcwHQYDVR0OBBYEFNlsZHxCASH4Tg6K/y9DvjTynYbNMB8GA1UdIwQY
# MBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6
# Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0YVBD
# QV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0
# dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3RhUENBXzIw
# MTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgw
# DQYJKoZIhvcNAQELBQADggEBAH1rA2T3Tqt5wG6g7sTOrsHxQa70SgVeIvxpzRtu
# xkMFj3P/enxq1VlELEc3jdrdYZsIxmgpjFIEfpQKRwNzBch80oRtUzOcXWaOlAQZ
# uqFH6s82oddmi1JX6+fzkDGZ58Azdtwu35Y0GkS45lniQ9lVwW0yjhjJCPGg1E+L
# Can4HQeSFCz+X9UiDRcljqTkVEoT8kURxVjjbno12pRn7eOi7dvg4CS0Ta6uvhXp
# VHDV9986XFlvwzK8Tmaq9NUk5K1XEK6G6qDWsh7yVrWBnmKK4jJZ5+0418zDSdJl
# yEYuKAN4ifDpd/DCAWcLlCXw9t/aM7EJSW4BhvPKQj4ycGIwggZxMIIEWaADAgEC
# AgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEG
# A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj
# cm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0
# aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0yNTA3MDEy
# MTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD
# VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAk
# BgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjANBgkqhkiG
# 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RUENWlCgCC
# hfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBED/FgiIRU
# QwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50YWeRX4FU
# sc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd/XcfPfBX
# day9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaRtogINeh4
# HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQABo4IB5jCC
# AeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8RhvFM2ha
# hW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNV
# HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYG
# A1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3Js
# L3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcB
# AQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kv
# Y2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSABAf8EgZUw
# gZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3dy5taWNy
# b3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEFBQcCAjA0
# HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBtAGUAbgB0
# AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Ehb7Prpsz1
# Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7uVOMzPRg
# Eop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqRUgCvOA8X
# 9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9Va8v/rbl
# jjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8+n99lmqQ
# eKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+Y1klD3ou
# OVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh2rBQHm+9
# 8eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRyzR30uIUB
# HoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoouLGp25ay
# p0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx16HSxVXj
# ad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341Hgi62jbb
# 01+P3nSISRKhggLSMIICOwIBATCB/KGB1KSB0TCBzjELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9wZXJhdGlv
# bnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQ5REUtRTM5
# QS00M0ZFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMK
# AQEwBwYFKw4DAhoDFQCfzl/Hfod7sXS+CbJSXPbDzaXQsaCBgzCBgKR+MHwxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv
# c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA42sEmzAi
# GA8yMDIwMTEyNzA4NTI0M1oYDzIwMjAxMTI4MDg1MjQzWjB3MD0GCisGAQQBhFkK
# BAExLzAtMAoCBQDjawSbAgEAMAoCAQACAhQrAgH/MAcCAQACAhEnMAoCBQDjbFYb
# AgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSCh
# CjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAKwk1/YotO75Mv6D0BgcnW5AQ
# KmpzP7esbPWiJtWfUZ1B8uH4hxZF4gB/UrUecNqdvfyHbXoa1c4u6jgjjIGDhHP4
# wxSclab950EK0Lm9/DJzCl5tkbNcl39KChVXzcPJlh+vnOdAEg66OPmG9tcGaSGH
# C9BBlgHeO//XwfC3kwExggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEG
# A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj
# cm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFt
# cCBQQ0EgMjAxMAITMwAAAS0uTUHKY2UzoAAAAAABLTANBglghkgBZQMEAgEFAKCC
# AUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCDz
# tThEfzrBJApaj11Tinj/AY/J4NFgZAUzAU3b8LHKfjCB+gYLKoZIhvcNAQkQAi8x
# geowgecwgeQwgb0EII7xWnJyfSAHj+KVEA88NtL4KZuqP+4LTXWahzmh4YPBMIGY
# MIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG
# A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAEtLk1BymNl
# M6AAAAAAAS0wIgQgV9aZPOiPwyNAn0sH5EOD+mAUOEsPF+Qi1bNjOLkb2UIwDQYJ
# KoZIhvcNAQELBQAEggEALU8KyE8v6DsA/46OL0RUpzrH3in9BZcib8lFL5BeMEK/
# yVct/qOYlpLLAJxafF03WkvT6TXPqZ60XzVy0YqUg0nmRMJTSf/pR0yBwhbf04rs
# RQkgWfnUIBimMdR0ghsQFEoREEyuuCzhRTS8cwMknl8cuf7QqkL73FCUlFu6+ydk
# SXbdmHDC1abROyT/X359xL2VtbFE7KQBEwO/JOM1NaYqQgfH42Ay61oGl1xoh0UQ
# hWHrO8+/1Hz2waPi3FoOjbXUByEcfFWGOGBIMYtAauHZByFt0nXgPUlXwXxlqi/5
# IdLlLyt7tYISsDw/sHZc2DEjY5QKYxtJGuQNZFSO3g==
# SIG # End signature block