PSGitLab.psm1

##########################
# Public Functions
##########################
Function New-GitLabBuild {
[cmdletbinding()]
param(
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName="Default",Mandatory=$true)]
    [Parameter(ParameterSetName="Variable",Mandatory=$true)]
    [int]$ProjectId,
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName="Default",Mandatory=$true)]
    [Parameter(ParameterSetName="Variable",Mandatory=$true)]
    [string]$Reference,
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName="Default",Mandatory=$true)]
    [Parameter(ParameterSetName="Variable",Mandatory=$true)]
    [string]$token,
    [Parameter(ParameterSetName="Variable",Mandatory=$true)]
    [string]$Variable,
    [Parameter(ParameterSetName="Variable",Mandatory=$true)]
    [string]$Value
)
    $LF = "`r`n"
    $uri = "http://gitlab.columbia.csc/api/v3/projects/$ProjectId/trigger/builds"
    $boundary = [System.Guid]::NewGuid().ToString()

    $bodyLines =
        "--$boundary$LF" +
        "Content-Disposition: form-data; name=`"token`"$LF" +
        "Content-Type: 'multipart/form-data'$LF$LF" +
        "$token$LF" +
        "--$boundary$LF" +
        "Content-Disposition: form-data; name=`"ref`"$LF" +
        "Content-Type: 'multipart/form-data'$LF$LF" +
        "$Reference$LF"

    if ($Variable) {
        $Variable = "variables[$Variable]"
        $bodyLines +=
            ("--$boundary$LF" +
            "Content-Disposition: form-data; name=`"$Variable`"$LF" +
            "Content-Type: multipart/form-data$LF$LF" +
            "$Value$LF")
    }
    $bodyLines += "--$boundary--$LF"

    try {
        $result = Invoke-RestMethod -Uri $uri -Method Post -ContentType "multipart/form-data; boundary=`"$boundary`"" -Body $bodyLines
        Write-Debug $result
        Write-Output "Build trigger sent successfully"
    }
    catch {
        Write-Output ($x.ErrorDetails.Message | convertfrom-json).message
    }
}


Function Get-GitLabSetting {
    [cmdletbinding()]
    [OutputType('GitLab.Setting')]
    param(
    )
    $Request = @{
        URI="/application/settings";
        Method='Get';
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Setting'

}


Function Get-GitLabVersion
{
    [OutputType('GitLab.Version')]
    [cmdletbinding()]
    param(
    )

BEGIN {}

PROCESS {
    Write-Verbose ( $PSBoundParameters | ConvertTo-Json )

    $Request = @{
        URI = "/version"
        Method = 'GET'
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Version'
}

END {}

}


Function Save-GitLabAPIConfiguration {
[cmdletbinding()]
param(
    [Parameter(Mandatory=$true,
               HelpMessage='You can find the token in your profile.',
               Position=0)]
    [ValidateNotNullOrEmpty()]
    $Token,

    [Parameter(Mandatory=$true,
               HelpMessage='Please provide a URI to the GitLab installation',
               Position=1)]
    [ValidateNotNullOrEmpty()]
    [ValidatePattern("^(?:http|https):\/\/(?:[\w\.\-\+]+:{0,1}[\w\.\-\+]*@)?(?:[a-z0-9\-\.]+)(?::[0-9]+)?(?:\/|\/(?:[\w#!:\.\?\+=&%@!\-\/\(\)]+)|\?(?:[\w#!:\.\?\+=&%@!\-\/\(\)]+))?$")]
    $Domain,

    $APIVersion = 4
)

if ( $Domain -match '^http:\/\/' ) {
    Write-Warning "All tokens will be sent over HTTP. Recommendation: Use HTTPS."
}

if ( $IsWindows -or ( [version]$PSVersionTable.PSVersion -lt [version]"5.99.0" ) ) {

    $Parameters = @{
        Token=(ConvertTo-SecureString -string $Token -AsPlainText -Force)
        Domain=$Domain;
        APIVersion=$APIVersion;
    }

    $ConfigFile = "$env:appdata\PSGitLab\PSGitLabConfiguration.xml"

} elseif ( $IsLinux -or $IsMacOS ) {

    Write-Warning "Warning: Your GitLab token will be stored in plain-text on non-Windows platforms."

    $Parameters = @{
        Token=$Token
        Domain=$Domain;
        APIVersion=$APIVersion;
    }

    $ConfigFile = "{0}/.psgitlab/PSGitLabConfiguration.xml" -f $HOME

} else {
    Write-Error "Unknown Platform"
}
if (-not (Test-Path (Split-Path $ConfigFile))) {
    New-Item -ItemType Directory -Path (Split-Path $ConfigFile) | Out-Null

}
$Parameters | Export-Clixml -Path $ConfigFile
Remove-Variable Parameters
}


Function Test-GitLabAPI {
    param(
    [Parameter(Mandatory=$false)]
    [string]$Version = 'v3'
)
    $GitLabConfig = ImportConfig

    if ($GitLabConfig.APIVersion) { $Version = "v$($GitLabConfig.APIVersion)" }

    $Domain = $GitLabConfig.Domain

    if ( $isWindows -or ( [version]$PSVersionTable.PSVersion -lt [version]"5.99.0") ) {

        $Token = DecryptString -Token $GitLabConfig.Token
    } elseif ( $isLinux  -or $IsMacOS ) {
        $Token = $GitLabConfig.Token
    }
    
    try {
        #https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netcore-2.0#System_Net_SecurityProtocolType_SystemDefault
        if ($PSVersionTable.PSVersion.Major -lt 6 -and [Net.ServicePointManager]::SecurityProtocol -notmatch 'Tls12') {
            [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::Tls12
        }
    }
    catch {
        Write-Warning -Message 'Adding TLS 1.2 to supported security protocols was unsuccessful.'
    }

    $Result = Invoke-WebRequest -UseBasicParsing -Uri "$Domain/api/$Version/projects?private_token=$Token"
    Remove-Variable Token
    GetGitLabStatusCode $Result.StatusCode
}


Function Get-GitLabGroup
{
  [cmdletbinding(DefaultParameterSetName = 'Groups')]
  [OutputType("GitLab.Group")]
  param(

    [Parameter(ParameterSetName = 'Single',
      Mandatory = $true)]
    [int]$Id,

    [Parameter(Mandatory = $false,
      ParameterSetName = 'Groups',
      HelpMessage = 'Return only archived groups')]
    [Parameter(Mandatory = $false,
      ParameterSetName = 'Owned',
      HelpMessage = 'Return only archived groups')]
    [Parameter(Mandatory = $false,
      ParameterSetName = 'All',
      HelpMessage = 'Return only archived groups')]
    [Parameter(Mandatory = $false,
      ParameterSetName = 'Starred',
      HelpMessage = 'Return only archived groups')]
    [switch]$Archived = $false,

    [Parameter(Mandatory = $false,
      HelpMessage = 'Limit by visibility',
      ParameterSetName = 'Groups')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Limit by visibility',
      ParameterSetName = 'Owned')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Limit by visibility',
      ParameterSetName = 'All')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Limit by visibility',
      ParameterSetName = 'Starred')]
    [ValidateSet("public", "internal", "private", "none")]
    $Visibility = 'none',

    [Parameter(Mandatory = $false,
      HelpMessage = 'Choose the order in which groups are returned.',
      ParameterSetName = 'Groups')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Choose the order in which groups are returned.',
      ParameterSetName = 'Owned')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Choose the order in which groups are returned.',
      ParameterSetName = 'All')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Choose the order in which groups are returned.',
      ParameterSetName = 'Starred')]
    [ValidateSet('id', 'name', 'path')]
    $Order_by = 'name',


    [Parameter(Mandatory = $false,
      HelpMessage = 'Ascending or Descending',
      ParameterSetName = 'Groups')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Ascending or Descending',
      ParameterSetName = 'Owned')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Ascending or Descending',
      ParameterSetName = 'All')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Ascending or Descending',
      ParameterSetName = 'Starred')]
    [ValidateSet('asc', 'desc')]
    $Sort = 'desc',

    [Parameter(Mandatory = $false,
      HelpMessage = 'Search for a project.',
      ParameterSetName = 'Groups')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Search for a project.',
      ParameterSetName = 'Owned')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Search for a project.',
      ParameterSetName = 'All')]
    [Parameter(Mandatory = $false,
      HelpMessage = 'Search for a project.',
      ParameterSetName = 'Starred')]
    $Search,

    [Parameter(ParameterSetName = 'Owned',
      Mandatory = $true)]
    [switch]$Owned,

    [Parameter(ParameterSetName = 'All',
      Mandatory = $true)]
    [switch]$All,

    [Parameter(ParameterSetName = 'Starred',
      Mandatory = $true)]
    [switch]$Starred

  )

  if ($PSCmdlet.ParameterSetName -ne 'Single')
  {
    Write-Verbose "Create GET Request"
    $GetUrlParameters = @()
    if ($archived)
    {
      $GetUrlParameters += @{archived = 'true'}
    }

    if ($search -ne $null)
    {
      $GetUrlParameters += @{search = $search}
    }
    $GetUrlParameters += @{order_by = $order_by}
    $GetUrlParameters += @{sort = $sort}
    $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters
    #$Request.URI = "$($Request.URI)" + "$URLParameters"
  }


  $Request = @{
    URI    = ''
    Method = 'GET'
  }

  Write-Verbose "Parameter Set Name: $($PSCmdlet.ParameterSetName)"

  switch ($PSCmdlet.ParameterSetName)
  {
    Groups { $Request.URI = "/groups$URLParameters"; break; }
    Owned { $Request.URI = "/groups/owned$URLParameters"; break; }
    All { $Request.URI = "/groups/all$URLParameters"; break; }
    Starred { $Request.URI = "/groups/starred$URLParameters"; break; }
    Single { $Request.URI = "/groups/$Id"; break; }
    default { Write-Error "Incorrect parameter set."; break; }

  }

  QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Group'

}


Function Get-GitLabIssue {
    [cmdletbinding(DefaultParameterSetName='User')]
    [OutputType('GitLab.Issue')]
    param(
        [Parameter(ParameterSetName='GroupID')]
        [Int]$GroupID,

        [Parameter(ParameterSetName='User')]
        [Parameter(ParameterSetName='GroupID')]
        [ValidateSet('All','CreatedByMe','AssignedToMe')]
        [String]$Scope,
 
        [Parameter(ParameterSetName='User')]
        [Parameter(ParameterSetName='GroupID')]
        [ValidateSet("opened", "closed")]
        [String]$State 
    )
    
    $Request = @{
        URI = '/issues'
        Method = 'GET'
    }

    # https://docs.gitlab.com/ee/api/issues.html

    $GetUrlParameters = @()
    
    if($State)
    {
        $GetUrlParameters += @{state=$State}
    }

    if($Scope)
    {
        # Set the expected API value for the scope. Kebab case will change in Gitlab 11.
        if($Scope -eq 'CreatedByMe') { 
            $GetUrlParameters += @{scope='created-by-me'} 
        }
        elseif($Scope -eq 'AssignedToMe') {
            $GetUrlParameters += @{scope='assigned-to-me'}
        }
        elseif($Scope -eq 'all') {
            $GetUrlParameters += @{scope='all'}
        }
        else {
        }
    }
    else {
        # Defaults to issues 'created' by the user.
    }

    $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters
        
    switch($PSCmdlet.ParameterSetName) {
        'GroupID' { $Request.URI = "/groups/$GroupID/issues$URLParameters"; break; }
        'User' { $Request.URI = "/issues$URLParameters"; break; }
    }
 
    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Issue'
}


