functions/vsts.ps1
# Functions and variables used for communication with AzureDevOps $script:cached_HttpClient = $null $script:cached_accountProjectMap = @{} $script:projectsUrl = "https://{0}.visualstudio.com/defaultcollection/_apis/projects?api-version=1.0" $script:gitReposUrl = "https://{0}.visualstudio.com/defaultcollection/{1}/_apis/git/repositories?api-version=1.0" $script:identityUrl = "https://{0}.visualstudio.com/defaultcollection/_api/_identity/CheckName?name={1}" $script:pullRequestUrl = "https://{0}.visualstudio.com/defaultcollection/_apis/git/repositories/{1}/pullRequests?api-version=1.0-preview.1" $script:openPullRequestUrl = "https://{0}.visualstudio.com/defaultcollection/{1}/_git/{2}/pullrequest/{3}" $script:buildDefinitionsUrl = "https://{0}.visualstudio.com/defaultcollection/{1}/_apis/build/definitions?name={2}&type={3}&`$top=1&api-version=2.0" $script:buildsUrlWithFilters ="https://{0}.visualstudio.com/defaultcollection/{1}/_apis/build/builds?definitions={2}&type={3}&`$top={4}&{5}api-version=2.0" $script:codeCoverageUrl = "https://{0}.visualstudio.com/defaultcollection/{1}/_apis/test/CodeCoverage?buildId={2}" $script:buildArtifactUrl = "https://{0}.visualstudio.com/defaultcollection/{1}/_apis/build/builds/{2}/artifacts?artifactName={3}&api-version=4.1" $script:runQueryUrl = "https://{0}.visualstudio.com/defaultcollection/{1}/_apis/wit/wiql?api-version=1.0" $script:getWorkItemsUrl = "https://{0}.visualstudio.com/defaultcollection/_apis/wit/workitems?ids={1}&fields=System.Id,System.Title,System.WorkItemType,System.AssignedTo,System.CreatedBy,System.ChangedBy,System.CreatedDate,System.ChangedDate,System.State&api-version=1.0" $script:openWorkItemUrl= "https://{0}.visualstudio.com/defaultcollection/_workitems/edit/{1}" # Override urls to run against a local TFS server if($PsAzureDevOps.OnPremiseMode) { $script:projectsUrl = "http://{0}:8080/tfs/defaultcollection/_apis/projects?api-version=1.0" $script:gitReposUrl = "http://{0}:8080/tfs/defaultcollection/{1}/_apis/git/repositories?api-version=1.0" $script:identityUrl = "http://{0}:8080/tfs/defaultcollection/_api/_identity/CheckName?name={1}" $script:pullRequestUrl = "http://{0}:8080/tfs/defaultcollection/_apis/git/repositories/{1}/pullRequests?api-version=1.0-preview.1" $script:openPullRequestUrl = "http://{0}:8080/tfs/defaultcollection/{1}/_git/{2}/pullrequest/{3}" $script:buildDefinitionsUrl = "http://{0}:8080/tfs/defaultcollection/{1}/_apis/build/definitions?name={2}&type={3}&`$top=1&api-version=2.0" $script:buildsUrlWithFilters ="http://{0}:8080/tfs/defaultcollection/{1}/_apis/build/builds?definitions={2}&type={3}&`$top={4}&{5}api-version=2.0" $script:codeCoverageUrl = "http://{0}:8080/tfs/defaultcollection/{1}/_apis/test/CodeCoverage?buildId={2}" $script:buildArtifactUrl = "http://{0}:8080/tfs/defaultcollection/{1}/_apis/build/builds/{2}/artifacts?artifactName={3}&api-version=4.1" $script:runQueryUrl = "http://{0}:8080/tfs/defaultcollection/{1}/_apis/wit/wiql?api-version=1.0" $script:getWorkItemsUrl= "http://{0}:8080/tfs/defaultcollection/_apis/wit/workitems?ids={1}&fields=System.Id,System.Title,System.WorkItemType,System.AssignedTo,System.CreatedBy,System.ChangedBy,System.CreatedDate,System.ChangedDate,System.State&api-version=1.0" $script:openWorkItemUrl= "http://{0}:8080/tfs/defaultcollection/_workitems/edit/{1}" } $script:stateExcludeFilterQueryPart = "AND ([System.State] NOT IN ({0}))" $script:stateIncludeFilterQueryPart = "AND ([System.State] IN ({0}))" $script:identityFilterQueryPart = " [{0}] = @me " $script:getMyWorkItemsQuery = "SELECT [System.Id] FROM WorkItems WHERE ([System.TeamProject] = @project) AND ([System.ChangedDate] > '{0}') {1} AND ({2}) ORDER BY [{3}] DESC,[System.Id] DESC" function openWorkItemInBrowser($account, $workItemId) { $webWorkItemUrl = [System.String]::Format($script:openWorkItemUrl, $account, $workItemId) Start-Process $webWorkItemUrl } function getWorkItemsFromIds($account, $wiIds) { $wiIdString = $wiIds -join "," $workItemsUrl = [System.String]::Format($script:getWorkItemsUrl, $account, $wiIdString) $workItemsResult = getUrl $workItemsUrl if($workItemsResult){ return $workItemsResult.value } } function getWorkItemsFromQuery($account, $project, $query, $take) { $queryUrl = [System.String]::Format($script:runQueryUrl, $account, $project) $payload = @{ "query" = $query } $queryResults = postUrl $queryUrl $payload if(-not $queryResults) { return $null } # The ids of the workitems in sorted order $resultIds = $queryResults.workItems.id | Select-Object -First $take if($resultIds) { $workItems = getWorkItemsFromIds $account $resultIds if($workItems) { # We need to sort the results by the query results since # work items rest call doesn't honor order $workItemMap = @{} $workItems | ForEach-Object { $workItemMap[$_.Id] = $_ } $sortedWorkItems = $resultIds | ForEach-Object { $workItemMap[$_] } return $sortedWorkItems } } } function getBuilds($account, $project, $definition, $type, $take, $filters) { $getBuildDefinitionUrl = [System.String]::Format($script:buildDefinitionsUrl, $account, $project, $definition, $type) $definitionResult = getUrl $getBuildDefinitionUrl if($definitionResult.value) { $getBuildUrl = [System.String]::Format($script:buildsUrlWithFilters, $account, $project, $definitionResult.value.id, $type, $take, $filters) $buildResults = getUrl $getBuildUrl if($buildResults) { return $buildResults.value } } return $null } function getBuildCodeCoverage($account, $project, $definition, $type) { $buildResult = getBuilds $account $project $definition $type 1 "status=completed&resultFilter=succeeded&" if ($buildResult) { $getCodeCoverageUrl = [System.String]::Format($script:codeCoverageUrl, $account, $project, $buildResult.id) $codeCoverageResults = getUrl $getCodeCoverageUrl if ($codeCoverageResults -and $codeCoverageResults.coverageData -and $codeCoverageResults.coverageData.coverageStats) { for ($ct = 0; $ct -lt $codeCoverageResults.coverageData.coverageStats.Length; $ct++) { $currentItem = $codeCoverageResults.coverageData.coverageStats[$ct] $currentItem | Add-Member 'coverage' ([math]::Round((100 * [int] $currentItem.covered[0]) / [int] $currentItem.total[0], 2)) $currentItem | Add-Member 'build' $buildResult.buildNumber } return $codeCoverageResults.coverageData.coverageStats } } return $null } function getBuildArtifact($account, $project, $definition, $artifactName, $type) { $buildResult = getBuilds $account $project $definition $type 1 "status=completed&resultFilter=succeeded&" if ($buildResult) { $getBuildArtifactUrl = [System.String]::Format($script:buildArtifactUrl, $account, $project, $buildResult.id, $artifactName) $buildArtifactResults = getUrl $getBuildArtifactUrl if ($buildArtifactResults) { return $buildArtifactResults.resource } } return $null } function createRepo($account, $project, $repo) { $projectId = getProjectId $account $project $payload = @{ "name" = $repoName "project" = @{ "id" = $projectId } } $url = [System.String]::Format($script:gitReposUrl, $account, $project) $repoResults = postUrl $url $payload if($repoResults) { return $repoResults } } function getRepos($account, $project) { $url = [System.String]::Format($script:gitReposUrl, $account, $project) $repoResults = getUrl $url if($repoResults) { return $repoResults.value } else { return $null } } function getRepoId($account, $project, $repository) { $repos = getRepos $account $project $repos = @($repos | Where-Object { $_.name -eq $repository }) if($repos.Count -le 0){ throw "Unable to find repository id for a repository named $repository" } return $repos[0].id } function getProjectId($account, $project) { # Check in the cache first for this account/project $projectId = getProjectIdFromCache $account $project # Check if a cache miss call the service and try again if(-not $projectId) { buildProjectMap $account $projectId = getProjectIdFromCache $account $project } if(-not $projectId) { throw "Unable to find the project $project in account $account" } return $projectId } function getProjectIdFromCache($account, $project) { # Check in the cache first for this account/project $projectId = $null $projectIdMap = $script:cached_accountProjectMap[$account] if($projectIdMap) { $projectId = $projectIdMap[$project] } return $projectId } function getProjects($account) { $url = [System.String]::Format($script:projectsUrl, $account) $projectResults = getUrl $url if($projectResults) { return $projectResults.value } else { return $null } } function getIdentityId($account, $name) { $url = [System.String]::Format($script:identityUrl, $account, $name) try { $identityResult = getUrl $url } catch { } if($identityResult -and $identityResult.Identity.TeamFoundationId) { return $identityResult.Identity.TeamFoundationId } else { Write-Warning "Unable to resolve the name $name" return $null } } function buildProjectMap($account) { $projectResults = getProjects $account if($projectResults) { $projectIdMap = @{} $projectResults | ForEach-Object { $projectIdMap[$_.name] = $_.id } $script:cached_accountProjectMap[$account] = $projectIdMap } else { Write-Error "Unable to get projects for $account" } } function postUrl($urlStr, $payload) { Write-Progress -Activity "Making REST Call" -Status "POST $urlStr" traceMessage "POST $urlStr" $payloadString = ConvertTo-Json $payload traceMessage "payload: $payloadString" $content = New-Object System.Net.Http.StringContent($payloadString, [System.Text.Encoding]::UTF8, "application/json") $httpClient = getHttpClient $url = New-Object System.Uri($urlStr) $response = $httpClient.PostAsync($urlStr, $content).Result return processRestReponse $response } function getUrl($urlStr) { Write-Progress -Activity "Making REST Call" -Status "GET $urlStr" traceMessage "GET $urlStr" $httpClient = getHttpClient $url = New-Object System.Uri($urlStr) $response = $httpClient.GetAsync($urlStr).Result return processRestReponse $response } function processRestReponse($response) { $result = $response.Content.ReadAsStringAsync().Result try { if($result){ $obj = ConvertFrom-Json $result traceMessage "REST RESPONSE: $obj" } } catch { } if($response.IsSuccessStatusCode) { return $obj } else { # TODO: Handle errors from the server throw "Recieved an error code of $($response.StatusCode) from the server" } } function getHttpClient() { if($script:cached_HttpClient){ return $script:cached_HttpClient; } $credentials = New-Object Microsoft.VisualStudio.Services.Client.VssClientCredentials $credentials.Storage = New-Object Microsoft.VisualStudio.Services.Client.VssClientCredentialStorage("VssApp", "VisualStudio") $requestSettings = New-Object Microsoft.VisualStudio.Services.Common.VssHttpRequestSettings $messageHandler = New-Object Microsoft.VisualStudio.Services.Common.VssHttpMessageHandler($credentials, $requestSettings) $httpClient = New-Object System.Net.Http.HttpClient($messageHandler) $httpClient.Timeout = [System.TimeSpan]::FromSeconds($PsAzureDevOps.TimeoutInSeconds) $httpClient.DefaultRequestHeaders.Add("User-Agent", "PsAzureDevOps/1.0"); $script:cached_HttpClient = $httpClient return $httpClient } |