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 = 3
)

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
    }
    
    $Result = Invoke-WebRequest -UseBasicParsing -Uri "$Domain/api/$Version/projects?private_token=$Token"
    Remove-Variable Token
    GetGitLabStatusCode $Result.StatusCode
}


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-GitLabProject {
    [cmdletbinding(DefaultParameterSetName='Projects')]
    [OutputType("GitLab.Project")]
    param(

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

        [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')]
        [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')]
        [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')]
        [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')]
        [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')]
        $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}
        $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; }
        Owned { $Request.URI = "/projects/owned$URLParameters"; break; }
        All { $Request.URI="/projects/all$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)]
        [string]$name,
        [string]$path,
        [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 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 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 = 'PUT'
        }

        $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 = 'PUT'
        }

        $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' }
        '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' }
        '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-restmethod.',
               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 = '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
}
$Headers = @{
    'PRIVATE-TOKEN'=$Token;
}

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

try  {
    Write-Verbose "URL: $($Request.URI)"
    $webContent = Invoke-WebRequest @Request
    $totalPages = ($webContent).Headers['X-Total-Pages'][0] -as [int]
    $Results = $webContent.Content | ConvertFrom-Json
    for ($i=1; $i -lt $totalPages; $i++) {
        $newRequest = $Request.PSObject.Copy()
        $newRequest.URI = $newRequest.URI + "?&page=$($i+1)"
        $Results += (Invoke-WebRequest @newRequest).Content | ConvertFrom-Json
    }
    Remove-Variable Token
    Remove-Variable Headers
    Remove-Variable Request
} catch {
    $ErrorMessage = $_.exception.response.statusDescription
    Write-Warning  -Message "$ErrorMessage. See $Domain/help/api/README.md#status-codes for more information."
}

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