Function Get-GitLabUserKey {

    [cmdletbinding(DefaultParameterSetName='All')]
    [OutputType('GitLab.User.Key')]
    param(
        [Parameter(ParameterSetName='All')]
        [switch]$All,

        [Parameter(ParameterSetName='Key')]
        [int]$Key,

        [Parameter(ParameterSetName='Username')]
        [string]$Username,

        [Parameter(ParameterSetName='UserID')]
        [int]$UserId

    )

    $Request = @{
        URI="";
        Method='Get';
    }

    if ( $PSCmdlet.ParameterSetName -eq 'Username' ) {
        $UserID = Get-GitLabUser -Username $Username | Select-Object -ExpandProperty Id -First 1
    }

    switch ( $PSCmdlet.ParameterSetName) {
        'Key' { $Request.URI = "/user/keys/$Key" }
        'All' { $Request.URI = "/user/keys/" }
        'Username' { $Request.URI = "/users/$UserID/keys" }
        'UserID' { $Request.URI = "/users/$UserID/keys" }
    }


    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User.Key'
}


Function New-GitLabUserKey {
    [cmdletbinding(DefaultParameterSetName='Explicit')]
    [OutputType('GitLab.User.Key')]
    param(
        [Parameter(ParameterSetName='Explicit')]
        [string]$Title,

        [Parameter(ParameterSetName='Explicit',Mandatory=$true)]
        [string]$Key,

        [Parameter(ParameterSetName='File',Mandatory=$true)]
        [string]$KeyFile = $null,

        [Parameter(Mandatory=$false)]
        $Username = $null
    )

    if ( $PSBoundParameters.ContainsKey('Username') ) {
        try {
            $User = Get-GitLabUser -Username $Username
        } catch {
            Write-Error "Unable to find user"
        }
        $URI = '/users/{0}/keys' -f $User.ID
    } else {
        $URI = "/user/keys"
    }

    if ( $PSCmdlet.ParameterSetName -eq 'File' ) {
        $Contents = Get-Content -Path $KeyFile
        $Title = ($Contents -split " ")[2]
        $Key = "{0} {1}" -f ($Contents -split " ")[0],($Contents -split " ")[1]

        Write-Verbose "Title: $Title Key: $Key"
    }

    if ($Title -eq $null) {
        Write-Error "Title could not be determined."
    }

    $Body = @{
        title= $Title
        key = $Key
    }

    $Request = @{
        URI=$URI;
        Method='POST'
        Body = $Body
    }



    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User.Key'
}


Function Push-SSHKeysToGitLab {
    [cmdletbinding(SupportsShouldProcess=$true,ConfirmImpact='High')]
    param(
        [ValidateScript({ Test-Path $_ })]
        $SSHDirectory = "~/.ssh/"
    )

    $PublicKeyFiles = Get-Childitem -Recurse -Path $SSHDirectory -Include "*.pub"

    if ($PublicKeyFiles.count -gt 0 ) {

        $PublicKeyFiles | ForEach-Object {
            if ( $PSCmdlet.ShouldProcess("Push SSH Key $_ to GitLab Instance") ) {
                Write-Verbose "Uploading $($_.Fullname)"
                New-GitLabUserKey -KeyFile $_.FullName
            }
        }

    } else {
        Write-Warning "No Public Keys Found"
    }
}


Function Remove-GitLabUserKey {
    [cmdletbinding(SupportsShouldProcess=$true)]
    [OutputType('GitLab.User.Key')]
    param(
        [Parameter(ParameterSetName='Id',Mandatory=$true)]
        [Parameter(ParameterSetName='User',Mandatory=$true)]
        [string]$Id,

        [Parameter(ParameterSetName='User',Mandatory=$true)]
        [string]$Username,

        [switch]$Passthru

    )

    $Request = @{
        URI='';
        Method='DELETE';
    }

    switch ( $PSCmdlet.ParameterSetName ) {
        'User' {
            $UserId = (Get-GitLabUser -Username $Username).Id
            $Request.URI = "/users/$UserId/keys/$Id"
        }
        'Id' {
            $Request.URI = "/user/keys/$Id"
        }
    }


    if ( $PSCmdlet.ShouldProcess("Delete SSH Key $Id") ) {
        $Results = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User.Key'
        if ( $Passthru ) {
            $Results
        }
    }
}


Function Close-GitLabMergeRequest {
    [cmdletbinding()]
    param(
        [Alias('project_id')]
        [ValidateNotNullOrEmpty()]
        [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
        [string]$ProjectId,

        [ValidateNotNullOrEmpty()]
        [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
        [string[]]$ID,

        [switch]$Passthru
    )

BEGIN {}

PROCESS {

    foreach ( $MergeRequestID in $ID ) {
        $Results = Set-GitLabMergeRequest -Project $ProjectId -Id $ID -StateEvent close

        if ( $Passthru.isPresent ) {
            $Results
        }
    }
}

END {}

}


Function Get-GitLabMergeRequest {
[cmdletbinding(DefaultParameterSetName='MergeRequests')]
[OutputType('GitLab.MergeRequest')]
param (
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName='Single',Mandatory)]
    [Parameter(ParameterSetName='MergeRequests',Mandatory)]
    [string]$ProjectId,

    [Parameter(ParameterSetName='Single',Mandatory)]
    [string]$Id,

    [Parameter(ParameterSetName='MergeRequests')]
    # merge request iid
    [string]$Iid,

    [Parameter(ParameterSetName='MergeRequests')]
    # possible values: all, merged, opened, closed
    [string]$State,

    [Parameter(ParameterSetName='MergeRequests')]
    # possible values: created_at (default) and updated_at
    [string]$OrderBy,

    [Parameter(ParameterSetName='MergeRequests')]
    # possible values: asc and desc (default)
    [string]$Sort

)
    $Project = Get-GitlabProject -Id $ProjectId;

    if($PSCmdlet.ParameterSetName -ne 'Single') {
        $GetUrlParameters = @()

        if ($Iid) {
            $GetUrlParameters += @{iid=$Id}
        }
        if ($State) {
            $GetUrlParameters += @{state=$State}
        }
        if ($OrderBy) {
            $GetUrlParameters += @{order_by=$OrderBy}
        }
        if ($Sort) {
            $GetUrlParameters += @{sort=$Sort}
        }

        $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters
    }


    $Request = @{
        URI = ''
        Method = 'GET'
    }

    Write-Verbose "Parameter Set Name: $($PSCmdlet.ParameterSetName)"

    switch ($PSCmdlet.ParameterSetName) {
        MergeRequests { $Request.URI="/projects/$($Project.id)/merge_requests$URLParameters"; break; }
        Single { $Request.URI="/projects/$($Project.id)/merge_requests/$Id"; break; }
        default { Write-Error "Incorrect parameter set."; break; }
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.MergeRequest'
}


Function New-GitLabMergeRequest {
    [cmdletbinding()]
    param(
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory)]
        [string]$ProjectId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory)]
        [string]$SourceBranch,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory)]
        [string]$TargetBranch,

        [int]$AssigneeId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory)]
        [string]$Title,

        [string]$Description,

        [string]$TargetProjectId,

        [string]$Labels,

        [string]$MilestoneId,

        [switch]$RemoveSourceBranch
    )

    $Project = $Project = Get-GitlabProject -Id $ProjectId;

    $GetUrlParameters = @()
    $GetUrlParameters += @{source_branch=$SourceBranch}
    $GetUrlParameters += @{target_branch=$TargetBranch}
    $GetUrlParameters += @{title=$Title}

    if ($AssigneeId) {
        $GetUrlParameters += @{assignee_id=$AssigneeId}
    }
    if ($Description) {
        $GetUrlParameters += @{description=$Description}
    }
    if ($TargetProjectId) {
        $GetUrlParameters += @{target_project_id=$TargetProjectId}
    }
    if ($Labels) {
        $GetUrlParameters += @{labels=$Labels}
    }
    if ($MilestoneId) {
        $GetUrlParameters += @{milestone_id=$MilestoneId}
    }
    if ($RemoveSourceBranch) {
        $GetUrlParameters += @{remove_source_branch=$true}
    }

    $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters

    $Request = @{
        URI="/projects/$($Project.id)/merge_requests$URLParameters";
        Method='POST';
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.MergeRequest'
}


Function Remove-GitLabMergeRequest {
[cmdletbinding(SupportsShouldProcess,ConfirmImpact='High')]
param(
    [Alias('project_id')]
    [ValidateNotNullOrEmpty()]
    [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
    [string]$ProjectId,

    [ValidateNotNullOrEmpty()]
    [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
    [string]$Id
)
    BEGIN {}

    PROCESS {
        $Project = $Project = Get-GitlabProject -Id $ProjectId;

        $Request = @{
            URI="/projects/$($Project.id)/merge_requests/$Id";
            Method='Delete';
        }

        $MergeRequest = Get-GitLabMergeRequest -Project $ProjectId -Id $Id

        if ($PSCmdlet.ShouldProcess($MergeRequest.Title, 'Delete Merge Request')) {
            $Worked = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.MergeRequest'
        }

    }

    END {}
}


Function Set-GitLabMergeRequest {
    [cmdletbinding()]
    param(
        [Alias('project_id')]
        [ValidateNotNullOrEmpty()]
        [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
        [string]$ProjectId,

        [ValidateNotNullOrEmpty()]
        [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
        [string[]]$ID,

        [string]$TargetBranch,

        [string]$AssigneeId,

        [string]$Title,

        [string]$Description,

        [ValidateSet("close", "reopen", "merge")]
        [string]$StateEvent,

        [string]$Labels,

        [string]$MilestoneId,

        [switch]$Passthru

    )

BEGIN {}

PROCESS {

    foreach ( $MergeRequestID in $ID ) {
        $Project = $Project = Get-GitlabProject -Id $ProjectId;

        $MergeRequest = Get-GitLabMergeRequest -ProjectId $ProjectId -Id $MergeRequestID

        Write-Verbose "Project Name: $($Project.Name), Merge Request Name: $($MergeRequest.Name)"

        $GetUrlParameters = @()

        if ($TargetBranch) {
            $GetUrlParameters += @{target_branch=$TargetBranch}
        }
        if ($AssigneeId) {
            $GetUrlParameters += @{assignee_id=$AssigneeId}
        }
        if ($Title) {
            $GetUrlParameters += @{title=$Title}
        }
        if ($Description) {
            $GetUrlParameters += @{description=$Description}
        }
        if ($StateEvent) {
            $GetUrlParameters += @{state_event=$StateEvent}
        }
        if ($Labels) {
            $GetUrlParameters += @{labels=$Labels}
        }
        if ($MilestoneId) {
            $GetUrlParameters += @{milestone_id=$MilestoneId}
        }

        $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters

        $Request = @{
            URI = "/projects/$($Project.ID)/merge_requests/$($MergeRequest.ID)$URLParameters"
            Method = 'PUT'
        }

        $Results = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.MergeRequest'

        if ( $Passthru.isPresent ) {
            $Results
        }
    }
}

END {}

}


Function Close-GitLabMilestone {
    [cmdletbinding()]
    param(
        [Alias('project_id')]
        [ValidateNotNullOrEmpty()]
        [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
        [string]$ProjectId,

        [ValidateNotNullOrEmpty()]
        [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
        [string[]]$ID,

        [switch]$Passthru
    )

BEGIN {}

PROCESS {

    foreach ( $MergeRequestID in $ID ) {
        $Results = Set-GitLabMilestone -Project $ProjectId -Id $ID -StateEvent close

        if ( $Passthru.isPresent ) {
            $Results
        }
    }
}

END {}

}


Function Get-GitLabMilestone {
[cmdletbinding(DefaultParameterSetName='MergeRequests')]
[OutputType('GitLab.Milestone')]
param (
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName='Single',Mandatory)]
    [Parameter(ParameterSetName='MergeRequests',Mandatory)]
    [string]$ProjectId,

    [Parameter(ParameterSetName='Single',Mandatory)]
    [string]$Id,

    [Parameter(ParameterSetName='MergeRequests')]
    # milestone iid
    [string]$Iid,

    [Parameter(ParameterSetName='MergeRequests')]
    # possible values: active, closed
    [string]$State

)
    $Project = Get-GitlabProject -Id $ProjectId;

    if($PSCmdlet.ParameterSetName -ne 'Single') {
        $GetUrlParameters = @()

        if ($Iid) {
            $GetUrlParameters += @{iid=$Id}
        }
        if ($State) {
            $GetUrlParameters += @{state=$State}
        }

        $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters
    }


    $Request = @{
        URI = ''
        Method = 'GET'
    }

    switch ($PSCmdlet.ParameterSetName) {
        MergeRequests { $Request.URI="/projects/$($Project.id)/milestones$URLParameters"; break; }
        Single { $Request.URI="/projects/$($Project.id)/milestones/$Id"; break; }
        default { Write-Error "Incorrect parameter set."; break; }
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Milestone'
}


Function New-GitLabMilestone {
    [cmdletbinding()]
    param(
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory)]
        [string]$ProjectId,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory)]
        [string]$Title,

        [string]$Description,

        [datetime]$DueDate
    )

    $Project = $Project = Get-GitlabProject -Id $ProjectId;

    $GetUrlParameters = @()
    $GetUrlParameters += @{title=$Title}

    if ($Description) {
        $GetUrlParameters += @{description=$Description}
    }
    if ($DueDate) {
        $GetUrlParameters += @{due_date=($DueDate).ToString("yyyy-MM-dd")}
    }

    $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters

    $Request = @{
        URI="/projects/$($Project.id)/milestones$URLParameters";
        Method='POST';
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Milestone'
}


Function Set-GitLabMilestone {
    [cmdletbinding()]
    param(
        [Alias('project_id')]
        [ValidateNotNullOrEmpty()]
        [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
        [string]$ProjectId,

        [ValidateNotNullOrEmpty()]
        [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
        [string[]]$ID,

        [string]$Title,

        [string]$Description,

        [Nullable[datetime]]$DueDate,

        [Alias('state_event')]
        [ValidateSet("close", "activate")]
        [string]$StateEvent
    )

BEGIN {}

PROCESS {

    foreach ( $MilestoneID in $ID ) {
        $Project = $Project = Get-GitlabProject -Id $ProjectId;

        $Milestone = Get-GitLabMilestone -ProjectId $ProjectId -Id $MilestoneID

        Write-Verbose "Project Name: $($Project.Name), Milestone Name: $($Milestone.Name)"

        $GetUrlParameters = @()

        if ($Title) {
            $GetUrlParameters += @{title=$Title}
        }
        if ($Description) {
            $GetUrlParameters += @{description=$Description}
        }
        if ($DueDate) {
            $GetUrlParameters += @{due_date=$DueDate.ToString("yyyy-MM-dd")}
        }
        if ($StateEvent) {
            $GetUrlParameters += @{state_event=$StateEvent}
        }

        $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters

        $Request = @{
            URI = "/projects/$($Project.ID)/milestones/$($Milestone.ID)$URLParameters"
            Method = 'PUT'
        }

        $Results = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Milestone'

        if ( $Passthru.isPresent ) {
            $Results
        }
    }
}

END {}

}


Function Get-GitLabNamespace {
[cmdletbinding()]
[OutputType('GitLab.Namespace')]
param (
    [string]$search
)
    $GetUrlParameters = @()

    if ($search -ne $null) {
        $GetUrlParameters += @{search=$search}
    }
    $GetUrlParameters += @{per_page=100}
    $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters
    #$Request.URI = "$($Request.URI)" + "$URLParameters"
    $Request = @{
        URI="/namespaces$URLParameters";
        Method='Get';
    }
    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Namespace'
}


Function Get-GitLabPipeline {
    [cmdletbinding(DefaultParameterSetName='Pipelines')]
    [OutputType("GitLab.Project.Pipeline")]
    param(

        [Parameter(Mandatory=$true)]

        [int]$ProjectID,

        [Parameter(Mandatory=$true,ParameterSetName='Single')]
        [int]$Id,

        [Parameter(Mandatory=$false, ParameterSetName='ByBranch')]
        [Parameter(Mandatory=$false, ParameterSetName='ByTag')]
        [Parameter(Mandatory=$false,ParameterSetName='Pipelines')]
        [ValidateSet("running","pending","finished","branches","tags","all")]
        $Scope = 'all',

        [Parameter(Mandatory=$false, ParameterSetName='ByBranch')]
        [Parameter(Mandatory=$false, ParameterSetName='ByTag')]
        [Parameter(Mandatory=$false, ParameterSetName='Pipelines')]
        [ValidateSet("running","pending","success","failed","canceled","skipped","all")]
        $Status = 'all',

        [Parameter(Mandatory=$true, ParameterSetName='ByBranch')]
        [String]$Branch,

        [Parameter(Mandatory=$true, ParameterSetName='ByTag')]
        [String]$Tag,

        [Parameter(Mandatory=$false, ParameterSetName='ByBranch')]
        [Parameter(Mandatory=$false, ParameterSetName='ByTag')]
        [Parameter(Mandatory=$false,ParameterSetName='Pipelines')]
        [ValidateSet("id","status","ref","user_id")]
        $Order_by = "id",

        [Parameter(Mandatory=$false, ParameterSetName='ByBranch')]
        [Parameter(Mandatory=$false, ParameterSetName='ByTag')]
        [Parameter(Mandatory=$false,ParameterSetName='Pipelines')]
        [ValidateSet('asc','desc')]
        $Sort = 'desc'

    )

    $Request = @{
        URI = ''
        Method = 'GET'
    }

    $Project = Get-GitlabProject -Id $ProjectId

    Write-Verbose -Message "Returning a pipeline(s) for the project $($Project.Name) and id $($Project.Id)"

    if ($PSCmdlet.ParameterSetName -ne 'Single') {

        $GetUrlParameters = @()

        if ($PSCmdlet.ParameterSetName -eq 'ByBranch') {
            $GetUrlParameters += @{ref=$Branch}
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'ByTag') {
            $GetUrlParameters += @{ref=$Tag}
        }

        if ($Scope -ne 'all') {
            $GetUrlParameters += @{scope='all'}
        }

        if ($Status -ne 'all') {
            $GetUrlParameters += @{status=$Status}
        }

        $GetUrlParameters += @{order_by=$order_by}
        $GetUrlParameters += @{sort=$sort}
        $GetUrlParameters += @{per_page=100}
        $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters

    }

    Write-Verbose -Message "Parameter Set Name: $($PSCmdlet.ParameterSetName)"

    switch ($PSCmdlet.ParameterSetName) {
        Pipelines { $Request.URI="/projects/$($Project.id)/pipelines$URLParameters"; break; }
        Single { $Request.URI= "/projects/$($Project.id)/pipelines/$($Id)"; break; }
        default { Write-Error "Incorrect parameter set."; break; }
    }

    Write-Verbose -Message "A prepared API request: $($($Request.URI).ToString())"

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Pipeline' -Verbose

}


Function Get-GitLabProject {
    [cmdletbinding(DefaultParameterSetName='Projects')]
    [OutputType("GitLab.Project")]
    param(

        [Parameter(ParameterSetName='Single',
                   Mandatory=$true)]
        [int]$Id,
        [Parameter(ParameterSetName='PerGroup',
                   Mandatory=$true)]
        [int]$GroupId,

        [Parameter(Mandatory=$false,
                   ParameterSetName='Projects',
                   HelpMessage='Return only archived projects')]
        [Parameter(Mandatory=$false,
                   ParameterSetName='Owned',
                   HelpMessage='Return only archived projects')]
        [Parameter(Mandatory=$false,
                   ParameterSetName='All',
                   HelpMessage='Return only archived projects')]
        [Parameter(Mandatory=$false,
                   ParameterSetName='Starred',
                   HelpMessage='Return only archived projects')]
        [Parameter(Mandatory=$false,
                   ParameterSetName='PerGroup',
                   HelpMessage='Return only archived projects')]
        [switch]$Archived = $false,

        [Parameter(Mandatory=$false,
                   HelpMessage='Limit by visibility',
                   ParameterSetName='Projects')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Limit by visibility',
                   ParameterSetName='Owned')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Limit by visibility',
                   ParameterSetName='All')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Limit by visibility',
                   ParameterSetName='Starred')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Limit by visibility',
                   ParameterSetName='PerGroup')]
        [ValidateSet("public", "internal", "private","none")]
        $Visibility = 'none',

        [Parameter(Mandatory=$false,
                   HelpMessage='Choose the order in which projects are returned.',
                   ParameterSetName='Projects')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Choose the order in which projects are returned.',
                   ParameterSetName='Owned')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Choose the order in which projects are returned.',
                   ParameterSetName='All')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Choose the order in which projects are returned.',
                   ParameterSetName='Starred')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Choose the order in which projects are returned.',
                   ParameterSetName='PerGroup')]
        [ValidateSet('id','name','path','created_at','updated_at','last_activity_at')]
        $Order_by = 'created_at',


        [Parameter(Mandatory=$false,
                   HelpMessage='Ascending or Descending',
                   ParameterSetName='Projects')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Ascending or Descending',
                   ParameterSetName='Owned')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Ascending or Descending',
                   ParameterSetName='All')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Ascending or Descending',
                   ParameterSetName='Starred')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Ascending or Descending',
                   ParameterSetName='PerGroup')]
        [ValidateSet('asc','desc')]
        $Sort = 'desc',

        [Parameter(Mandatory=$false,
                   HelpMessage='Search for a project.',
                   ParameterSetName='Projects')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Search for a project.',
                   ParameterSetName='Owned')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Search for a project.',
                   ParameterSetName='All')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Search for a project.',
                   ParameterSetName='Starred')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Search for a project.',
                   ParameterSetName='PerGroup')]
        $Search,

        [Parameter(ParameterSetName='Owned',
                   Mandatory=$true)]
        [switch]$Owned,

        [Parameter(ParameterSetName='All',
                   Mandatory=$true)]
        [switch]$All,

        [Parameter(ParameterSetName='Starred',
                   Mandatory=$true)]
        [switch]$Starred

    )

    if ($PSCmdlet.ParameterSetName -ne 'Single') {
        Write-Verbose "Create GET Request"
        $GetUrlParameters = @()
        if ($archived) {
            $GetUrlParameters += @{archived='true'}
        }
        else {
            $GetUrlParameters += @{archived='false'}
        }
        
        if ($search -ne $null) {
            $GetUrlParameters += @{search=$search}
        }
        $GetUrlParameters += @{order_by=$order_by}
        $GetUrlParameters += @{sort=$sort}
        $GetUrlParameters += @{per_page=100}
        $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters
        #$Request.URI = "$($Request.URI)" + "$URLParameters"
    }


    $Request = @{
        URI = ''
        Method = 'GET'
    }

    Write-Verbose "Parameter Set Name: $($PSCmdlet.ParameterSetName)"

    switch ($PSCmdlet.ParameterSetName) {
        Projects { $Request.URI = "/projects$URLParameters"; break; }
        PerGroup { $Request.URI = "/groups/$GroupId/projects$URLParameters"; break; }
        Owned { $Request.URI = "/projects/owned$URLParameters"; break; }
        All { $Request.URI="/projects$URLParameters"; break; }
        Starred { $Request.URI="/projects/starred$URLParameters"; break; }
        Single { $Request.URI="/projects/$Id"; break; }
        default { Write-Error "Incorrect parameter set."; break; }

    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project'

}


Function Get-GitLabProjectArchive
{
[OutputType('String')]
  param(
    [parameter(mandatory,HelpMessage = 'Project ID')][int]$ProjectID,
    [string]$CommitID = $null,
    [string]$OutFile = "$PWD\test.zip"
  )

  $Commits = Get-GitLabProjectCommit -id $ProjectID
  IF (-not($CommitID))
  {
    $CommitID = $Commits[0].id #most recent commit id
  }
  ElseIF ($Commits | Where-Object -FilterScript {
      $_.id -eq $CommitID
  })
  {
    Write-Verbose -Message ('Commit id OK')
  }
  Else
  {
    Throw 'Commit ID bad'
  }
  $ProjectName = (Get-GitLabProject | Where-Object -FilterScript {
      ($_.id -eq $ProjectID)
  }).Name
  $RequestURI = ('/projects/{0}/repository/archive.zip?sha={1}' -f $ProjectID, $CommitID)
  DownloadFromGitLabAPI -RequestURI $RequestURI -OutFile $OutFile
  return ('Project ID:{0} Archive saved to {1}' -f $ProjectID, $OutFile)
}


Function Get-GitLabProjectCommit
{
[OutputType('GitLab.Project.Commit')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace,

    [Alias('Tag')]
    [string]
    $Branch,

    [datetime]
    $After,

    [datetime]
    $Before

  )

  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
    }
  }

  $Body = @{}

  switch ($PsBoundParameters.Keys) {
    'Branch'
    {
      $Body.Add('ref_name',$Branch)
    }
    'After'
    {
      $Body.Add('since',(Get-Date $After -Format s))
    }
    'Before'
    {
      $Body.Add('until',(Get-Date $Before -Format s))
    }
  }

  $Request = @{
    URI    = "/projects/$($Project.id)/repository/commits"
    Method = 'GET'
    Body   = $Body
  }

  QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Commit'
}


Function Get-GitLabProjectEvent {
[cmdletbinding()]
[OutputType('GitLab.Project.Event')]
param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName='Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName='Namespace')]
    [string]$Namespace
)

    $queryID = $null
    switch ($PSCmdlet.ParameterSetName) {
        'Id' { $queryID = $id }
        'Namespace' { $queryID = $Namespace -replace '/','%2F' -replace ' ','' }
    }

    $Request = @{
        URI="/projects/$queryID/events";
        Method='Get';
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Event'


}


function Get-GitlabProjectRepositoryTree
{

  [OutputType('GitLab.Repository.Tree')]
  [cmdletbinding()]
  param(
     [parameter(mandatory,HelpMessage='Project ID')][int]$ProjectID,
     [string]$CommitID
  )


  $Commits = Get-GitLabProjectCommit -Id $ProjectID
  IF (-not($CommitID))
  {
    $CommitID = $Commits[0].id #most recent commit id
  }
  ElseIF ($Commits | Where-Object -FilterScript {
      $_.id -eq $CommitID
  })
  {
    Write-Verbose -Message ('Commit id OK')
  }
  Else
  {
    Throw 'Commit ID bad'
  }

  $repoTree = (QueryGitLabAPI -Request @{
      URI    = ('/projects/{0}/repository/tree?ref_name={1}&recursive=true' -f $ProjectID, $CommitID)
      Method = 'GET'
  } -ObjectType 'GitLab.Repository.Tree')
  $repoTree | Add-Member -MemberType NoteProperty -Name 'ProjectID' -Value $ProjectID
  $repoTree | Add-Member -MemberType NoteProperty -Name 'CommitID' -Value $CommitID
  return $repoTree
}


Function Get-GitLabProjectServiceMSTeams
{
[OutputType('GitLab.Project.Service.MSTeams')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace
  )

  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
    }
  }

  $Request = @{
    URI    = "/projects/$($Project.id)/services/microsoft-teams"
    Method = 'GET'
  }

  QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Service.MSTeams'
}


Function Get-GitLabProjectServiceSlack
{
[OutputType('GitLab.Project.Service.Slack')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace
  )

  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
    }
  }

  $Request = @{
    URI    = "/projects/$($Project.id)/services/slack"
    Method = 'GET'
  }

  QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Service.Slack'
}


function Get-GitlabProjectSubmodule
{

  [OutputType('GitLab.Project.Submodule.Information')]
  [cmdletbinding()]
  param(
    [parameter(mandatory,HelpMessage='Project ID')][int]$ProjectID,
    [string]$CommitID
  )
  $repoTree = Get-GitlabProjectRepositoryTree -ProjectID $ProjectID
  If (-not($repotree | Where-Object -FilterScript {
      ($_.Type -eq 'blob') -and ($_.name -eq '.gitmodules')
  })) {
    Throw ('.gitmodules file missing from Project {0}' -f $ProjectID)
  }
  $Projects = Get-GitLabProject
  $repoTree = $repoTree | Where-Object -FilterScript {
      $_.Type -eq 'commit'
  }
  $Request = @{
    URI    = ('/projects/{0}/repository/files/.gitmodules?ref={1}' -f $repoTree[0].ProjectID, $repoTree[0].CommitID)
    Method = 'GET'
  }
  $GitsubmoduleFile = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String((QueryGitLabAPI -Request $Request -ObjectType 'File').content))
  $Gitsubmodules = $GitsubmoduleFile.Split('[]') -ne '' | ForEach-Object -Begin {
    $i = 0
  } -Process {
    if ($i++ % 2)
    {
      [PSCustomObject](ConvertFrom-StringData -StringData $_)
    }
  }
  $Object = @()
  Foreach ($commit in $repoTree)
  {
    $SubmoduleURL = ($Gitsubmodules | Where-Object -FilterScript {
        $_.path -eq $commit.Path
    }).url
    $SubmoduleProjectName = $SubmoduleURL.Split('/')[-1].Replace('.git','')
    $SubModuleNamespace = $SubmoduleURL.Split('/')[-2]
    $SubmoduleProjectID = ($Projects | Where-Object -FilterScript {
        $_.name -eq $SubmoduleProjectName
    }).Id
    $hash = [pscustomobject]@{
      ParentProjectID          = $commit.ProjectID
      ParentProjectCommitID    = $commit.CommitID
      SubModuleNamespace       = $SubModuleNamespace
      SubmoduleProjectID       = $SubmoduleProjectID
      SubmoduleProjectName     = $SubmoduleProjectName
      SubmodulePath            = $commit.Path
      SubmoduleFoldername      = $commit.Name
      SubmoduleURL             = $SubmoduleURL
      SubmoduleProjectCommitID = $commit.ID
    }
    $Object += $hash
  }

  $Object
}


Function Get-GitLabProjectTag
{
[OutputType('GitLab.Project.Tag')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace
  )

  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
    }
  }

  $Request = @{
    URI    = "/projects/$($Project.id)/repository/tags"
    Method = 'GET'
  }

  QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Tag'
}


Function Get-GitLabProjectWebhook
{

  [OutputType('GitLab.Project.Webhook')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace
  )

  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
    }
  }

  $Request = @{
    URI    = "/projects/$($Project.id)/hooks"
    Method = 'GET'
  }

  QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Webhook'
}


Function New-GitLabFork {
    [cmdletbinding()]
    param(
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [Parameter(ParameterSetName='Id')]
        [string]$Id,

        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        #[Parameter(ParameterSetName='Namespace')]
        [string]$Namespace
    )

    $Project = $null
    switch ($PSCmdlet.ParameterSetName) {
        'Id' { $Project = Get-GitLabProject -Id $Id }
        'Namespace' { $Project = Get-GitLabProject -Namespace $Namespace }
    }

    $Request = @{
        URI="/projects/fork/$($Project.id)";
        Method='POST';
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project'
}


Function New-GitLabProject {
    [cmdletbinding()]
    param(
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$true)]
        [Alias('Path')]
        [string]$name,
        [Parameter(Mandatory=$true)]
        [string]$namespace,
        [string]$description,
        [switch]$issues_enabled,
        [switch]$merge_requests_enabled,
        [switch]$builds_enabled,
        [switch]$wiki_enabled,
        [Switch]$snippets_enabled,
        [Switch]$container_registry_enabled,
        [Switch]$public,
        [ValidateSet("Private", "Internal", "Public")]
        [String]$visibility_level
    )

    $Body = @{
        name = $Name
    }
    $PSBoundParameters.Remove('Name') | Out-Null

    try {
        if ($PSBoundParameters.ContainsKey('Namespace')) {
            $nSpace = Get-GitLabNamespace | Where-Object {$_.path -eq "$Namespace"}
            if ($nSpace.id.Count -eq 1) {
                $Body.Add('namespace_id', $nSpace.id)
                $PSBoundParameters.Remove('Namespace') | Out-Null
            } else {
                throw "Error: No Namespace found"
        }

        foreach($p in $PSBoundParameters.GetEnumerator()) {
            if ($p.Key -eq 'visibility_level') {
                $vLevel = switch ($p.Value) {
                    'Private' {0}
                    'Internal' {10}
                    'Public' {20}
                }
                $Body.Add($p.Key, $vLevel)
            } else {
                $Body.Add($p.Key, $p.Value)
            }
        }

        $Request = @{
            URI='/projects';
            Method='POST';
            Body=$Body;
        }

        QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project'
        }
    }
    catch {
        Write-Error $_
    }
}


Function New-GitLabProjectWebhook
{


  [OutputType('GitLab.Project.Webhook')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace,
    [parameter(mandatory,HelpMessage = 'WebHook URL')][string]$URL,
    [switch]$push_events,
    [switch]$issues_events,
    [switch]$merge_requests_events,
    [switch]$tag_push_events,
    [switch]$note_events,
    [switch]$pipeline_events,
    [switch]$wiki_events,
    [switch]$enable_ssl_verification,
    [string]$Token

  )
  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
      $null = $PSBoundParameters.Remove('Id')
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
      $null = $PSBoundParameters.Remove('Namespace')
    }
  }
  $Body = @{
    id  = $Project.id
    url = $URL
  }
  If ($PSBoundParameters.ContainsKey('verbose'))
  {
    $null = $PSBoundParameters.Remove('verbose')
  }
  $PSBoundParameters.Remove('URL')
  If (-not($PSBoundParameters.ContainsKey('enable_ssl_verification')))
  {
    $PSBoundParameters.Add('enable_ssl_verification',$False)
  }
  try
  {
    foreach($p in $PSBoundParameters.GetEnumerator())
    {
      $Body.Add($p.Key, $($p.Value))
    }

    $Request = @{
      URI    = "/projects/$($Project.id)/hooks"
      Method = 'POST'
      Body   = $Body
    }
    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Webhook'
  }
  catch
  {
    Write-Error -Message $_
  }
}


Function Remove-GitLabProject {
[cmdletbinding(SupportsShouldProcess=$True,ConfirmImpact='High')]
param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(Mandatory=$true,
               ValueFromPipelineByPropertyName=$true)]
    [string]$Id
)
    BEGIN {}

    PROCESS {

        $Request = @{
            URI="/projects/$ID";
            Method='Delete';
        }

        $Project = Get-GitLabProject -Id $Id

        if ($PSCmdlet.ShouldProcess($Project.Name, 'Delete Project')) {
            $Worked = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project'
        }

    }

    END {}




}


Function Remove-GitLabProjectServiceMSTeams
{
[OutputType('GitLab.Project.Service.MSTeams')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace

  )

  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
      $null = $PSBoundParameters.Remove('Id')
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
      $null = $PSBoundParameters.Remove('Namespace')
    }
  }
  If ($PSBoundParameters.ContainsKey('verbose'))
  {
    $null = $PSBoundParameters.Remove('verbose')
  }

  try
  {
    $Request = @{
      URI    = "/projects/$($Project.id)/services/microsoft-teams"
      Method = 'DELETE'
    }
    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Services.MSTeams'
  }
  catch
  {
    Write-Error -Message $_
  }
}


Function Remove-GitLabProjectServiceSlack
{
[OutputType('GitLab.Project.Service.Slack')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace

  )

  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
      $null = $PSBoundParameters.Remove('Id')
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
      $null = $PSBoundParameters.Remove('Namespace')
    }
  }
  If ($PSBoundParameters.ContainsKey('verbose'))
  {
    $null = $PSBoundParameters.Remove('verbose')
  }

  try
  {
    $Request = @{
      URI    = "/projects/$($Project.id)/services/slack"
      Method = 'DELETE'
    }
    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Services.Slack'
  }
  catch
  {
    Write-Error -Message $_
  }
}


Function Remove-GitLabProjectWebhook
{
  [OutputType('GitLab.Project.Webhook')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace,
    [parameter(mandatory,HelpMessage = 'Webhook id')][string]$hook_id

  )

  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
      $null = $PSBoundParameters.Remove('Id')
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
      $null = $PSBoundParameters.Remove('Namespace')
    }
  }
  If ($PSBoundParameters.ContainsKey('verbose'))
  {
    $null = $PSBoundParameters.Remove('verbose')
  }
  $Body = @{
    id      = $Project.id
    url     = $URL
    hook_id = $hook_id
  }
  try
  {
    $Request = @{
      URI    = "/projects/$($Project.id)/hooks/$hook_id"
      Method = 'DELETE'
      Body   = $Body
    }
    $Body
    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Webhook'
  }
  catch
  {
    Write-Error -Message $_
  }
}


Function Restart-GitLabPipeline {
    [OutputType("GitLab.Project.Pipeline")]
    param(

        [Parameter(Mandatory=$true)]
        [int]$ProjectID,

        [Parameter(Mandatory=$true)]
        [int]$Id

    )

    $Request = @{
        URI = ''
        Method = 'GET'
    }

    $Project = Get-GitlabProject -Id $ProjectId

    $Pipeline = Get-GitLabPipeline -Id $ProjectID -Id $Id

    if (@('running', 'pending') -contains $Pipeline.status ) {

        Write-Warning -Message "The status for the pipeline $Id is $PipelineId so it can't be canceled."

    }
    else {

        $Request.URI= "/projects/$($Project.id)/pipelines/$Id/retry"

        Write-Verbose -Message "A prepared API request: $($($Request.URI).ToString())"

        QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Pipeline'

    }

}


Function Set-GitLabProject
{
    [cmdletbinding()]
    param(

        [ValidateNotNullOrEmpty()]
        [Parameter(
            Mandatory=$true,
            ParameterSetName='ID',
            ValueFromPipelineByPropertyName=$true
        )]
        [string[]]$ID,

        [string]$Name = $null,

        [string]$Path = $null,

        [string]$Description = $null,

        #$default_branch,

        #[Alias('issues_enabled')]
        #[switch]$IssuesEnabled = $false,
        #$merge_requests_enabled,
        #$wiki_enabled,
        #$snippets_enabled,
        #$public,

        [Alias('visibility_level')]
        [ValidateSet("Public", "Internal", "Private")]
        $VisabilityLevel = $null,

        [switch]$Passthru

    )

BEGIN {}

PROCESS {

    foreach ( $ProjID in $ID ) {
        $Project = Get-GitLabProject -Id $ProjID

        Write-Verbose "Project Name: $($Project.Name)"

        $Body = @{}

        if ($Name -ne $null) { $Body.Add('name',$Name) }
        if ($Path -ne $null) { $Body.Add('path',$Path) }
        if ($Description -ne $null) { $Body.Add('description',$Description) }
        if ($VisabilityLevel -ne $null ) { $Body.Add('visibility_level', (GetVisibilityLevel $VisabilityLevel) )}

        Write-Verbose ( $PSBoundParameters | ConvertTo-Json )

        Write-Verbose "Body: $($Body | ConvertTo-Json )"

        $Request = @{
            URI = "/projects/$($Project.ID)"
            Method = 'PUT'
            Body = $Body
            ContentType = 'application/x-www-form-urlencoded'
        }

        $Results = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User'

        if ( $Passthru.isPresent ) {
            $Results
        }
    }
}

END {}

}


Function Set-GitLabProjectServiceMSTeams
{
[OutputType('GitLab.Project.Service.MSTeams')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace,
    [parameter(mandatory,HelpMessage='MSTeams Webhook')][string]$webhook
  )

  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
      $null = $PSBoundParameters.Remove('Id')
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
      $null = $PSBoundParameters.Remove('Namespace')
    }
  }
  If ($PSBoundParameters.ContainsKey('verbose'))
  {
    $null = $PSBoundParameters.Remove('verbose')
  }
  $Body = @{
    webhook = $webhook
  }
  $null = $PSBoundParameters.Remove('webhook')
  try
  {
    $Request = @{
      URI         = "/projects/$($Project.id)/services/microsoft-teams"
      Method      = 'PUT'
      Body        = $Body
      ContentType = 'application/x-www-form-urlencoded'
    }
    $Body
    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Service.MSTeams'
  }
  catch
  {
    Write-Error -Message $_
  }
}


Function Set-GitLabProjectServiceSlack
{
[OutputType('GitLab.Project.Service.Slack')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace,
    [parameter(mandatory,HelpMessage='Slack Webhook')][string]$webhook
  )

  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
      $null = $PSBoundParameters.Remove('Id')
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
      $null = $PSBoundParameters.Remove('Namespace')
    }
  }
  If ($PSBoundParameters.ContainsKey('verbose'))
  {
    $null = $PSBoundParameters.Remove('verbose')
  }
  $Body = @{
    webhook = $webhook
  }
  $null = $PSBoundParameters.Remove('webhook')
  try
  {
    $Request = @{
      URI         = "/projects/$($Project.id)/services/slack"
      Method      = 'PUT'
      Body        = $Body
      ContentType = 'application/x-www-form-urlencoded'
    }
    $Body
    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Service.Slack'
  }
  catch
  {
    Write-Error -Message $_
  }
}


Function Set-GitLabProjectWebhook
{

[OutputType('GitLab.Project.Webhook')]
  [cmdletbinding()]
  param(
    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    [Parameter(ParameterSetName = 'Id')]
    [string]$Id,

    [ValidateNotNull()]
    [ValidateNotNullOrEmpty()]
    #[Parameter(ParameterSetName='Namespace')]
    [string]$Namespace,
    [parameter(mandatory,HelpMessage='Webhook URL')][string]$URL,
    [parameter(mandatory,HelpMessage='Hook ID')][string]$hook_id,
    [switch]$push_events,
    [switch]$issues_events,
    [switch]$merge_requests_events,
    [switch]$tag_push_events,
    [switch]$note_events,
    [switch]$pipeline_events,
    [switch]$wiki_events,
    [switch]$enable_ssl_verification,
    [string]$Token

  )

  $Project = $null
  switch ($PSCmdlet.ParameterSetName) {
    'Id'
    {
      $Project = Get-GitLabProject -Id $Id
      $null = $PSBoundParameters.Remove('Id')
    }
    'Namespace'
    {
      $Project = Get-GitLabProject -Namespace $Namespace
      $null = $PSBoundParameters.Remove('Namespace')
    }
  }
  If ($PSBoundParameters.ContainsKey('verbose'))
  {
    $null = $PSBoundParameters.Remove('verbose')
  }
  $Body = @{
    id  = $Project.id
    url = $URL
  }
  $null = $PSBoundParameters.Remove('URL')
  try
  {
    foreach($p in $PSBoundParameters.GetEnumerator())
    {
      $Body.Add($p.Key, $($p.Value))
    }

    $Request = @{
      URI    = "/projects/$($Project.id)/hooks/$hook_id"
      Method = 'PUT'
      Body   = $Body
      ContentType = 'application/x-www-form-urlencoded'
    }
    $Body
    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Webhook'
  }
  catch
  {
    Write-Error -Message $_
  }
}


Function Start-GitLabPipeline {
    [OutputType("GitLab.Project.Pipeline")]
    param(

        [Parameter(Mandatory=$true)]
        [int]$ProjectID,

        [Parameter(Mandatory=$true, ParameterSetName='ByBranch')]
        [String]$Branch,

        [Parameter(Mandatory=$true, ParameterSetName='ByTag')]
        [String]$Tag

    )

    $Request = @{
        URI = ''
        Method = 'POST'
    }

    $Project = Get-GitlabProject -Id $ProjectId

    Write-Verbose -Message "Starting a pipeline for the project $($Project.Name) and id $($Project.Id)"

    if ($PSCmdlet.ParameterSetName -eq 'ByBranch') {
        $ref=$Branch
    }
    elseif ($PSCmdlet.ParameterSetName -eq 'ByTag') {
        $ref=$Tag
    }

    $Request.URI= "/projects/$($Project.id)/pipeline/?ref=$ref"

    Write-Verbose -Message "A prepared API request: $($($Request.URI).ToString())"

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Pipeline'

}


Function Stop-GitLabPipeline {
    [OutputType("GitLab.Project.Pipeline")]
    param(

        [Parameter(Mandatory=$true)]
        [int]$ProjectID,

        [Parameter(Mandatory=$true)]
        [int]$Id

    )

    $Request = @{
        URI = ''
        Method = 'GET'
    }

    $Project = Get-GitlabProject -Id $ProjectId

    Write-Verbose -Message "Stoping a pipeline for the project $($Project.Name) and id $($Project.Id)"

    $Pipeline = Get-GitLabPipeline -Id $ProjectID -Id $Id

    if (@('running', 'pending') -contains $Pipeline.status ) {

        $Request.URI= "/projects/$($Project.id)/pipelines/$Id/cancel"

        Write-Verbose -Message "A prepared API request: $($($Request.URI).ToString())"

        QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Project.Pipeline'

    }
    else {

        Write-Warning -Message "The status for the pipeline $Id is $PipelineId so it can't be canceled."

    }

}


Function Add-GitLabProjectRunner {
    param(
        [Parameter(Mandatory=$true)]
        [int]$RunnerId,

        [Parameter(Mandatory=$true)]
        [int]$ProjectId)

    $Body = @{
        runner_id  = $RunnerId
    }

    $Request = @{
        URI="/projects/$ProjectId/runners";
        Method='POST';
        Body=$Body;
        ContentType='application/x-www-form-urlencoded';
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Runner'
}


Function Get-GitLabRunner {
    [cmdletbinding(DefaultParameterSetName='Single')]
    [OutputType("GitLab.Runner")]
    param(
        [Parameter(ParameterSetName='Single',
                   Mandatory=$true)]
        [int]$Id,

        [Parameter(ParameterSetName='Project',
                   Mandatory=$true)]
        [int]$ProjectId,

        [Parameter(ParameterSetName='Owned',
                   Mandatory=$true)]
        [switch]$Owned,

        [Parameter(ParameterSetName='All',
                   Mandatory=$true)]
        [switch]$All,

        [Parameter(Mandatory=$false,
                   HelpMessage='Limit by scope',
                   ParameterSetName='Owned')]
        [Parameter(Mandatory=$false,
                   HelpMessage='Limit by scope',
                   ParameterSetName='All')]
        [ValidateSet("active", "paused", "online")]
        $Scope = $null
    )

    if ($PSCmdlet.ParameterSetName -ne 'Single') {
        Write-Verbose "Create GET Request"
        $GetUrlParameters = @()
        if ($Scope) {
            $GetUrlParameters += @{scope=$Scope}
        }

        $URLParameters = GetMethodParameters -GetURLParameters $GetUrlParameters
        Write-Host $URLParameters
    }

    $Request = @{
        URI = ''
        Method = 'GET'
    }

    Write-Verbose "Parameter Set Name: $($PSCmdlet.ParameterSetName)"

    switch ($PSCmdlet.ParameterSetName) {
        Single { $Request.URI = "/runners/$Id"; break; }
        Owned { $Request.URI = "/runners/$URLParameters"; break; }
        All { $Request.URI="/runners/all$URLParameters"; break; }
        Project { $Request.URI="/projects/$ProjectId/runners"; break; }
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Runner'
}


Function New-GitLabRunner {
    param(
        [Parameter(Mandatory=$true)]
        [string] $Token,

        [Parameter(Mandatory=$true)]
        [string] $Description,

        [Parameter(Mandatory=$true)]
        [string] $Tags,

        [bool] $RunUntagged = $false,
        [bool] $Locked = $true,

        [Parameter(Mandatory=$true)]
        [string] $Platform = "linux",

        [Parameter(Mandatory=$true)]
        [string] $Architecture = "amd64",

        [Parameter(Mandatory=$true)]
        [ValidateSet("shell", "docker", "docker-ssh", "ssh", "parallels", "virtualbox", "docker+machine", "docker-ssh+machine", "kubernetes")]
        [string] $Executor = "shell",

        [bool] $Artifacts = $true,
        [bool] $Cache = $true,
        [bool] $Image = $true,
        [bool] $Services = $true,
        [bool] $Shared = $true,
        [bool] $Variables = $true,

        [Parameter(Mandatory=$true)]
        [string] $Name,

        [Parameter(Mandatory=$true)]
        [string] $Revision = "c1ecf97f",

        [Parameter(Mandatory=$true)]
        [string] $Version = "10.1.0"
    )

    $requestObject = @{
        "info" = @{
            "name" = $Name;
            "version" = $Version;
            "revision" = $Revision;
            "platform" = $Platform;
            "architecture" = $Architecture;
            "executor" = $Executor;
            "features" = @{
                "variables" = $Variables;
                "image" = $Image;
                "services" = $Services;
                "artifacts" = $Artifacts;
                "cache" = $Cache;
                "shared" = $Shared;
            };
        };
        "token" = $Token;
        "description" = $Description;
        "tag_list" = $Tags;
        "run_untagged" = $RunUntagged;
        "locked" = $locked;
    }

    $Body = ConvertTo-Json $requestObject

    $Request = @{
        URI='/runners';
        Method='POST';
        Body=$Body;
        ContentType="application/json"
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Runner'
}


Function Remove-GitLabProjectRunner {
    param(
        [Parameter(Mandatory=$true)]
        [int]$RunnerId,

        [Parameter(Mandatory=$true)]
        [int]$ProjectId)

    $Request = @{
        URI="/projects/$ProjectId/runners/$RunnerId";
        Method='DELETE';
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Runner'
}


Function Remove-GitLabRunner {
    param(
        [Parameter(Mandatory=$true)]
        [int]$Id)

    $Request = @{
        URI="/runners/$Id";
        Method='DELETE';
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Runner'
}


Function Set-GitLabRunner {
    param(
        [Parameter(Mandatory=$true)]
        [int]$Id,

        [string]$Description,

        # See http://itproctology.blogspot.be/2014/03/powershell-passing-empty-parameters-and.html
        # for the rationale for this
        [ValidateSet("True","False","", 0, 1)]
        [string]$Active,
        [string]$Tags,

        [ValidateSet("True","False","", 0, 1)]
        [string]$RunUntagged,

        [ValidateSet("True","False","", 0, 1)]
        [string]$Locked,

        [ValidateSet("not_protected", "ref_protected")]
        [string]$AccessLevel)

    $Body = @{}


    if ($Description) { $Body.Add('description',$Description) }
    if ($Active) { $Body.Add('active',$active) }
    if ($Tags) { $Body.Add('tag_list', $Tags) }
    if ($RunUntagged) { $Body.Add('run_untagged', $RunUntagged) }
    if ($Locked) {$Body.Add('locked', $Locked) }
    if ($AccessLevel) { $Body.Add('access_level', $AccessLevel) }

    Write-Host (ConvertTo-Json $Body)

    $Request = @{
        URI="/runners/$Id";
        Method='PUT';
        Body=$Body;
        ContentType='application/x-www-form-urlencoded';
    }

    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Runner'
}


Function Get-GitLabCommitStats {
[cmdletbinding()]
[OutputType('GitLab.Commit')]
param(
    [Parameter(ParameterSetName="Id",Mandatory=$true)]
    [Parameter(ParameterSetName="IdAuth",Mandatory=$true)]
    [Parameter(ParameterSetName="IdLastYear",Mandatory=$true)]
    [Parameter(ParameterSetName="IdByAuthor",Mandatory=$false)]
    [Parameter(ParameterSetName="IdBDate",Mandatory=$true)]
    [Parameter(ParameterSetName="IdADate",Mandatory=$true)]
    [Parameter(ParameterSetName="IdBothDate",Mandatory=$true)]
    [int]$Id,
    [Parameter(ParameterSetName="All",Mandatory=$true)]
    [Parameter(ParameterSetName="AllAuth",Mandatory=$true)]
    [Parameter(ParameterSetName="AllLastYear",Mandatory=$true)]
    [Parameter(ParameterSetName="AllByAuthor",Mandatory=$false)]
    [Parameter(ParameterSetName="AllBDate",Mandatory=$true)]
    [Parameter(ParameterSetName="AllADate",Mandatory=$true)]
    [Parameter(ParameterSetName="AllBothDate",Mandatory=$true)]
    [switch]$All,
    [Parameter(ParameterSetName="IdAuth",Mandatory=$true)]
    [Parameter(ParameterSetName="IdLastYear",Mandatory=$false)]
    [Parameter(ParameterSetName="IdByAuthor",Mandatory=$false)]
    [Parameter(ParameterSetName="IdBDate",Mandatory=$false)]
    [Parameter(ParameterSetName="IdADate",Mandatory=$false)]
    [Parameter(ParameterSetName="AllAuth",Mandatory=$true)]
    [Parameter(ParameterSetName="AllLastYear",Mandatory=$false)]
    [Parameter(ParameterSetName="AllByAuthor",Mandatory=$false)]
    [Parameter(ParameterSetName="AllBDate",Mandatory=$false)]
    [Parameter(ParameterSetName="AllADate",Mandatory=$false)]
    [Parameter(ParameterSetName="IdBothDate",Mandatory=$false)]
    [Parameter(ParameterSetName="AllBothDate",Mandatory=$false)]
    [string[]]$author = "*",
    [Parameter(ParameterSetName="IdBDate",Mandatory=$true)]
    [Parameter(ParameterSetName="AllBDate",Mandatory=$true)]
    [Parameter(ParameterSetName="IdBothDate",Mandatory=$true)]
    [Parameter(ParameterSetName="AllBothDate",Mandatory=$true)]
    [datetime]$beforeDate,
    [Parameter(ParameterSetName="IdADate",Mandatory=$true)]
    [Parameter(ParameterSetName="AllADate",Mandatory=$true)]
    [Parameter(ParameterSetName="IdBothDate",Mandatory=$true)]
    [Parameter(ParameterSetName="AllBothDate",Mandatory=$true)]
    [datetime]$afterDate,
    [Parameter(ParameterSetName="IdLastYear",Mandatory=$true)]
    [Parameter(ParameterSetName="AllLastYear",Mandatory=$true)]
    [switch]$lastYear,
    [Parameter(ParameterSetName="IdByAuthor",Mandatory=$true)]
    [Parameter(ParameterSetName="AllByAuthor",Mandatory=$true)]
    [switch]$byAuthor
)
    try {
        $commits = @()
        $dtcommits = @()
        if (!($Id)) {
            $allProjectsId = (Get-GitLabProject -All).Id
            foreach ($project in $allProjectsId) {
                $Request = @{
                    URI="/projects/$project/repository/commits?per_page=100";
                    Method='Get';
                }
            $commits += QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Commit' -Version "v4"
            }
        } else {
            $Request = @{
                URI="/projects/$Id/repository/commits?per_page=100";
                Method='Get';
            }
            $commits = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.Commit' -Version "v4"
        }
        foreach ($name in $author) {
            if ($lastYear) {
                $dtCommits += $commits | Where-Object {[datetime]$_.created_at -ge ((Get-Date).AddDays(-365))} | Where-Object {$_.author_name -like $name}
            } elseif ($beforeDate) {
                if ($afterDate) {
                    if ($beforeDate -le $afterDate) {
                        throw "beforeDate cannot be less than afterDate"
                    } else {
                        $dtCommits += $commits | Where-Object {([datetime]$_.created_at -le (Get-Date $beforeDate)) -and ([datetime]$_.created_at -ge (Get-Date $afterDate))} | Where-Object {$_.author_name -like $name}
                    }
                } else {
                    $dtCommits += $commits | Where-Object {[datetime]$_.created_at -le (Get-Date $beforeDate)} | Where-Object {$_.author_name -like $name}
                }
            } elseif ($afterDate) {
                $dtCommits += $commits | Where-Object {[datetime]$_.created_at -ge (Get-Date $afterDate)} | Where-Object {$_.author_name -like $name}
            } else {
                $dtCommits += $commits | Where-Object {$_.author_name -like $name}
            }
        }
        if ($dtCommits) {
            if ($byAuthor) {
                FormatCommits -dtCommits $dtCommits -ByAuthor
            } else {
                FormatCommits -dtCommits $dtCommits -ByWeek
            }
        } else {
            Write-Output "No commits found"
        }
    } catch {
        Write-Error $_.Exception.Message
    }
}


Function Block-GitLabUser {
    [cmdletbinding(DefaultParameterSetName='ID')]
    param(
        [Parameter(Mandatory=$True,
                   ParameterSetName='ID',
                   ValueFromPipelineByPropertyName=$true)]
        [string]$ID,

        [Parameter(Mandatory=$True,
                   ParameterSetName='Username')]
        [string]$Username,

        [Parameter(Mandatory=$True,
                   ParameterSetName='Email')]
        [string]$Email,

        [switch] $Passthru = $false
    )

    BEGIN {}

    PROCESS {

        Write-Verbose "$ID"
        switch ($PSCmdlet.ParameterSetName) {
            'ID' { $User = Get-GitLabUser -ID $ID }
            'Email' { $User = Get-GitLabUser -ID $Email }
            'Username' { $User = Get-GitLabUser -ID $Username }
        }

        $request = @{
            URI = "/users/$($User.ID)/block"
            Method = 'POST'
        }

        $null = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User'
        if ($Passthru.IsPresent) {
            Get-GitLabuser -id $User.ID
        }

    }

    END {}



}


Function Get-GitLabUser {
    [cmdletbinding(DefaultParameterSetName='All')]
    [OutputType('GitLab.User')]
    param(
        [Parameter(ParameterSetName='ID')]
        [string]$ID,

        [Parameter(ParameterSetName='All')]
        [switch]$All,

        [Parameter(ParameterSetName='Username')]
        [string]$Username,

        [Parameter(ParameterSetName='Email')]
        [string]$Email,

        [Parameter(ParameterSetName='CurrentUser')]
        [switch]$CurrentUser
    )
    $Request = @{
        URI = '/users'
        Method = 'GET'
    }

    switch ( $PSCmdlet.ParameterSetName) {
        'ID' { $Request.URI = "/users/$ID" }
        'All' { $Request.URI = '/users' }
        'CurrentUser' { $Request.URI = '/user' }
    }

    if ( $PSCmdlet.ParameterSetName -eq 'Username') {

        QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User' | where-object { $_.username -eq $Username }

    } elseif ( $PSCmdlet.ParameterSetName -eq 'Email') {

        QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User' | where-object { $_.email -eq $email }

    } else {

        QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User'
    }
}


Function New-GitLabUser {
    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$true)]
        [string]$Email,

        [ValidateNotNullOrEmpty()]
        [ValidatePattern("(?# Error: Password Must Contain at least 8 characters).{8,}")]
        [Parameter(Mandatory=$true)]
        [string]$Password,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$true)]
        [string]$Username,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$true)]
        [string]$Name,

        [string]$SkypeID = $null,
        [string]$LinkedIn = $null,
        [string]$Twitter = $null,
        [string]$WebsiteURL = $null,
        [int]$ProjectsLimit = 0,
        [switch]$Admin = $false,
        [switch]$CanCreateGroup = $false,
        [switch]$External = $false,

        [switch]$Passthru = $false
    )

    $Body = @{
        email = $Email
        password = $Password
        username = $Username
        name = $Name
    }

    if ($SkypeID -ne $null ) { $Body.Add('skype',$SkypeID) }
    if ($LinkedIn -ne $null ) { $Body.Add('linkedin',$LinkedIn) }
    if ($Twitter -ne $null ) { $Body.Add('twitter',$Twitter) }
    if ($WebsiteURL -ne $null ) { $Body.Add('website_url',$WebsiteURL) }
    if ($ProjectsLimit -ne $null ) { $Body.Add('projects_limit',$ProjectsLimit) }
    if ($Admin.IsPresent ) { $Body.Add('admin','true') }
    if ($CanCreateGroup.IsPresent ) { $Body.Add('can_create_group','true') }
    if ($External.IsPresent ) { $Body.Add('external','true') }

    $Request = @{
        URI = '/users'
        Method = 'POST'
        Body = $Body
    }

    $Results = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User'
    if ($Passthru.IsPresent) {
        $Results
    }

}


Function Remove-GitLabUser {
    [cmdletbinding(SupportsShouldProcess=$True,ConfirmImpact='High')]
    param(
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true)]
        [string[]]$Username
    )

    BEGIN {}

    PROCESS {
        foreach ( $user in $Username ) {

            $UserInfo = (Get-GitLabUser -Username $User)[0]
            Write-Verbose "$($UserInfo.Username)"
            $Request = @{
                URI="/users/$($UserInfo.ID)"
                Method = 'DELETE'
            }

            if ( $PSCmdlet.ShouldProcess("Delete User $Username") ) {
                $Results = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User'
            }

        }

    }

    END {}

}


Function Search-GitLabUser {
[cmdletbinding()]
param(
    [Parameter(Mandatory=$true)]
    [string]$User
)

    $Request = @{
        URI="/users?search=$($User)";
        Method='Get';
    }
    QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User' -Version "v4"
}


Function Set-GitLabUser {
    [cmdletbinding()]
    param(
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$true,ParameterSetName='ID')]
        [string]$ID,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$true,ParameterSetName='Email')]
        [string]$Email,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$true,ParameterSetName='Username')]
        [string]$Username,

        #[string]$NewEmail = $null,
        [string]$Password = $null,
        [string]$NewUsername = $null,
        [string]$Name = $null,
        [string]$SkypeID = $null,
        [string]$LinkedIn = $null,
        [string]$Twitter = $null,
        [string]$WebsiteURL = $null,
        [int]$ProjectsLimit = 0,
        [switch]$Admin = $false,
        [switch]$CanCreateGroup = $false,
        [switch]$External = $false,

        [switch]$Passthru = $false
    )

    switch ($PScmdlet.ParameterSetName ) {
        'Email' { $User = Get-GitLabUser -Email $Email }
        'Username' { $User = Get-GitLabUser -Username $UserName }
        'ID' { $User = Get-GitLabUser -id $ID }
    }

    $Body = @{}

    #if ($NewEmail -ne $null ) { $Body.Add('email',$NewEmail) }
    if ($Password -ne $null ) { $Body.Add('password',$Password) }
    if ($NewUsername -ne $null ) { $Body.Add('username',$NewUsername) }
    if ($Name -ne $null ) { $Body.Add('name',$Name) }
    if ($SkypeID -ne $null ) { $Body.Add('skype',$SkypeID) }
    if ($LinkedIn -ne $null ) { $Body.Add('linkedin',$LinkedIn) }
    if ($Twitter -ne $null ) { $Body.Add('twitter',$Twitter) }
    if ($WebsiteURL -ne $null ) { $Body.Add('website_url',$WebsiteURL) }
    if ($ProjectsLimit -ne 0 ) { $Body.Add('projects_limit',$ProjectsLimit) }
    if ($Admin.IsPresent ) { $Body.Add('admin','true') }
    if ($CanCreateGroup.IsPresent ) { $Body.Add('can_create_group','true') }
    if ($External.IsPresent ) { $Body.Add('external','true') }

    $Request = @{
        URI = "/users/$($User.ID)"
        Method = 'PUT'
        Body = $Body
        ContentType = 'application/x-www-form-urlencoded'
    }

    #Write-Debug -Message "Before Request"
    Write-Verbose "Body: $( $Body | ConvertTo-Json ) "

    $Results = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User'
    if ($Passthru.IsPresent) {
        $Results
    }
}


Function Unblock-GitLabUser {
    [cmdletbinding(DefaultParameterSetName='ID')]
    param(
        [Parameter(Mandatory=$True,
                   ParameterSetName='ID',
                   ValueFromPipelineByPropertyName=$true)]
        [string]$ID,

        [Parameter(Mandatory=$True,
                   ParameterSetName='Username')]
        [string]$Username,

        [Parameter(Mandatory=$True,
                   ParameterSetName='Email')]
        [string]$Email,

        [switch] $Passthru = $false
    )

    BEGIN {}

    PROCESS {

        Write-Verbose "$ID"
        switch ($PSCmdlet.ParameterSetName) {
            'ID' { $User = Get-GitLabUser -ID $ID }
            'Email' { $User = Get-GitLabUser -ID $Email }
            'Username' { $User = Get-GitLabUser -ID $Username }
        }

        $request = @{
            URI = "/users/$($User.ID)/unblock"
            Method = 'POST'
        }

        $null = QueryGitLabAPI -Request $Request -ObjectType 'GitLab.User'
        if ($Passthru.IsPresent) {
            Get-GitLabuser -id $User.ID
        }

    }

    END {}


}


##########################
# Private Functions
##########################
# https://blogs.msdn.microsoft.com/besidethepoint/2010/09/21/decrypt-secure-strings-in-powershell/
function DecryptString {
param(
    [Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=0)]
    [System.Security.SecureString]
    $Token
)

$marshal = [System.Runtime.InteropServices.Marshal]
$ptr = $marshal::SecureStringToBSTR( $token )
$key = $marshal::PtrToStringBSTR( $ptr )
$marshal::ZeroFreeBSTR( $ptr )
$key
Remove-Variable key
}


Function DownloadFromGitLabAPI
{
  <#
    .SYNOPSIS
    Download file from GitLab API
 
    .DESCRIPTION
    Download file from GitLab API
 
    .PARAMETER RequestURI
    The URL for download.
 
    .PARAMETER OutFile
    Where to save the file.
 
    .EXAMPLE
    DownloadFromGitLabAPI -RequestURI /projects/1/repository/archive.zip -OutFile C:\Temp\temp.zip
    Downloads the whole repo in a zip file for project id 1
    .NOTES
 
    .LINK
 
    .INPUTS
 
    .OUTPUTS
 
  #>



  [cmdletbinding()]
  param(
    [Parameter(Mandatory,
        HelpMessage = 'URI',
    Position = 0)]
    [ValidateNotNullOrEmpty()]
    [String]$RequestURI,

    [Parameter(Mandatory,
        HelpMessage = 'Output File',
    Position = 1)]
    [string]$OutFile
  )
  $GitLabConfig = ImportConfig
  $Domain = $GitLabConfig.Domain
  if ( $IsWindows -or ( [version]$PSVersionTable.PSVersion -lt [version]'5.99.0' ) )
  {
    $Token = DecryptString -Token $GitLabConfig.Token
  }
  elseif ( $IsLinux )
  {
    $Token = $GitLabConfig.Token
  }

  $RequestURI = ('{0}/api/v3{1}' -f $Domain, $RequestURI)
  try
  {
    Write-Verbose -Message ('URL: {0}' -f $RequestURI)
    $wc = New-Object -TypeName System.Net.WebClient
    $wc.Headers.Add('PRIVATE-TOKEN',$Token)
    Write-Verbose -Message ('Downloading File from {0} to {1}' -f $RequestURI, $OutFile)
    $wc.DownloadFile($RequestURI,$OutFile)
    Remove-Variable -Name Token
    Remove-Variable -Name RequestURI
  }
  catch
  {
    $ErrorMessage = $_.exception.response.statusDescription
    Write-Warning  -Message ('{0}. See {1}/help/api/README.md#status-codes for more information.' -f $ErrorMessage, $Domain)
  }
}


Function FormatCommits {
    param(
        [Parameter(Mandatory=$true)]
        [psobject]$dtCommits,
        [switch]$byAuthor,
        [switch]$byWeek
    )

    $commits = @()
    $results = @()

    if ($byAuthor) {
        $authors = $dtCommits.author_email.ToLower() | sort | unique -AsString
        $commitHash = @{}
        foreach ($name in $authors.author_email) {
            $commitHash.Add($name, 0)
        }
        foreach ($c in $dtcommits) {
            $tempValue = $null
            $tempValue = $commitHash[$c.author_email] + 1
            $commitHash.Set_Item($c.author_email, $tempValue)
        }
        foreach ($k in $commitHash.keys) {
            $tempObj = new-object psobject
            Add-Member -InputObject $tempObj -MemberType NoteProperty -Name Author -Value $k
            Add-Member -InputObject $tempObj -MemberType NoteProperty -Name Commits -Value $commitHash[$k]
            $commits += $tempObj
        }
        $results = $commits
    }
    if ($byWeek) {
        foreach ($c in $dtCommits) {
            $tempObj = new-object psobject
            Add-Member -InputObject $tempObj -MemberType NoteProperty -Name Week -Value (GetWeek -DateTime $c.created_at)
            Add-Member -InputObject $tempObj -MemberType NoteProperty -Name Day -Value (Get-Date ([datetime]($c.created_at)) -UFormat %w)
            $commits += $tempObj
        }
        $weeks = $commits | Sort-Object -Property Week | Group-Object -Property Week
        foreach ($week in $weeks.Name) {
            $total = 0
            $getWeek = $commits | Where-Object {$_.Week -eq $week}
            $days = $getWeek | Sort-Object -Property Day | Group-Object -Property Day
            $output = new-object psobject
            Add-Member -InputObject $output -MemberType NoteProperty -Name Week -Value $week
            Add-Member -InputObject $output -MemberType NoteProperty -Name Sun -Value 0
            Add-Member -InputObject $output -MemberType NoteProperty -Name Mon -Value 0
            Add-Member -InputObject $output -MemberType NoteProperty -Name Tue -Value 0
            Add-Member -InputObject $output -MemberType NoteProperty -Name Wed -Value 0
            Add-Member -InputObject $output -MemberType NoteProperty -Name Thu -Value 0
            Add-Member -InputObject $output -MemberType NoteProperty -Name Fri -Value 0
            Add-Member -InputObject $output -MemberType NoteProperty -Name Sat -Value 0
            foreach ($day in $days) {
                if ($day.Name -eq 0) {
                    $output.Sun = $day.Count
                } elseif ($day.Name -eq 1) {
                    $output.Mon = $day.Count
                } elseif ($day.Name -eq 2) {
                    $output.Tue = $day.Count
                } elseif ($day.Name -eq 3) {
                    $output.Wed = $day.Count
                } elseif ($day.Name -eq 4) {
                    $output.Thu = $day.Count
                } elseif ($day.Name -eq 5) {
                    $output.Fri = $day.Count
                } elseif ($day.Name -eq 6) {
                    $output.Sat = $day.Count
                }
                $total += $day.Count
            }
            Add-Member -InputObject $output -MemberType NoteProperty -Name WeeklyTotal -Value $total
            if ($weeks.Name.Count -gt 1)
            {
                $runningTotal += $total
                Add-Member -InputObject $output -MemberType NoteProperty -Name RunningTotal -Value $runningTotal
            }
            $results += $output
        }
    }
    Write-Output $results
}


Function GetGitLabStatusCode {
    param(
        [Parameter(Mandatory=$true)]
        [int]$StatusCode
    )

    switch ($StatusCode) {
        '200' { $Text = 'OK - The GET, PUT or DELETE request was successful, the resource(s) itself is returned as JSON' }
        '201' { $Text = 'Created - The POST request was successful and the resource is returned as JSON' }
        '204' { $Text = 'The server has successfully fulfilled the request and that there is no additional content to send in the response payload body.'}
        '304' { $Text = 'Indicates that the resource has not been modified since the last request.'}
        '400' { $Text = 'Bad Request - A required attribute of the API request is missing, e.g. the title of an issue is not given' }
        '401' { $Text = 'Unauthorized - The user is not authenticated, a valid user token is necessary, see above' }
        '403' { $Text = 'Forbidden - The request is not allowed, e.g. the user is not allowed to delete a project' }
        '404' { $Text = 'Not Found - A resource could not be accessed, e.g. an ID for a resource could not be found' }
        '405' { $Text = 'Method Not Allowed - The request is not supported' }
        '409' { $Text = 'Conflict - A conflicting resource already exists, e.g. creating a project with a name that already exists' }
        '412' { $Text = 'Indicates the request was denied. May happen if the If-Unmodified-Since header is provided when trying to delete a resource, which was modified in between.'}
        '422' { $Text = 'Unprocessable - The entity could not be processed' }
        '500' { $Text = 'Server Error - While handling the request something went wrong on the server side' }
    }

    $Return = [pscustomobject]@{
        StatusCode = $StatusCode;
        StatusText = $Text;
    }
    $Return.pstypenames.insert(0,'PSGitLab.Configuration.StatusCode')
    Write-Output $Return
}


Function GetMethodParameters {
[cmdletbinding()]
param(
    $GetURLParameters
)

$string = '?'
foreach ($Param in $GetUrlParameters) {
    $Param.Keys | ForEach-Object {
        $key = $_
        $value = $Param[$_]
    }
    $string += "&"
    $string += [uri]::EscapeDataString($key)
    $string += "="
    $string += [uri]::EscapeDataString($value)
}
$string = $string -replace '\?&','?'
Write-Output $string
}


function GetVisibilityLevel {
    param(
        [ValidateSet("Public", "Internal", "Private")]
        $String
    )

    switch ($String)
    {
        'Public' { 20; break; }
        'Internal' { 10; break; }
        'Private' { 0; break; }
    }

}


function GetWeek([datetime]$DateTime = (Get-Date)) {
    $cultureInfo = [System.Globalization.CultureInfo]::CurrentCulture
    $cultureInfo.Calendar.GetWeekOfYear($DateTime,$cultureInfo.DateTimeFormat.CalendarWeekRule,$cultureInfo.DateTimeFormat.FirstDayOfWeek)
}


Function ImportConfig {
<#
.Synopsis
   Check for configuration and output the information.
.DESCRIPTION
   Check for configuration and output the information. Goes into the $env:appdata for the configuration file.
.EXAMPLE
    ImportConfig
#>


if ( ( $null -ne $env:PSGitLabDomain) -and ( $null -ne $env:PSGitLabToken ) -and ( $null -ne $env:PSGitLabAPIVersion ) ) {
    $Token = ConvertTo-SecureString -String $env:PSGitLabToken -AsPlainText -Force
    [PSCustomObject]@{
        Domain=$env:PSGitLabDomain
        Token=$Token
        APIVersion=$env:PSGitLabAPIVersion
    }
    break;
}

if ( $IsWindows -or ( [version]$PSVersionTable.PSVersion -lt [version]"5.99.0" ) ) {
    $ConfigFile = "{0}\PSGitLab\PSGitLabConfiguration.xml" -f $env:appdata
} elseif ( $IsLinux -or $IsMacOS ) {
    $ConfigFile = "{0}/.psgitlab/PSGitLabConfiguration.xml" -f $HOME
} else {
    Write-Error "Unknown Platform"
}
if (Test-Path $ConfigFile) {
    Import-Clixml $ConfigFile

} else {
    Write-Warning 'No saved configuration information. Run Save-GitLabAPIConfiguration.'
    break;
}
}


Function QueryGitLabAPI {
    [cmdletbinding()]
    param(
        [Parameter(Mandatory=$true,
                   HelpMessage='A hash table used for splatting against Invoke-WebRequest.',
                   Position=0)]
        [ValidateNotNullOrEmpty()]
        $Request,

        [Parameter(Mandatory=$false,
                   HelpMessage='Provide a datatype for the returing objects.',
                   Position=1)]
        [ValidateNotNullOrEmpty()]
        [string]$ObjectType,

        [Parameter(Mandatory=$false,
                   HelpMessage='Provide API version to use',
                   Position=2)]
        [ValidateNotNullOrEmpty()]
        [string]$Version = 'v4'
    )

    $GitLabConfig = ImportConfig

    if ($GitLabConfig.APIVersion) { $Version = "v$($GitLabConfig.APIVersion)" }

    $Domain = $GitLabConfig.Domain
    if ( $IsWindows -or ( [version]$PSVersionTable.PSVersion -lt [version]"5.99.0" ) ) {
        $Token = DecryptString -Token $GitLabConfig.Token
    } elseif ( $IsLinux -or $IsMacOS ) {
        $Token = $GitLabConfig.Token
    }
    $Headers = @{
        'PRIVATE-TOKEN'=$Token;
    }

    $Request.Add('Headers',$Headers)
    $Request.URI = "$Domain/api/$Version" + $Request.URI
    $Request.UseBasicParsing = $true

    try {
        #https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netcore-2.0#System_Net_SecurityProtocolType_SystemDefault
        if ($PSVersionTable.PSVersion.Major -lt 6 -and [Net.ServicePointManager]::SecurityProtocol -notmatch 'Tls12') {
            Write-Verbose "Enabling TLS 1.2"
            [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::Tls12
        }
    }
    catch {
        Write-Warning -Message 'Adding TLS 1.2 to supported security protocols was unsuccessful.'
    }

    try  {
        $ProgressPreference = 'SilentlyContinue'
        Write-Verbose "URL: $($Request.URI)"
        $webContent = Invoke-WebRequest @Request
        $totalPages = if ($webContent.Headers.ContainsKey('X-Total-Pages')) {
            (($webContent).Headers['X-Total-Pages']).tostring() -as [int]
        } else { 0 }

        if ($webContent.rawcontentlength -eq 0 ) { break; }

        $bytes = $webContent.Content.ToCharArray() | Foreach-Object{ [byte]$_ }
        $Results = [Text.Encoding]::UTF8.GetString($bytes) | ConvertFrom-Json
        for ($i=1; $i -lt $totalPages; $i++) {
            $newRequest = $Request.PSObject.Copy()
            if ( $newRequest['URI'] -match '\?') {
                $newRequest.URI = $newRequest.URI + "&page=$($i+1)"
            }
            else {
                $newRequest.URI = $newRequest.URI + "?page=$($i+1)"
            }
            $Results += (Invoke-WebRequest @newRequest).Content | ConvertFrom-Json
        }
    } catch {
        $GitLabErrorText = "{0} - {1}" -f $webcontent.statuscode,$webcontent.StatusDescription
        Write-Error -Message $GitLabErrorText
    }
    finally {
        $ProgressPreference = 'Continue'
        Remove-Variable -Name newRequest -ErrorAction SilentlyContinue
        Remove-Variable -Name Token
        Remove-Variable -Name Headers
        Remove-Variable -Name Request
    }

    foreach ($Result in $Results) {
        $Result.pstypenames.insert(0,$ObjectType)
        Write-Output $Result
    }

}